Commit a9ce9c03 authored by Viacheslav Pavlov's avatar Viacheslav Pavlov Committed by Matija Obreza
Browse files

Admin: OAuth client management

added initial code for editing of OAuthClients
fixed Boolean, Checkbox, Radio selections
moved BooleanRadioGroup to common/form

added addOAuthClient button
added translations
added missing fields to OAuthEditForm
added remove(All)Access/RefreshToken actions
changed Api URLs

fixed ssr
fixed 500 error for 'DeleteClientSecret'
fixed OAuthClientForm
parent 9237ecbe
......@@ -103,6 +103,7 @@
"true": "True",
"to": "To",
"untitled": "Untitled",
"useDefault": "Use default",
"UUID": "UUID",
"version": "Version {{version, numeric}}",
"yes": "Yes",
......
......@@ -1903,6 +1903,52 @@
},
"edit": {
"title": "Update user profile"
},
"oAuthEdit": {
"createNew": "Create new OAuthClient"
},
"oAuthBrowse": {
"title": "Browse OAuth2 Clients"
},
"oAuthDisplay": {
"accessTokens": "Access tokens",
"refreshTokens": "Refresh tokens",
"deleteAll": "Delete all"
},
"createOAuthSecretDialog": {
"generateSecret": "Generate client secret",
"newSecret": "New client secret",
"secret": "Your client secret",
"password": "Client secret",
"confirm": "Do you really want to create new client secret?"
},
"removeOAuthSecretDialog": {
"confirm": "Do you really want to delete client secret?",
"success": "Client secret was deleted successfully"
},
"deleteClient": {
"confirm": "Do you really want to delete {{clientId}} client?"
},
"deleteAllRefresh": {
"confirm": "Do you really want to delete all refresh tokens?"
},
"deleteRefresh": {
"confirm": "Do you really want to delete {{tokenId}} refresh token?"
},
"deleteAllAccess": {
"confirm": "Do you really want to delete all access tokens?"
},
"deleteAccess": {
"confirm": "Do you really want to delete {{tokenId}} access token?"
}
},
"c": {
"oAuthClientCard": {
"removeSecret": "Remove client secret",
"title": "OAuth client details"
},
"tokenCard": {
"expiresAt": "Expires"
}
}
},
......@@ -1942,6 +1988,7 @@
"common": {
"email": "Email",
"emailAddress": "E-mail address",
"oAuthClientModelName": "OAuth client",
"name": "Name",
"fullName": "Full name",
"surname": "Surname",
......@@ -1954,6 +2001,40 @@
"expired": "Expired"
},
"roleLabel": "User roles",
"oAuth": {
"clientId": "clientId",
"clientTitle": "Title",
"accessTokenValidity": "Access token validity",
"refreshTokenValidity": "Refresh token validity",
"clientSecret": "Client secret",
"description": "Description",
"roles": "Roles",
"authority": "Authorities",
"authorities": {
"CLIENT": "Client",
"TRUSTED_CLIENT": "Trusted client",
"EVERYONE": "Everyone"
},
"clientScope": "Client scopes",
"clientScopes": {
"read": "Read",
"write": "Write",
"trust": "Trust"
},
"grantType": "Grant types",
"grantTypes": {
"implicit": "Implicit",
"password": "Password",
"client_credentials": "Client credentials",
"authorization_code": "Authorization code"
},
"redirectUris": "Redirect URIs",
"resourceIds": "Resource IDs",
"autoApprove": "Auto approve",
"autoApproveTrue": "Auto approve",
"autoApproveFalse": "Do not auto approve",
"autoApproveScope": "Auto approve scopes"
},
"role": {
"USER": "User",
"ADMINISTRATOR": "Administrator",
......
......@@ -12,7 +12,7 @@ import FormLabel from '@material-ui/core/FormLabel/FormLabel';
import { TextField } from 'ui/common/text-field';
import ReduxCheckbox from 'ui/common/checkbox';
import FormControl from 'ui/common/forms/FormControl';
import BooleanRadioGroup from 'requests/ui/request-stepper/steps/requestInfo/c/BooleanRadioGroup';
import BooleanRadioGroup from 'ui/common/forms/BooleanRadioGroup';
const InstituteForm = ({ error, handleSubmit, initialValues, wiewsCode, t }) => {
......
/*
* Defined in Swagger as '#/definitions/AccessToken'
*/
class AccessToken {
public authentication: string;
public authenticationId: string;
public clientId: string;
public expiration: Date;
public id: string;
public refreshToken: string;
public token: string;
public tokenId: string;
public username: string;
}
export default AccessToken;
import AccessToken from 'model/oauth/AccessToken';
import OAuthClient from 'model/oauth/OAuthClient';
import RefreshToken from 'model/oauth/RefreshToken';
/*
* Defined in Swagger as '#/definitions/ClientDetails'
*/
class ClientDetails {
public accessTokens: AccessToken[];
public clientDetails: OAuthClient;
public refreshTokens: RefreshToken[];
}
export default ClientDetails;
/*
* Defined in Swagger as '#/definitions/GrantedAuthority'
*/
class GrantedAuthority {
public authority: string;
public static AVAILABLE_AUTHORITIES = [
'CLIENT', 'TRUSTED_CLIENT', 'EVERYONE',
];
}
export default GrantedAuthority;
import GrantedAuthority from 'model/oauth/GrantedAuthority';
/*
* Defined in Swagger as '#/definitions/OAuthClient'
*/
class OAuthClient {
public static clazz: string = 'org.genesys.blocks.oauth.model.OAuthClient';
public accessTokenValidity: number;
public accessTokenValiditySeconds: number;
public active: boolean;
public authorities: GrantedAuthority[];
public authorizedGrantTypes: string[];
public autoApprove: boolean;
public autoApproveScopes: string[];
public clientId: string;
public clientScopes: string[];
public clientSecret: string;
public createdBy: number;
public createdDate: Date;
public description: string;
public fullName: string;
public grantTypes: string[];
public id: number;
public lastModifiedBy: number;
public lastModifiedDate: Date;
public principal: boolean;
public redirectUris: string[];
public refreshTokenValidity: number;
public refreshTokenValiditySeconds: number;
public registeredRedirectUri: string[];
public resourceIds: string[];
public roles: string[];
public scoped: boolean;
public secretRequired: boolean;
public sid: string;
public title: string;
public version: number;
public static AVAILABLE_GRANT_TYPES = [
'implicit', 'password', 'client_credentials', 'authorization_code',
];
public static AVAILABLE_SCOPES = [
'read', 'write', 'trust',
];
}
export default OAuthClient;
import DateFilter from 'model/filter/DateFilter';
import StringFilter from 'model/filter/StringFilter';
/*
* Defined in Swagger as '#/definitions/OAuthClientFilter'
*/
class OAuthClientFilter {
public NOT: OAuthClientFilter;
public NOTNULL: string[];
public NULL: string[];
public active: boolean;
public autoApprove: boolean;
public clientId: string[];
public createdBy: number[];
public createdDate: DateFilter;
public grants: StringFilter;
public id: number[];
public lastModifiedBy: number[];
public lastModifiedDate: DateFilter;
public redirect: StringFilter;
public resource: StringFilter;
public roles: string[];
public title: StringFilter;
public version: number[];
}
export default OAuthClientFilter;
/*
* Defined in Swagger as '#/definitions/RefreshToken'
*/
class RefreshToken {
public authentication: string;
public clientId: string;
public expiration: Date;
public id: string;
public token: string;
public tokenId: string;
public username: string;
}
export default RefreshToken;
......@@ -9,7 +9,7 @@ import {REQUEST_INFO_FORM} from 'requests/constants';
import Validators from 'utilities/Validators';
// UI
import {TextField} from 'ui/common/text-field/index';
import BooleanRadioGroup from './BooleanRadioGroup';
import BooleanRadioGroup from 'ui/common/forms/BooleanRadioGroup';
import PurposeTypeRadioGroup from './PurposeTypeRadioGroup';
......
import * as UrlTemplate from 'url-template';
import * as QueryString from 'query-string';
import { axiosBackend } from 'utilities/requestUtils';
import AccessToken from 'model/oauth/AccessToken';
import ClientDetails from 'model/oauth/ClientDetails';
import FilteredPage, { IPageRequest } from 'model/FilteredPage';
import OAuthClient from 'model/oauth/OAuthClient';
import OAuthClientFilter from 'model/oauth/OAuthClientFilter';
const URL_LIST_CLIENTS = `/api/v1/admin/oauth-clients/`;
const URL_SAVE_EXISTING_CLIENT = `/api/v1/admin/oauth-clients/save-client`;
const URL_GET_ISSUED_TOKENS = UrlTemplate.parse(`/api/v1/admin/oauth-clients/user/{uuid}/tokens`);
const URL_REMOVE_USERS_ACCESS_TOKEN = UrlTemplate.parse(`/api/v1/admin/oauth-clients/user/{uuid}/{tokenId}/remove`);
const URL_DELETE_CLIENT = UrlTemplate.parse(`/api/v1/admin/oauth-clients/{clientId}`);
const URL_REMOVE_ALL_ACCESS_TOKENS = UrlTemplate.parse(`/api/v1/admin/oauth-clients/{clientId}/access-tokens`);
const URL_REMOVE_ACCESS_TOKEN = UrlTemplate.parse(`/api/v1/admin/oauth-clients/{clientId}/access-tokens/{tokenId}`);
const URL_CLIENT_DETAILS_INFO = UrlTemplate.parse(`/api/v1/admin/oauth-clients/{clientId}/details`);
const URL_GENERATE_SECRET = UrlTemplate.parse(`/api/v1/admin/oauth-clients/{clientId}/generate-secret`);
const URL_REMOVE_ALL_REFRESH_TOKENS = UrlTemplate.parse(`/api/v1/admin/oauth-clients/{clientId}/refresh-tokens`);
const URL_REMOVE_REFRESH_TOKEN = UrlTemplate.parse(`/api/v1/admin/oauth-clients/{clientId}/refresh-tokens/{tokenId}`);
const URL_REMOVE_SECRET = UrlTemplate.parse(`/api/v1/admin/oauth-clients/{clientId}/remove-secret`);
/*
* Defined in Swagger as 'oauthManagement'
*/
class OauthManagementService {
/**
* listClients at /api/v1/admin/oauth-clients/
*
* @param filter filter
* @param page = paged query
*/
public static listClients(filter: string | OAuthClientFilter, page: IPageRequest): Promise<FilteredPage<OAuthClient>> {
const qs = QueryString.stringify({
f: typeof filter === 'string' ? filter : undefined,
p: page.page || undefined,
l: page.size || 100,
d: page.direction ? page.direction : undefined,
s: page.properties && page.properties.length && page.properties || undefined,
}, {});
const apiUrl = URL_LIST_CLIENTS + (qs ? `?${qs}` : '');
// console.log(`Fetching from ${apiUrl}`);
const content = { data: typeof filter === 'string' ? null : { ...filter } };
return axiosBackend.request({
url: apiUrl,
method: 'GET',
...content,
}).then(({ data }) => data as FilteredPage<OAuthClient>);
}
/**
* saveExistingClient at /api/v1/admin/oauth-clients/save-client
*
* @param updates updates
*/
public static saveExistingClient(updates: OAuthClient): Promise<OAuthClient> {
const apiUrl = URL_SAVE_EXISTING_CLIENT;
// console.log(`Fetching from ${apiUrl}`);
const content = { data: updates };
return axiosBackend.request({
url: apiUrl,
method: 'POST',
...content,
}).then(({ data }) => data as OAuthClient);
}
/**
* getIssuedTokens at /api/v1/admin/oauth-clients/user/{uuid}/tokens
*
* @param uuid uuid
*/
public static getIssuedTokens(uuid: string): Promise<AccessToken[]> {
const apiUrl = URL_GET_ISSUED_TOKENS.expand({uuid});
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
return axiosBackend.request({
url: apiUrl,
method: 'GET',
...content,
}).then(({ data }) => data as AccessToken[]);
}
/**
* removeUsersAccessToken at /api/v1/admin/oauth-clients/user/{uuid}/{tokenId}/remove
*
* @param tokenId tokenId
* @param uuid uuid
*/
public static removeUsersAccessToken(tokenId: string, uuid: string): Promise<boolean> {
const apiUrl = URL_REMOVE_USERS_ACCESS_TOKEN.expand({tokenId, uuid});
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
return axiosBackend.request({
url: apiUrl,
method: 'DELETE',
...content,
}).then(({ data }) => data as boolean);
}
/**
* deleteClient at /api/v1/admin/oauth-clients/{clientId}
*
* @param clientId clientId
*/
public static deleteClient(clientId: string): Promise<OAuthClient> {
const apiUrl = URL_DELETE_CLIENT.expand({clientId});
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
return axiosBackend.request({
url: apiUrl,
method: 'DELETE',
...content,
}).then(({ data }) => data as OAuthClient);
}
/**
* removeAllAccessTokens at /api/v1/admin/oauth-clients/{clientId}/access-tokens
*
* @param clientId clientId
*/
public static removeAllAccessTokens(clientId: string): Promise<boolean> {
const apiUrl = URL_REMOVE_ALL_ACCESS_TOKENS.expand({clientId});
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
return axiosBackend.request({
url: apiUrl,
method: 'DELETE',
...content,
}).then(({ data }) => data as boolean);
}
/**
* removeAccessToken at /api/v1/admin/oauth-clients/{clientId}/access-tokens/{tokenId}
*
* @param clientId clientId
* @param tokenId tokenId
*/
public static removeAccessToken(clientId: string, tokenId: string): Promise<boolean> {
const apiUrl = URL_REMOVE_ACCESS_TOKEN.expand({clientId, tokenId});
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
return axiosBackend.request({
url: apiUrl,
method: 'DELETE',
...content,
}).then(({ data }) => data as boolean);
}
/**
* clientDetailsInfo at /api/v1/admin/oauth-clients/{clientId}/details
*
* @param clientId clientId
*/
public static clientDetailsInfo(clientId: string): Promise<ClientDetails> {
const apiUrl = URL_CLIENT_DETAILS_INFO.expand({clientId});
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
return axiosBackend.request({
url: apiUrl,
method: 'GET',
...content,
}).then(({ data }) => data as ClientDetails);
}
/**
* generateSecret at /api/v1/admin/oauth-clients/{clientId}/generate-secret
*
* @param clientId clientId
*/
public static generateSecret(clientId: string): Promise<string> {
const apiUrl = URL_GENERATE_SECRET.expand({clientId});
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
return axiosBackend.request({
url: apiUrl,
method: 'POST',
...content,
}).then(({ data }) => data as string);
}
/**
* removeAllRefreshTokens at /api/v1/admin/oauth-clients/{clientId}/refresh-tokens
*
* @param clientId clientId
*/
public static removeAllRefreshTokens(clientId: string): Promise<boolean> {
const apiUrl = URL_REMOVE_ALL_REFRESH_TOKENS.expand({clientId});
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
return axiosBackend.request({
url: apiUrl,
method: 'DELETE',
...content,
}).then(({ data }) => data as boolean);
}
/**
* removeRefreshToken at /api/v1/admin/oauth-clients/{clientId}/refresh-tokens/{tokenId}
*
* @param clientId clientId
* @param tokenId tokenId
*/
public static removeRefreshToken(clientId: string, tokenId: string): Promise<boolean> {
const apiUrl = URL_REMOVE_REFRESH_TOKEN.expand({clientId, tokenId});
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
return axiosBackend.request({
url: apiUrl,
method: 'DELETE',
...content,
}).then(({ data }) => data as boolean);
}
/**
* removeSecret at /api/v1/admin/oauth-clients/{clientId}/remove-secret
*
* @param clientId clientId
*/
public static removeSecret(clientId: string): Promise<ClientDetails> {
const apiUrl = URL_REMOVE_SECRET.expand({clientId});
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
return axiosBackend.request({
url: apiUrl,
method: 'POST',
...content,
}).then(({ data }) => data as ClientDetails);
}
}
export default OauthManagementService;
......@@ -12,7 +12,7 @@ const BooleanRadioGroup = ({input, meta, classes, formLabel, labelTrue, labelFal
<RadioGroup
{ ...input }
{ ...rest }
value={ input.value }
value={ `${input.value}` }
onChange={
(event, value) => input.onChange(value) // tslint:disable-line
}
......
......@@ -58,6 +58,7 @@ const CheckboxSelection = ({formLabel, singleColumn = false, options, renderOpti
label={ renderOptionLabel ? renderOptionLabel(option) : valueField ? option[valueField] : option }
control={
<Checkbox
{ ...rest }
checked={ fields.getAll() && fields.getAll().indexOf(valueField ? option[valueField] : option) !== -1 }
onChange={ (event, value) => handleCheckboxChange(fields,valueField ? option[valueField] : option) } // tslint:disable-line
/>
......
......@@ -49,6 +49,7 @@ const RadioSelection = ({formLabel, singleColumn = false, options, renderOptionL
label={ renderOptionLabel ? renderOptionLabel(option) : valueField ? option[valueField] : option }
control={ <Radio/> }
className={ classes.label }
{ ...rest }
/>
))
}
......
......@@ -10,6 +10,7 @@ import Country from 'model/geo/Country';
import {User} from 'model/user/User';
import GeoRegion from 'model/geo/GeoRegion';
import Article from 'model/cms/Article';
import OAuthClient from 'model/oauth/OAuthClient';
function SubsetLink({ to: subset, edit = false, children = null }
: { to: Subset, edit?: boolean, children?: any }) {
......@@ -86,6 +87,17 @@ function UserLink({ to: user, edit = false, children = null }: { to: User, edit?
return null;
}
}
function OAuthClientLink({ to: oAuthClient, edit = false, children = null }: { to: OAuthClient, edit?: boolean, children?: any }) {
if (oAuthClient) {
return (
<Link to={ `/admin/oauth/${oAuthClient.clientId}` }>
{ children || oAuthClient.clientId }
</Link>
);
} else {
return null;