Skip to content

Commit

Permalink
Merge pull request #31 from eclipse-che/che#17246
Browse files Browse the repository at this point in the history
Provide user access token for filtered REST API methods in workspace service
  • Loading branch information
vzhukovs authored Jul 2, 2020
2 parents dce0652 + c316fbb commit 42d64f2
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 54 deletions.
4 changes: 3 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export interface IRestAPIConfig {
// path to self signed certificate
ssCrtPath?: string;
loggingEnabled?: boolean;
machineToken?: string;
userToken?: string;
}

export default class WorkspaceClient {
Expand All @@ -43,7 +45,7 @@ export default class WorkspaceClient {

const headers = config.headers || {};

const resources = new Resources(this.createAxiosInstance(config), baseUrl, headers);
const resources = new Resources(this.createAxiosInstance(config), baseUrl, headers, config.machineToken, config.userToken);
return new RemoteAPI(resources);
}

Expand Down
26 changes: 9 additions & 17 deletions src/rest/remote-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,8 @@ export interface IRemoteAPI {
deleteSshKey(service: string, name: string): Promise<void>;
/**
* Return the current authenticated user
*
* @param token keycloak user token should be used for requesting CheAPI. In case it's missing -
* the authorization header from the default headers list will be set.
*/
getCurrentUser(token?: string): Promise<User>;
getCurrentUser(): Promise<User>;
getUserPreferences(): Promise<Preferences>;
getUserPreferences(filter: string | undefined): Promise<Preferences>;
updateUserPreferences(update: Preferences): Promise<Preferences>;
Expand All @@ -104,17 +101,12 @@ export interface IRemoteAPI {
* Return registered oauth token.
*
* @param oAuthProvider oauth provider's name e.g. github.
* @param token keycloak user token should be used for requesting CheAPI. In case it's missing -
* the authorization header from the default headers list will be set.
*/
getOAuthToken(oAuthProvider: string, token?: string): Promise<string>;
getOAuthToken(oAuthProvider: string): Promise<string>;
/**
* Return list of registered oAuth providers.
*
* @param token keycloak user token should be used for requesting CheAPI. In case it's missing -
* the authorization header from the default headers list will be set.
*/
getOAuthProviders(token?: string): Promise<string[]>;
getOAuthProviders(): Promise<string[]>;
updateActivity(workspaceId: string): Promise<void>;
}

Expand Down Expand Up @@ -420,9 +412,9 @@ export class RemoteAPI implements IRemoteAPI {
});
}

