Skip to content

Commit

Permalink
feat: add reject the authorisation API
Browse files Browse the repository at this point in the history
Signed-off-by: Oleksii Orel <[email protected]>
  • Loading branch information
olexii4 committed Nov 2, 2023
1 parent 72f0baa commit 5fab32e
Show file tree
Hide file tree
Showing 9 changed files with 204 additions and 6 deletions.
10 changes: 4 additions & 6 deletions packages/common/src/dto/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,15 +136,13 @@ export interface IUserProfile {
email: string;
username: string;
}
export interface IDevWorkspacePreferences {
'skip-authorisation': GitProvider[];
[key: string]: string | string[];
}

export type IEventList = CoreV1EventList;
export type IPodList = V1PodList;
export type PodLogs = {
[containerName: string]: {
logs: string;
failure: boolean;
};
};

export interface IDevWorkspaceList {
apiVersion?: string;
Expand Down
3 changes: 3 additions & 0 deletions packages/dashboard-backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { registerServerConfigRoute } from '@/routes/api/serverConfig';
import { registerSShKeysRoutes } from '@/routes/api/sshKeys';
import { registerUserProfileRoute } from '@/routes/api/userProfile';
import { registerWebsocket } from '@/routes/api/websocket';
import { registerWorkspacePreferencesRoute } from '@/routes/api/workspacePreferences';
import { registerYamlResolverRoute } from '@/routes/api/yamlResolver';
import { registerFactoryAcceptanceRedirect } from '@/routes/factoryAcceptanceRedirect';
import { registerWorkspaceRedirect } from '@/routes/workspaceRedirect';
Expand Down Expand Up @@ -117,5 +118,7 @@ export default async function buildApp(server: FastifyInstance): Promise<unknown
registerGettingStartedSamplesRoutes(isLocalRun(), server),

registerSShKeysRoutes(server),

registerWorkspacePreferencesRoute(server),
]);
}
13 changes: 13 additions & 0 deletions packages/dashboard-backend/src/constants/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,19 @@ export const namespacedTemplateSchema: JSONSchema7 = {
required: ['namespace', 'templateName'],
};

export const namespacedWorkspacePreferencesSchema: JSONSchema7 = {
type: 'object',
properties: {
namespace: {
type: 'string',
},
provider: {
type: 'string',
},
},
required: ['namespace', 'provider'],
};

