Browse Source

feat: authguard for password change api

Signed-off-by: Pranav C <pranavxc@gmail.com>
pull/5444/head
Pranav C 1 year ago
parent
commit
973a7e67ba
  1. 28
      packages/nocodb-nest/src/app.module.ts
  2. 7
      packages/nocodb-nest/src/guards/global/global.guard.spec.ts
  3. 36
      packages/nocodb-nest/src/guards/global/global.guard.ts
  4. 33
      packages/nocodb-nest/src/modules/global/global.module.ts
  5. 5
      packages/nocodb-nest/src/modules/users/users.controller.ts
  6. 6
      packages/nocodb-nest/src/modules/users/users.module.ts
  7. 2
      packages/nocodb-nest/src/modules/users/users.service.ts
  8. 4
      packages/nocodb-nest/src/modules/views/views.controller.ts
  9. 10
      packages/nocodb-nest/src/strategies/authtoken.strategy/authtoken.strategy.ts
  10. 65
      packages/nocodb-nest/src/strategies/jwt.strategy.ts

28
packages/nocodb-nest/src/app.module.ts

@ -1,12 +1,13 @@
import { Module, RequestMethod } from '@nestjs/common';
import { APP_FILTER } from '@nestjs/core';
import { APP_FILTER, APP_GUARD } from '@nestjs/core';
import { ExtractJwt } from 'passport-jwt';
import { Connection } from './connection/connection';
import { GlobalExceptionFilter } from './filters/global-exception/global-exception.filter';
import { GlobalGuard } from './guards/global/global.guard';
import { GlobalMiddleware } from './middlewares/global/global.middleware';
import { AuthModule } from './modules/auth/auth.module';
import { ExtractProjectIdMiddleware } from './middlewares/extract-project-id/extract-project-id.middleware';
import { AuthService } from './modules/auth/auth.service'
import { AuthService } from './modules/auth/auth.service';
import { UsersModule } from './modules/users/users.module';
import { MetaService } from './meta/meta.service';
import { UsersService } from './modules/users/users.service';
@ -51,23 +52,24 @@ import { CachesModule } from './modules/caches/caches.module';
import { TestModule } from './modules/test/test.module';
import { PluginsModule } from './modules/plugins/plugins.module';
import { GlobalModule } from './modules/global/global.module';
import { LocalStrategy } from './strategies/local.strategy'
import NcConfigFactory from './utils/NcConfigFactory'
import { LocalStrategy } from './strategies/local.strategy';
import NcConfigFactory from './utils/NcConfigFactory';
import NcUpgrader from './version-upgrader/NcUpgrader';
import { ClientService } from './services/client/client.service';
import { AuthTokenStrategy } from './strategies/authtoken.strategy/authtoken.strategy';
import { BaseViewStrategy } from './strategies/base-view.strategy/base-view.strategy';
import { GoogleStrategy } from './strategies/google.strategy/google.strategy';
import type {
MiddlewareConsumer,
OnApplicationBootstrap,
Provider,
} from '@nestjs/common';
import { ClientService } from './services/client/client.service';
import { AuthTokenStrategy } from './strategies/authtoken.strategy/authtoken.strategy';
import { BaseViewStrategy } from './strategies/base-view.strategy/base-view.strategy';
import { GoogleStrategy } from './strategies/google.strategy/google.strategy';
/*
export const JwtStrategyProvider: Provider = {
provide: JwtStrategy,
useFactory: async (usersService: UsersService) => {
const config = await NcConfigFactory.make()
const config = await NcConfigFactory.make();
const options = {
// ignoreExpiration: false,
@ -82,6 +84,7 @@ export const JwtStrategyProvider: Provider = {
},
inject: [UsersService],
};
*/
@Module({
imports: [
@ -129,18 +132,23 @@ export const JwtStrategyProvider: Provider = {
],
controllers: [],
providers: [
// {
// provide: APP_GUARD,
// useClass: GlobalGuard,
// },
AuthService,
{
provide: APP_FILTER,
useClass: GlobalExceptionFilter,
},
JwtStrategyProvider,
// JwtStrategyProvider,
LocalStrategy,
ExtractProjectIdMiddleware,
ClientService,
AuthTokenStrategy,
BaseViewStrategy,
GoogleStrategy,
// GlobalGuard,
],
})
export class AppModule implements OnApplicationBootstrap {

7
packages/nocodb-nest/src/guards/global/global.guard.spec.ts

@ -0,0 +1,7 @@
import { GlobalGuard } from './global.guard';
describe('GlobalGuard', () => {
it('should be defined', () => {
expect(new GlobalGuard()).toBeDefined();
});
});

36
packages/nocodb-nest/src/guards/global/global.guard.ts

@ -0,0 +1,36 @@
import { Inject, Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { JwtStrategy } from '../../strategies/jwt.strategy';
import type { ExecutionContext } from '@nestjs/common';
@Injectable()
export class GlobalGuard extends AuthGuard(['jwt']) {
constructor(private jwtStrategy: JwtStrategy) {
super();
}
async canActivate(context: ExecutionContext) {
let result;
try {
result = (await super.canActivate(context)) as boolean;
} catch (e) {
console.log(e);
}
if (!result) {
// If JWT authentication fails, use the fallback strategy to set a default user
const req = context.switchToHttp().getRequest();
const user = await this.fallbackAuthenticate(req);
req.user = user;
return true;
}
return true;
}
private async fallbackAuthenticate(req: any): Promise<any> {
return this.jwtStrategy.validate(req, {
roles: {
guest: true,
},
});
}
}

33
packages/nocodb-nest/src/modules/global/global.module.ts

@ -1,8 +1,33 @@
import { Global, Module } from '@nestjs/common'
import { Global, Module, Provider } from '@nestjs/common'
import { JwtModule, JwtService } from '@nestjs/jwt'
import { ExtractJwt } from 'passport-jwt'
import { Connection } from '../../connection/connection'
import { GlobalGuard } from '../../guards/global/global.guard'
import { MetaService } from '../../meta/meta.service'
import { JwtStrategy } from '../../strategies/jwt.strategy'
import NcConfigFactory from '../../utils/NcConfigFactory'
import { jwtConstants } from '../auth/constants'
import { UsersModule } from '../users/users.module'
import { UsersService } from '../users/users.service'
export const JwtStrategyProvider: Provider = {
provide: JwtStrategy,
useFactory: async (usersService: UsersService) => {
const config = await NcConfigFactory.make();
const options = {
// ignoreExpiration: false,
jwtFromRequest: ExtractJwt.fromHeader('xc-auth'),
// expiresIn: '10h',
passReqToCallback: true,
secretOrKey: config.auth.jwt.secret,
...config.auth.jwt.options,
};
return new JwtStrategy(options, usersService);
},
inject: [UsersService],
};
@Global()
@Module({
@ -12,11 +37,17 @@ import { jwtConstants } from '../auth/constants'
providers: [
Connection,
MetaService,
UsersService,
JwtStrategyProvider,
GlobalGuard
],
exports: [
Connection,
MetaService,
// JwtService,
JwtStrategyProvider,
UsersService,
GlobalGuard
],
})
export class GlobalModule {

5
packages/nocodb-nest/src/modules/users/users.controller.ts

@ -12,6 +12,7 @@ import {
} from '@nestjs/common'
import * as ejs from 'ejs';
import { AuthGuard } from '@nestjs/passport';
import { GlobalGuard } from '../../guards/global/global.guard'
import { NcError } from '../../helpers/catchError';
import {
Acl,
@ -142,7 +143,7 @@ export class UsersController {
}
@Get(['/auth/user/me', '/api/v1/db/auth/user/me', '/api/v1/auth/user/me'])
@UseGuards(ExtractProjectIdMiddleware, AuthGuard('jwt'))
@UseGuards(ExtractProjectIdMiddleware, GlobalGuard)
async me(@Request() req) {
const user = {
...req.user,
@ -156,7 +157,9 @@ export class UsersController {
'/api/v1/db/auth/password/change',
'/api/v1/auth/password/change',
])
@UseGuards(GlobalGuard)
@Acl('passwordChange')
@HttpCode(200)
async passwordChange(@Request() req: any, @Body() body: any): Promise<any> {
if (!(req as any).isAuthenticated()) {
NcError.forbidden('Not allowed');

6
packages/nocodb-nest/src/modules/users/users.module.ts

@ -7,11 +7,7 @@ import { UsersController } from './users.controller';
@Module({
imports: [
GlobalModule,
JwtModule.register({
secret: jwtConstants.secret,
signOptions: { expiresIn: '10h' },
}),
GlobalModule
],
controllers: [UsersController],
providers: [UsersService],

2
packages/nocodb-nest/src/modules/users/users.service.ts

@ -1,6 +1,5 @@
import { promisify } from 'util';
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { OrgUserRoles, validatePassword } from 'nocodb-sdk';
import { v4 as uuidv4 } from 'uuid';
import { isEmail } from 'validator';
@ -28,7 +27,6 @@ import type {
export class UsersService {
constructor(
private metaService: MetaService,
private jwtService: JwtService,
) {}
async findOne(email: string) {

4
packages/nocodb-nest/src/modules/views/views.controller.ts

@ -10,17 +10,17 @@ import {
Request,
UseGuards,
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { ViewUpdateReqType } from 'nocodb-sdk';
import { PagedResponseImpl } from '../../helpers/PagedResponse';
import {
ExtractProjectIdMiddleware,
UseAclMiddleware,
} from '../../middlewares/extract-project-id/extract-project-id.middleware';
import { GlobalGuard } from '../../guards/global/global.guard';
import { ViewsService } from './views.service';
@Controller()
@UseGuards(ExtractProjectIdMiddleware, AuthGuard('jwt'))
@UseGuards(ExtractProjectIdMiddleware, GlobalGuard)
export class ViewsController {
constructor(private readonly viewsService: ViewsService) {}

10
packages/nocodb-nest/src/strategies/authtoken.strategy/authtoken.strategy.ts

@ -7,10 +7,12 @@ import type { Request } from 'express';
@Injectable()
export class AuthTokenStrategy extends PassportStrategy(Strategy, 'authtoken') {
constructor() {
super({
headerFields: ['xc-token'],
passReqToCallback: true,
});
super(
{
headerFields: ['xc-token'],
passReqToCallback: true,
},
);
}
// eslint-disable-next-line @typescript-eslint/ban-types

65
packages/nocodb-nest/src/strategies/jwt.strategy.ts

@ -1,16 +1,16 @@
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { OrgUserRoles } from 'nocodb-sdk';
import NocoCache from '../cache/NocoCache';
import { ProjectUser, User } from '../models';
import { genJwt } from '../modules/users/helpers';
import Noco from '../Noco';
import extractRolesObj from '../utils/extractRolesObj';
import { CacheGetType, CacheScope } from '../utils/globals';
import { jwtConstants } from '../modules/auth/constants';
import { UsersService } from '../modules/users/users.service';
import NcConfigFactory from '../utils/NcConfigFactory';
import { Injectable, UnauthorizedException } from '@nestjs/common'
import { PassportStrategy } from '@nestjs/passport'
import { ExtractJwt, Strategy } from 'passport-jwt'
import { OrgUserRoles } from 'nocodb-sdk'
import NocoCache from '../cache/NocoCache'
import { ProjectUser, User } from '../models'
import { genJwt } from '../modules/users/helpers'
import Noco from '../Noco'
import extractRolesObj from '../utils/extractRolesObj'
import { CacheGetType, CacheScope } from '../utils/globals'
import { jwtConstants } from '../modules/auth/constants'
import { UsersService } from '../modules/users/users.service'
import NcConfigFactory from '../utils/NcConfigFactory'
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
@ -18,10 +18,13 @@ export class JwtStrategy extends PassportStrategy(Strategy) {
super({
expiresIn: '10h',
...options,
});
})
}
async validate(req: any, jwtPayload: any) {
if (!jwtPayload?.email) return jwtPayload
// todo: improve this
if (
req.ncProjectId &&
@ -31,19 +34,19 @@ export class JwtStrategy extends PassportStrategy(Strategy) {
return {
...user,
roles: extractRolesObj(`owner,creator,${OrgUserRoles.SUPER_ADMIN}`),
};
});
}
})
}
const keyVals = [jwtPayload?.email];
const keyVals = [jwtPayload?.email]
if (req.ncProjectId) {
keyVals.push(req.ncProjectId);
keyVals.push(req.ncProjectId)
}
const key = keyVals.join('___');
const key = keyVals.join('___')
const cachedVal = await NocoCache.get(
`${CacheScope.USER}:${key}`,
CacheGetType.TYPE_OBJECT,
);
)
if (cachedVal) {
/*todo: tobe fixed
@ -54,12 +57,12 @@ export class JwtStrategy extends PassportStrategy(Strategy) {
) {
throw new Error('Token Expired. Please login again.');
}*/
return cachedVal;
return cachedVal
}
return User.getByEmail(jwtPayload?.email).then(
async (user: { roles: any; id: string }) => {
user.roles = extractRolesObj(user?.roles);
user.roles = extractRolesObj(user?.roles)
/*
todo: tobe fixed
if (
@ -77,26 +80,26 @@ export class JwtStrategy extends PassportStrategy(Strategy) {
return ProjectUser.get(req.ncProjectId, user.id).then(
async (projectUser) => {
user.roles = extractRolesObj(projectUser?.roles || user.roles);
user.roles = extractRolesObj(projectUser?.roles || user.roles)
user.roles = extractRolesObj(
user.roles === 'owner' ? 'owner,creator' : user.roles,
);
)
// + (user.roles ? `,${user.roles}` : '');
await NocoCache.set(`${CacheScope.USER}:${key}`, user);
return user;
await NocoCache.set(`${CacheScope.USER}:${key}`, user)
return user
},
);
)
} else {
// const roles = projectUser?.roles ? JSON.parse(projectUser.roles) : {guest: true};
if (user) {
await NocoCache.set(`${CacheScope.USER}:${key}`, user);
return user;
await NocoCache.set(`${CacheScope.USER}:${key}`, user)
return user
} else {
throw new Error('User not found');
throw new Error('User not found')
}
}
},
);
)
}
}

Loading…
Cancel
Save