getCurrentUser(token?: string): Promise<User> {
getCurrentUser(): Promise<User> {
return new Promise((resolve, reject) => {
this.remoteAPI.getCurrentUser(token)
this.remoteAPI.getCurrentUser()
.then((response: AxiosResponse<User>) => {
resolve(response.data);
})
Expand Down Expand Up @@ -480,9 +472,9 @@ export class RemoteAPI implements IRemoteAPI {
});
}

getOAuthToken(oAuthProvider: string, token?: string): Promise<string> {
getOAuthToken(oAuthProvider: string): Promise<string> {
return new Promise((resolve, reject) => {
this.remoteAPI.getOAuthToken(oAuthProvider, token)
this.remoteAPI.getOAuthToken(oAuthProvider)
.then((response: AxiosResponse<{ token: string }>) => {
resolve(response.data.token);
})
Expand All @@ -492,9 +484,9 @@ export class RemoteAPI implements IRemoteAPI {
});
}

getOAuthProviders(token?: string): Promise<string[]> {
getOAuthProviders(): Promise<string[]> {
return new Promise<string[]>((resolve, reject) => {
this.remoteAPI.getOAuthProviders(token)
this.remoteAPI.getOAuthProviders()
.then((response: AxiosResponse<any[]>) => {
resolve(response.data.map(provider => provider.name));
})
Expand Down
92 changes: 58 additions & 34 deletions src/rest/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,14 @@ export interface IResources {
createSshKey: (sshKeyPair: che.ssh.SshPair) => AxiosPromise<void>;
getSshKey: <T>(service: string, name: string) => AxiosPromise<T>;
getAllSshKey: <T>(service: string) => AxiosPromise<T[]>;
getOAuthProviders: (token?: string) => AxiosPromise<any[]>;
getOAuthProviders: () => AxiosPromise<any[]>;
deleteSshKey(service: string, name: string): AxiosPromise<void>;
getCurrentUser(token?: string): AxiosPromise<User>;
getCurrentUser(): AxiosPromise<User>;
getUserPreferences(filter: string | undefined): AxiosPromise<Preferences>;
updateUserPreferences(update: Preferences): AxiosPromise<Preferences>;
replaceUserPreferences(preferences: Preferences): AxiosPromise<Preferences>;
deleteUserPreferences(list: string[] | undefined): AxiosPromise<void>;
getOAuthToken(oAuthProvider: string, token?: string): AxiosPromise<{ token: string }>;
getOAuthToken(oAuthProvider: string): AxiosPromise<{ token: string }>;
updateActivity(workspaceId: string): AxiosPromise<void>;
}

Expand All @@ -66,7 +66,9 @@ export class Resources implements IResources {

constructor(private readonly axios: AxiosInstance,
private readonly baseUrl: string,
private readonly headers: { [headerTitle: string]: string } = {}) {
private readonly headers: { [headerTitle: string]: string } = {},
private readonly machineToken?: string,
private readonly userToken?: string) {
for (const title in headers) {
if (headers.hasOwnProperty(title)) {
this.axios.defaults.headers.common[title] = headers[title];
Expand All @@ -78,23 +80,26 @@ export class Resources implements IResources {
return this.axios.request<T[]>({
method: 'GET',
baseURL: this.baseUrl,
url: this.workspaceUrl
url: this.workspaceUrl,
headers: this.getHeadersWithAuthorization(this.userToken)
});
}

public getAllByNamespace<T>(namespace: string): AxiosPromise<T[]> {
return this.axios.request<T[]>({
method: 'GET',
baseURL: this.baseUrl,
url: `${this.workspaceUrl}/namespace/${namespace}`
url: `${this.workspaceUrl}/namespace/${namespace}`,
headers: this.getHeadersWithAuthorization(this.userToken)
});
}

public getById<T>(workspaceKey: string): AxiosPromise<T> {
return this.axios.request<T>({
method: 'GET',
baseURL: this.baseUrl,
url: `${this.workspaceUrl}/${workspaceKey}`
url: `${this.workspaceUrl}/${workspaceKey}`,
headers: this.getHeadersWithAuthorization(this.machineToken)
});
}

Expand All @@ -110,7 +115,8 @@ export class Resources implements IResources {
method: 'POST',
data: config,
baseURL: this.baseUrl,
url: url
url: url,
headers: this.getHeadersWithAuthorization(this.machineToken)
});
}

Expand All @@ -119,15 +125,17 @@ export class Resources implements IResources {
method: 'PUT',
data: workspace,
baseURL: this.baseUrl,
url: `${this.workspaceUrl}/${workspaceId}`
url: `${this.workspaceUrl}/${workspaceId}`,
headers: this.getHeadersWithAuthorization(this.machineToken)
});
}

public delete(workspaceId: string): AxiosPromise<any> {
return this.axios.request<any>({
method: 'DELETE',
baseURL: this.baseUrl,
url: `${this.workspaceUrl}/${workspaceId}`
url: `${this.workspaceUrl}/${workspaceId}`,
headers: this.getHeadersWithAuthorization(this.machineToken)
});
}

Expand All @@ -136,7 +144,8 @@ export class Resources implements IResources {
method: 'POST',
data: {},
baseURL: this.baseUrl,
url: `${this.workspaceUrl}/${workspaceId}/runtime?environment=${environmentName}`
url: `${this.workspaceUrl}/${workspaceId}/runtime?environment=${environmentName}`,
headers: this.getHeadersWithAuthorization(this.machineToken)
});
}

Expand All @@ -146,30 +155,34 @@ export class Resources implements IResources {
data: config,
baseURL: this.baseUrl,
url: `${this.workspaceUrl}/runtime?temporary=true`,
headers: this.getHeadersWithAuthorization(this.machineToken)
});
}

public stop(workspaceId: string): AxiosPromise<any> {
return this.axios.request<any>({
method: 'DELETE',
baseURL: this.baseUrl,
url: `${this.workspaceUrl}/${workspaceId}/runtime`
url: `${this.workspaceUrl}/${workspaceId}/runtime`,
headers: this.getHeadersWithAuthorization(this.machineToken)
});
}

public getSettings<T>(): AxiosPromise<T> {
return this.axios.request<T>({
method: 'GET',
baseURL: this.baseUrl,
url: `${this.workspaceUrl}/settings`
url: `${this.workspaceUrl}/settings`,
headers: this.getHeadersWithAuthorization(this.machineToken)
});
}

public getFactory<T>(factoryId: string): AxiosPromise<T> {
return this.axios.request<T>({
method: 'GET',
baseURL: this.baseUrl,
url: `${this.factoryUrl}/${factoryId}`
url: `${this.factoryUrl}/${factoryId}`,
headers: this.getHeadersWithAuthorization(this.machineToken)
});
}

Expand All @@ -178,7 +191,8 @@ export class Resources implements IResources {
method: 'POST',
baseURL: this.baseUrl,
data: {service: service, name: name},
url: `/ssh/generate`
url: `/ssh/generate`,
headers: this.getHeadersWithAuthorization(this.machineToken)
});
}

Expand All @@ -187,48 +201,53 @@ export class Resources implements IResources {
method: 'POST',
data: sshKeyPair,
baseURL: this.baseUrl,
url: `/ssh/`
url: `/ssh/`,
headers: this.getHeadersWithAuthorization(this.machineToken)
});
}

public getSshKey<T>(service: string, name: string): AxiosPromise<T> {
return this.axios.request<any>({
method: 'GET',
baseURL: this.baseUrl,
url: `/ssh/${service}/find?name=${name}`
url: `/ssh/${service}/find?name=${name}`,
headers: this.getHeadersWithAuthorization(this.machineToken)
});
}

