From 04b02af817b686cdd15d5e108d58b922530af742 Mon Sep 17 00:00:00 2001 From: Gianni Carafa Date: Mon, 14 Jul 2025 20:08:57 +0200 Subject: [PATCH 1/4] add readonly guard to new routes --- client/src/components/loginprompt.vue | 31 +++++++++++++++++++ server/.env.template | 4 ++- server/src/auth/permissions.guard.ts | 10 ++++++ server/src/auth/strategies/jwt.guard.ts | 3 ++ server/src/common/guards/readonly.guard.ts | 4 +++ .../deployments.service.spec.ts.old | 20 ++++++++++++ server/src/groups/groups.controller.ts | 4 +++ server/src/roles/roles.controller.ts | 4 +++ server/src/token/token.controller.ts | 5 ++- server/src/users/users.controller.ts | 7 +++++ 10 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 server/src/deployments/deployments.service.spec.ts.old diff --git a/client/src/components/loginprompt.vue b/client/src/components/loginprompt.vue index 72493d84f..2e52961cd 100644 --- a/client/src/components/loginprompt.vue +++ b/client/src/components/loginprompt.vue @@ -17,6 +17,18 @@ max-width="100" class="mx-auto" > + + + + + User: demo
+ Pass: demo +
({ error: false, errorshake: false, + username: '', + password: '', authMethods : { "local": false, "github": false, "oauth2": false } }), + computed: { + isDemoDomain(): boolean { + const demoDomains = [ + 'demo.kubero.dev', + //'kubero.localhost', + //'localhost' + ]; + const demoDomain = demoDomains.includes(window.location.hostname) + if (demoDomain) { + this.username = 'demo'; + this.password = 'demo'; + } + return demoDomain; + } + }, mounted() { this.getAuthMethods(); }, diff --git a/server/.env.template b/server/.env.template index a93d8903c..ef526a847 100644 --- a/server/.env.template +++ b/server/.env.template @@ -29,7 +29,9 @@ KUBERO_SESSION_KEY=randomString KUBERO_CLUSTERISSUER=letsencrypt-prod KUBERO_BUILD_REGISTRY=kubero-registry-yourdomain.com/something -KUBERO_PROMETHEUS_ENDPOINT=http://prometheus.localhost +# KUBERO_PROMETHEUS_ENDPOINT=http://kubero-prometheus-server # within cluster +KUBERO_PROMETHEUS_ENDPOINT=http:/127.0.0.1:8080 # for local development +# kubectl port-forward svc/kubero-prometheus-server 8080:80 -n kubero KUBERO_AUDIT=false KUBERO_AUDIT_DB_PATH=./db diff --git a/server/src/auth/permissions.guard.ts b/server/src/auth/permissions.guard.ts index 49ae0617b..0b297c55f 100644 --- a/server/src/auth/permissions.guard.ts +++ b/server/src/auth/permissions.guard.ts @@ -11,6 +11,16 @@ export class PermissionsGuard implements CanActivate { context.getHandler(), context.getClass(), ]); + /* Disabling for RBAC + if ( + !process.env.KUBERO_USERS && + !process.env.GITHUB_CLIENT_SECRET && + !process.env.OAUTH2_CLIENT_SECRET + ) { + return true; + } + */ + if (!requiredPermissions || requiredPermissions.length === 0) { return true; } diff --git a/server/src/auth/strategies/jwt.guard.ts b/server/src/auth/strategies/jwt.guard.ts index 5d30033d6..ef03fff08 100644 --- a/server/src/auth/strategies/jwt.guard.ts +++ b/server/src/auth/strategies/jwt.guard.ts @@ -19,6 +19,8 @@ export class JwtAuthGuard extends AuthGuard('jwt') { handleRequest(err, user, info) { // Disabling authentication when no auth method is defined + + /* Disabling for RBAC if ( !process.env.KUBERO_USERS && !process.env.GITHUB_CLIENT_SECRET && @@ -26,6 +28,7 @@ export class JwtAuthGuard extends AuthGuard('jwt') { ) { return true; } + */ if (err || !user) { //this.logger.debug('JwtAuthGuard.handleRequest Error', err, user, info); diff --git a/server/src/common/guards/readonly.guard.ts b/server/src/common/guards/readonly.guard.ts index c55eca5a3..fc1654056 100644 --- a/server/src/common/guards/readonly.guard.ts +++ b/server/src/common/guards/readonly.guard.ts @@ -6,6 +6,7 @@ import { Logger, } from '@nestjs/common'; +/* DEPRECATED: This guard is deprecated and will be removed in future versions in favour of Kubero roles*/ @Injectable() export class ReadonlyGuard implements CanActivate { private logger = new Logger(ReadonlyGuard.name); @@ -14,6 +15,9 @@ export class ReadonlyGuard implements CanActivate { this.logger.warn( 'Kubero is in read-only mode, write operations are blocked', ); + this.logger.warn( + 'KUBERO_READONLY is deprecated! Use Kubero\'s RBAC feature instead.', + ); throw new HttpException('Kubero is in read-only mode', 202); } return true; diff --git a/server/src/deployments/deployments.service.spec.ts.old b/server/src/deployments/deployments.service.spec.ts.old new file mode 100644 index 000000000..0968ab0b7 --- /dev/null +++ b/server/src/deployments/deployments.service.spec.ts.old @@ -0,0 +1,20 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { DeploymentsController } from './deployments.controller'; +import { DeploymentsService } from './deployments.service'; + +describe('DeploymentsService', () => { + let service: DeploymentsService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [DeploymentsController], + providers: [{ provide: DeploymentsService, useValue: {} }], + }).compile(); + + service = module.get(DeploymentsService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/server/src/groups/groups.controller.ts b/server/src/groups/groups.controller.ts index 436d90547..529072181 100644 --- a/server/src/groups/groups.controller.ts +++ b/server/src/groups/groups.controller.ts @@ -19,6 +19,7 @@ import { OKDTO } from '../common/dto/ok.dto'; import { GroupsService } from './groups.service'; import { PermissionsGuard } from '../auth/permissions.guard'; import { Permissions } from '../auth/permissions.decorator'; +import { ReadonlyGuard } from '../common/guards/readonly.guard'; @Controller({ path: 'api/groups', version: '1' }) export class GroupsController { @@ -46,6 +47,7 @@ export class GroupsController { @Post('/') @UseGuards(JwtAuthGuard, PermissionsGuard) @Permissions('user:write') + @UseGuards(ReadonlyGuard) @ApiBearerAuth('bearerAuth') @ApiForbiddenResponse({ description: 'Error: Unauthorized', @@ -68,6 +70,7 @@ export class GroupsController { @Delete('/:id') @UseGuards(JwtAuthGuard, PermissionsGuard) @Permissions('user:write') + @UseGuards(ReadonlyGuard) @ApiBearerAuth('bearerAuth') @ApiForbiddenResponse({ description: 'Error: Unauthorized', @@ -90,6 +93,7 @@ export class GroupsController { @Put('/:id') @UseGuards(JwtAuthGuard, PermissionsGuard) @Permissions('user:write') + @UseGuards(ReadonlyGuard) @ApiBearerAuth('bearerAuth') @ApiForbiddenResponse({ description: 'Error: Unauthorized', diff --git a/server/src/roles/roles.controller.ts b/server/src/roles/roles.controller.ts index 3cd25705c..173bd0657 100644 --- a/server/src/roles/roles.controller.ts +++ b/server/src/roles/roles.controller.ts @@ -19,6 +19,7 @@ import { OKDTO } from '../common/dto/ok.dto'; import { RolesService } from './roles.service'; import { PermissionsGuard } from '../auth/permissions.guard'; import { Permissions } from '../auth/permissions.decorator'; +import { ReadonlyGuard } from '../common/guards/readonly.guard'; @Controller({ path: 'api/roles', version: '1' }) export class RolesController { @@ -46,6 +47,7 @@ export class RolesController { @Post('/') @UseGuards(JwtAuthGuard, PermissionsGuard) @Permissions('user:write') + @UseGuards(ReadonlyGuard) @ApiBearerAuth('bearerAuth') @ApiForbiddenResponse({ description: 'Error: Unauthorized', @@ -65,6 +67,7 @@ export class RolesController { @Delete('/:roleId') @UseGuards(JwtAuthGuard, PermissionsGuard) @Permissions('user:write') + @UseGuards(ReadonlyGuard) @ApiBearerAuth('bearerAuth') @ApiForbiddenResponse({ description: 'Error: Unauthorized', @@ -84,6 +87,7 @@ export class RolesController { @Put('/:roleId') @UseGuards(JwtAuthGuard, PermissionsGuard) @Permissions('user:write') + @UseGuards(ReadonlyGuard) @ApiBearerAuth('bearerAuth') @ApiForbiddenResponse({ description: 'Error: Unauthorized', diff --git a/server/src/token/token.controller.ts b/server/src/token/token.controller.ts index c0b89b85b..de8fbca89 100644 --- a/server/src/token/token.controller.ts +++ b/server/src/token/token.controller.ts @@ -22,7 +22,7 @@ import { OKDTO } from '../common/dto/ok.dto'; import { TokenService } from './token.service'; import { PermissionsGuard } from '../auth/permissions.guard'; import { Permissions } from '../auth/permissions.decorator'; - +import { ReadonlyGuard } from '../common/guards/readonly.guard'; @Controller({ path: 'api/tokens', version: '1' }) export class TokenController { @@ -71,6 +71,7 @@ export class TokenController { @Post('/my') @UseGuards(JwtAuthGuard, PermissionsGuard) @Permissions('token:ok', 'token:write') + @UseGuards(ReadonlyGuard) @ApiBearerAuth('bearerAuth') @ApiForbiddenResponse({ description: 'Error: Unauthorized', @@ -109,6 +110,7 @@ export class TokenController { @Delete('/:id') @UseGuards(JwtAuthGuard, PermissionsGuard) @Permissions('token:ok', 'token:write') + @UseGuards(ReadonlyGuard) @ApiBearerAuth('bearerAuth') @ApiForbiddenResponse({ description: 'Error: Unauthorized', @@ -156,6 +158,7 @@ export class TokenController { @Delete('/my/:id') @UseGuards(JwtAuthGuard, PermissionsGuard) @Permissions('token:ok', 'token:write') + @UseGuards(ReadonlyGuard) @ApiBearerAuth('bearerAuth') @ApiForbiddenResponse({ description: 'Error: Unauthorized', diff --git a/server/src/users/users.controller.ts b/server/src/users/users.controller.ts index 4b5c18377..40a511148 100644 --- a/server/src/users/users.controller.ts +++ b/server/src/users/users.controller.ts @@ -26,6 +26,7 @@ import { GetAllUsersDTO } from './dto/users.dto'; import { FileInterceptor } from '@nestjs/platform-express'; import { PermissionsGuard } from '../auth/permissions.guard'; import { Permissions } from '../auth/permissions.decorator'; +import { ReadonlyGuard } from '../common/guards/readonly.guard'; @Controller({ path: 'api/users', version: '1' }) export class UsersController { @@ -130,6 +131,7 @@ export class UsersController { @Put('/:id') @UseGuards(JwtAuthGuard, PermissionsGuard) @Permissions('user:write') + @UseGuards(ReadonlyGuard) @ApiBearerAuth('bearerAuth') @ApiForbiddenResponse({ description: 'Error: Unauthorized', @@ -149,6 +151,7 @@ export class UsersController { @Delete('/:id') @UseGuards(JwtAuthGuard, PermissionsGuard) @Permissions('user:write') + @UseGuards(ReadonlyGuard) @ApiBearerAuth('bearerAuth') @ApiForbiddenResponse({ description: 'Error: Unauthorized', @@ -168,6 +171,7 @@ export class UsersController { @Put('/:id/password/') @UseGuards(JwtAuthGuard, PermissionsGuard) @Permissions('user:write') + @UseGuards(ReadonlyGuard) @ApiBearerAuth('bearerAuth') @ApiForbiddenResponse({ description: 'Error: Unauthorized', @@ -199,6 +203,7 @@ export class UsersController { @Put('/update-my-password') @UseGuards(JwtAuthGuard) + @UseGuards(ReadonlyGuard) @ApiBearerAuth('bearerAuth') @ApiForbiddenResponse({ description: 'Error: Unauthorized', @@ -231,6 +236,7 @@ export class UsersController { @Post('/') @UseGuards(JwtAuthGuard) + @UseGuards(ReadonlyGuard) @ApiBearerAuth('bearerAuth') @ApiForbiddenResponse({ description: 'Error: Unauthorized', @@ -276,6 +282,7 @@ export class UsersController { @Post('/profile/avatar') @UseGuards(JwtAuthGuard) @UseInterceptors(FileInterceptor('avatar')) + @UseGuards(ReadonlyGuard) @ApiBearerAuth('bearerAuth') @ApiForbiddenResponse({ description: 'Error: Unauthorized', From 2645b1b8fe6bc2f178fb821b03cb1312e0729329 Mon Sep 17 00:00:00 2001 From: Gianni Carafa Date: Tue, 15 Jul 2025 15:49:14 +0200 Subject: [PATCH 2/4] Update login credentials in the login prompt component to use 'demo/reader' as the username and '123456' as the password for demo domain access. --- client/src/components/loginprompt.vue | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/client/src/components/loginprompt.vue b/client/src/components/loginprompt.vue index 2e52961cd..55c14d362 100644 --- a/client/src/components/loginprompt.vue +++ b/client/src/components/loginprompt.vue @@ -26,8 +26,8 @@ border="start" class="mt-5" > - User: demo
- Pass: demo + User: demo/reader
+ Pass: 123456