export const namespacedSchema: JSONSchema7 = {
type: 'object',
properties: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import {
IDevWorkspaceApi,
IDevWorkspacePreferencesApi,
IDevWorkspaceTemplateApi,
IDockerConfigApi,
IEventApi,
Expand Down Expand Up @@ -68,4 +69,7 @@ export class DevWorkspaceClient implements IDevWorkspaceClient {
get sshKeysApi(): IShhKeysApi {
throw new Error('Method not implemented.');
}
get devWorkspacePreferencesApi(): IDevWorkspacePreferencesApi {
throw new Error('Method not implemented.');
}
}
6 changes: 6 additions & 0 deletions packages/dashboard-backend/src/devworkspaceClient/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import * as k8s from '@kubernetes/client-node';

import { DevWorkspaceApiService } from '@/devworkspaceClient/services/devWorkspaceApi';
import { DevWorkspacePreferencesApiService } from '@/devworkspaceClient/services/devWorkspacePreferencesApi';
import { DevWorkspaceTemplateApiService } from '@/devworkspaceClient/services/devWorkspaceTemplateApi';
import { DockerConfigApiService } from '@/devworkspaceClient/services/dockerConfigApi';
import { EventApiService } from '@/devworkspaceClient/services/eventApi';
Expand All @@ -29,6 +30,7 @@ import { UserProfileApiService } from '@/devworkspaceClient/services/userProfile
import {
IDevWorkspaceApi,
IDevWorkspaceClient,
IDevWorkspacePreferencesApi,
IDevWorkspaceTemplateApi,
IDockerConfigApi,
IEventApi,
Expand Down Expand Up @@ -108,4 +110,8 @@ export class DevWorkspaceClient implements IDevWorkspaceClient {
get sshKeysApi(): IShhKeysApi {
return new SshKeysService(this.kubeConfig);
}

get devWorkspacePreferencesApi(): IDevWorkspacePreferencesApi {
return new DevWorkspacePreferencesApiService(this.kubeConfig);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Copyright (c) 2018-2023 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/

import { api } from '@eclipse-che/common';
import { GitProvider } from '@eclipse-che/common/lib/dto/api';
import * as k8s from '@kubernetes/client-node';

import { createError } from '@/devworkspaceClient/services/helpers/createError';
import {
CoreV1API,
prepareCoreV1API,
} from '@/devworkspaceClient/services/helpers/prepareCoreV1API';
import { IDevWorkspacePreferencesApi } from '@/devworkspaceClient/types';

const ERROR_LABEL = 'CORE_V1_API_ERROR';
const DEV_WORKSPACE_PREFERENCES_CONFIGMAP = 'workspace-preferences-configmap';

const SKIP_AUTORIZATION_KEY = 'skip-authorisation';

export class DevWorkspacePreferencesApiService implements IDevWorkspacePreferencesApi {
private readonly coreV1API: CoreV1API;

constructor(kc: k8s.KubeConfig) {
this.coreV1API = prepareCoreV1API(kc);
}

async getWorkspacePreferences(namespace: string): Promise<api.IDevWorkspacePreferences> {
try {
const response = await this.coreV1API.readNamespacedConfigMap(
DEV_WORKSPACE_PREFERENCES_CONFIGMAP,
namespace,
);
const data = response.body.data;
if (data === undefined) {
throw new Error('Data is empty');
}

const skipAuthorisation =
data[SKIP_AUTORIZATION_KEY] && data[SKIP_AUTORIZATION_KEY] !== '[]'
? data[SKIP_AUTORIZATION_KEY].replace(/^\[/, '').replace(/\]$/, '').split(', ')
: [];

return Object.assign({}, data, {
[SKIP_AUTORIZATION_KEY]: skipAuthorisation,
}) as api.IDevWorkspacePreferences;
} catch (e) {
throw createError(e, ERROR_LABEL, 'Unable to get workspace preferences data');
}
}

public async removeProviderFromSkipAuthorization(
namespace: string,
provider: GitProvider,
): Promise<void> {
const devWorkspacePreferences = await this.getWorkspacePreferences(namespace);

const skipAuthorisation = devWorkspacePreferences[SKIP_AUTORIZATION_KEY].filter(
(val: string) => val !== provider,
);
const skipAuthorisationStr =
skipAuthorisation.length > 0 ? `[${skipAuthorisation.join(', ')}]` : '[]';
const data = Object.assign({}, devWorkspacePreferences, {
[SKIP_AUTORIZATION_KEY]: skipAuthorisationStr,
});

try {
await this.coreV1API.patchNamespacedConfigMap(
DEV_WORKSPACE_PREFERENCES_CONFIGMAP,
namespace,
{ data },
undefined,
undefined,
undefined,
undefined,
undefined,
{
headers: {
'content-type': k8s.PatchUtils.PATCH_FORMAT_STRATEGIC_MERGE_PATCH,
},
},
);
} catch (error) {
const message = `Unable to update workspace preferences in the namespace "${namespace}"`;
throw createError(undefined, ERROR_LABEL, message);
}
}
}
14 changes: 14 additions & 0 deletions packages/dashboard-backend/src/devworkspaceClient/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
V221DevfileComponents,
} from '@devfile/api';
import { api } from '@eclipse-che/common';
import { GitProvider } from '@eclipse-che/common/lib/dto/api';
import * as k8s from '@kubernetes/client-node';
import { IncomingHttpHeaders } from 'http';

Expand Down Expand Up @@ -348,6 +349,18 @@ export interface IUserProfileApi {
getUserProfile(namespace: string): Promise<api.IUserProfile | undefined>;
}

export interface IDevWorkspacePreferencesApi {
/**
* Returns workspace preferences object that contains skip-authorisation info.
*/
getWorkspacePreferences(namespace: string): Promise<api.IDevWorkspacePreferences>;

/**
* Removes the target provider from skip-authorisation property from the workspace preferences object.
*/
removeProviderFromSkipAuthorization(namespace: string, provider: GitProvider): Promise<void>;
}

export interface IPersonalAccessTokenApi {
/**
* Reads all the PAT secrets from the specified namespace.
Expand Down Expand Up @@ -390,6 +403,7 @@ export interface IDevWorkspaceClient {
gitConfigApi: IGitConfigApi;
gettingStartedSampleApi: IGettingStartedSampleApi;
sshKeysApi: IShhKeysApi;
devWorkspacePreferencesApi: IDevWorkspacePreferencesApi;
}

export interface IWatcherService<T = Record<string, unknown>> {
Expand Down
5 changes: 5 additions & 0 deletions packages/dashboard-backend/src/models/restParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,15 @@
*/

import { V1alpha2DevWorkspace, V1alpha2DevWorkspaceTemplate } from '@devfile/api';
import { api } from '@eclipse-che/common';

export interface INamespacedParams {
namespace: string;
}
export interface IWorkspacePreferencesParams {
namespace: string;
provider: api.GitProvider;
}

export interface IDockerConfigParams {
dockerconfig: string;
Expand Down
59 changes: 59 additions & 0 deletions packages/dashboard-backend/src/routes/api/workspacePreferences.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright (c) 2018-2023 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/

import { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';

import { baseApiPath } from '@/constants/config';
import { namespacedSchema, namespacedWorkspacePreferencesSchema } from '@/constants/schemas';
import { restParams } from '@/models';
import { getDevWorkspaceClient } from '@/routes/api/helpers/getDevWorkspaceClient';
import { getToken } from '@/routes/api/helpers/getToken';
import { getSchema } from '@/services/helpers';

const tags = ['WorkspacePreferences'];

export function registerWorkspacePreferencesRoute(instance: FastifyInstance) {
instance.register(async server => {
server.get(
`${baseApiPath}/workspacepreferences/:namespace`,
getSchema({ tags, params: namespacedSchema }),
async function (request: FastifyRequest) {
const { namespace } = request.params as restParams.INamespacedParams;
const token = getToken(request);
const { devWorkspacePreferencesApi } = getDevWorkspaceClient(token);
return devWorkspacePreferencesApi.getWorkspacePreferences(namespace);
},
);

server.delete(
`${baseApiPath}/workspacepreferences/:namespace/skip-authorisation/:provider`,
getSchema({
tags,
params: namespacedWorkspacePreferencesSchema,
response: {
204: {
description: 'The Provider is successfully removed from skip-authorisation list',
type: 'null',
},
},
}),
async function (request: FastifyRequest, reply: FastifyReply) {
const { namespace, provider } = request.params as restParams.IWorkspacePreferencesParams;
const token = getToken(request);
const { devWorkspacePreferencesApi } = getDevWorkspaceClient(token);
await devWorkspacePreferencesApi.removeProviderFromSkipAuthorization(namespace, provider);
reply.code(204);
return reply.send();
},
);
});
}

0 comments on commit 5fab32e

Please sign in to comment.