public getAllSshKey<T>(service: string): AxiosPromise<T[]> {
return this.axios.request<any>({
method: 'GET',
baseURL: this.baseUrl,
url: `/ssh/${service}`
url: `/ssh/${service}`,
headers: this.getHeadersWithAuthorization(this.machineToken)
});
}

public getCurrentUser(token?: string): AxiosPromise<User> {
public getCurrentUser(): AxiosPromise<User> {
return this.axios.request<User>({
method: 'GET',
headers: this.getDefaultHeadersWithAuthorization(token),
baseURL: this.baseUrl,
url: `/user`
url: `/user`,
headers: this.getHeadersWithAuthorization(this.userToken)
});
}

public deleteSshKey(service: string, name: string): AxiosPromise<void> {
return this.axios.request<any>({
method: 'DELETE',
baseURL: this.baseUrl,
url: `/ssh/${service}?name=${name}`
url: `/ssh/${service}?name=${name}`,
headers: this.getHeadersWithAuthorization(this.machineToken)
});
}

public getUserPreferences(filter: string | undefined = undefined): AxiosPromise<Preferences> {
return this.axios.request<Preferences>({
method: 'GET',
baseURL: this.baseUrl,
url: filter ? `/preferences?filter=${filter}` : '/preferences'
url: filter ? `/preferences?filter=${filter}` : '/preferences',
headers: this.getHeadersWithAuthorization(this.machineToken)
});
}

Expand All @@ -237,7 +256,8 @@ export class Resources implements IResources {
method: 'PUT',
baseURL: this.baseUrl,
url: `/preferences`,
data: update
data: update,
headers: this.getHeadersWithAuthorization(this.machineToken)
});
}

Expand All @@ -246,7 +266,8 @@ export class Resources implements IResources {
method: 'POST',
baseURL: this.baseUrl,
url: `/preferences`,
data: preferences
data: preferences,
headers: this.getHeadersWithAuthorization(this.machineToken)
});
}

Expand All @@ -256,29 +277,31 @@ export class Resources implements IResources {
method: 'DELETE',
baseURL: this.baseUrl,
url: `/preferences`,
data: list
data: list,
headers: this.getHeadersWithAuthorization(this.machineToken)
});
}
return this.axios.request<void>({
method: 'DELETE',
baseURL: this.baseUrl,
url: `/preferences`
url: `/preferences`,
headers: this.getHeadersWithAuthorization(this.machineToken)
});
}

public getOAuthToken(oAuthProvider: string, token?: string): AxiosPromise<{ token: string }> {
public getOAuthToken(oAuthProvider: string): AxiosPromise<{ token: string }> {
return this.axios.request<{ token: string }>({
method: 'GET',
headers: this.getDefaultHeadersWithAuthorization(token),
baseURL: this.baseUrl,
url: `/oauth/token?oauth_provider=${oAuthProvider}`
url: `/oauth/token?oauth_provider=${oAuthProvider}`,
headers: this.getHeadersWithAuthorization(this.userToken)
});
}

public getOAuthProviders(token?: string): AxiosPromise<any[]> {
public getOAuthProviders(): AxiosPromise<any[]> {
return this.axios.request<any[]>({
method: 'GET',
headers: this.getDefaultHeadersWithAuthorization(token),
headers: this.getHeadersWithAuthorization(this.userToken),
baseURL: this.baseUrl,
url: '/oauth'
});
Expand All @@ -288,11 +311,12 @@ export class Resources implements IResources {
return this.axios.request<void>({
method: 'PUT',
baseURL: this.baseUrl,
url: `/activity/${workspaceId}`
url: `/activity/${workspaceId}`,
headers: this.getHeadersWithAuthorization(this.machineToken)
});
}

private getDefaultHeadersWithAuthorization(token?: string): { [key: string]: string } {
private getHeadersWithAuthorization(token?: string): { [key: string]: string } {
const headers: { [key: string]: string } = {};
for (const key in this.headers) {
if (this.headers.hasOwnProperty(key)) {
Expand Down
4 changes: 2 additions & 2 deletions test/client.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ describe('RestAPI >', () => {
);
await restApi.getAll();
expect(axios.request).toHaveBeenCalledTimes(1);
expect(axios.request).toHaveBeenCalledWith({'baseURL': '/api', 'method': 'GET', 'url': '/workspace'});
expect(axios.request).toHaveBeenCalledWith({'baseURL': '/api', 'headers': {}, 'method': 'GET', 'url': '/workspace'});

});

Expand All @@ -50,7 +50,7 @@ describe('RestAPI >', () => {
const preferences = await restApi.getUserPreferences();

expect(axios.request).toHaveBeenCalledTimes(1);
expect(axios.request).toHaveBeenCalledWith({'baseURL': '/api', 'method': 'GET', 'url': '/preferences'});
expect(axios.request).toHaveBeenCalledWith({'baseURL': '/api', 'headers':{}, 'method': 'GET', 'url': '/preferences'});
expect(preferences).toBeDefined();
expect(preferences).toHaveProperty('key1');
expect(preferences).toHaveProperty('key2');
Expand Down

0 comments on commit 42d64f2

Please sign in to comment.