Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Naming #119

Merged
merged 5 commits into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/api-gateway/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ async function bootstrap() {
.setDescription('Juno Public API Docs')
.setVersion('1.0')
.addBearerAuth(
{ type: 'http', scheme: 'bearer', bearerFormat: 'API Key' },
'API Key',
{ type: 'http', scheme: 'bearer', bearerFormat: 'API_Key' },
'API_Key',
)

.build();
Expand Down
8 changes: 0 additions & 8 deletions packages/api-gateway/src/models/auth.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,6 @@ import { IsNotEmpty } from 'class-validator';
import { ApiKeyProto, IdentifierProto, JwtProto } from 'juno-proto';

export class IssueApiKeyRequest {
@ApiProperty({ description: 'Issuing user email' })
@IsNotEmpty()
email: string;

@ApiProperty({ description: 'Issuing user password' })
@IsNotEmpty()
password: string;

@ApiProperty({ description: 'Optional description for key' })
description?: string | undefined;

Expand Down
56 changes: 46 additions & 10 deletions packages/api-gateway/src/modules/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,35 +20,44 @@ import {
ApiResponse,
ApiBody,
} from '@nestjs/swagger';
import { ApiKeyProto, JwtProto } from 'juno-proto';
import { ApiKeyServiceClient } from 'juno-proto/dist/gen/api_key';
import { JwtServiceClient } from 'juno-proto/dist/gen/jwt';
import { ApiKeyProto, JwtProto, ProjectProto, UserProto } from 'juno-proto';
import { lastValueFrom } from 'rxjs';
import { User } from 'src/decorators/user.decorator';
import {
IssueApiKeyRequest,
IssueApiKeyResponse,
IssueJWTResponse,
} from 'src/models/auth.dto';
import { userLinkedToProject } from 'src/user_project_validator';

const { JWT_SERVICE_NAME } = JwtProto;
const { API_KEY_SERVICE_NAME } = ApiKeyProto;
const { PROJECT_SERVICE_NAME } = ProjectProto;

@ApiTags('auth')
@Controller('auth')
export class AuthController implements OnModuleInit {
private jwtService: JwtServiceClient;
private apiKeyService: ApiKeyServiceClient;
private jwtService: JwtProto.JwtServiceClient;
private apiKeyService: ApiKeyProto.ApiKeyServiceClient;
private projectService: ProjectProto.ProjectServiceClient;

constructor(
@Inject(JWT_SERVICE_NAME) private jwtClient: ClientGrpc,
@Inject(API_KEY_SERVICE_NAME) private apiClient: ClientGrpc,
@Inject(PROJECT_SERVICE_NAME) private projectClient: ClientGrpc,
) {}

onModuleInit() {
this.jwtService =
this.jwtClient.getService<JwtServiceClient>(JWT_SERVICE_NAME);
this.jwtClient.getService<JwtProto.JwtServiceClient>(JWT_SERVICE_NAME);
this.apiKeyService =
this.apiClient.getService<ApiKeyServiceClient>(API_KEY_SERVICE_NAME);
this.apiClient.getService<ApiKeyProto.ApiKeyServiceClient>(
API_KEY_SERVICE_NAME,
);
this.projectService =
this.projectClient.getService<ProjectProto.ProjectServiceClient>(
PROJECT_SERVICE_NAME,
);
}

@Post('/jwt')
Expand Down Expand Up @@ -88,12 +97,39 @@ export class AuthController implements OnModuleInit {
description: 'The API Key has been successfully created',
type: IssueApiKeyResponse,
})
@ApiHeader({
name: 'X-User-Email',
description: 'Email of an admin or superadmin user',
required: true,
schema: {
type: 'string',
},
})
@ApiHeader({
name: 'X-User-Password',
description: 'Password of the admin or superadmin user',
required: true,
schema: {
type: 'string',
},
})
@ApiBody({ type: IssueApiKeyRequest })
@Post('/key')
async createApiKey(@Body() issueApiKeyRequest: IssueApiKeyRequest) {
async createApiKey(
@User() user: UserProto.User,
@Body() issueApiKeyRequest: IssueApiKeyRequest,
) {
const linked = await userLinkedToProject({
project: issueApiKeyRequest.project,
user,
projectClient: this.projectService,
});
if (!linked || user.type == UserProto.UserType.USER) {
throw new UnauthorizedException(
'Only Superadmins & Linked Admins can create API Keys',
);
}
const obs = this.apiKeyService.issueApiKey({
email: issueApiKeyRequest.email,
password: issueApiKeyRequest.password,
description: issueApiKeyRequest.description,
environment: issueApiKeyRequest.environment,
project: issueApiKeyRequest.project,
Expand Down
40 changes: 38 additions & 2 deletions packages/api-gateway/src/modules/auth/auth.module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { Module } from '@nestjs/common';
import {
MiddlewareConsumer,
Module,
NestModule,
RequestMethod,
} from '@nestjs/common';
import { ClientsModule, Transport } from '@nestjs/microservices';
import { AuthController } from './auth.controller';
import { join } from 'path';
Expand All @@ -8,10 +13,17 @@ import {
ApiKeyProtoFile,
JwtProto,
JwtProtoFile,
ProjectProto,
ProjectProtoFile,
UserProto,
UserProtoFile,
} from 'juno-proto';
import { CredentialsMiddleware } from 'src/middleware/credentials.middleware';

const { JWT_SERVICE_NAME, JUNO_JWT_PACKAGE_NAME } = JwtProto;
const { API_KEY_SERVICE_NAME, JUNO_API_KEY_PACKAGE_NAME } = ApiKeyProto;
const { USER_AUTH_SERVICE_NAME, JUNO_USER_PACKAGE_NAME } = UserProto;
const { PROJECT_SERVICE_NAME, JUNO_PROJECT_PACKAGE_NAME } = ProjectProto;

@Module({
imports: [
Expand All @@ -37,8 +49,32 @@ const { API_KEY_SERVICE_NAME, JUNO_API_KEY_PACKAGE_NAME } = ApiKeyProto;
protoPath: ApiKeyProtoFile,
},
},
{
name: USER_AUTH_SERVICE_NAME,
transport: Transport.GRPC,
options: {
url: process.env.AUTH_SERVICE_ADDR,
package: JUNO_USER_PACKAGE_NAME,
protoPath: UserProtoFile,
},
},
{
name: PROJECT_SERVICE_NAME,
transport: Transport.GRPC,
options: {
url: process.env.DB_SERVICE_ADDR,
package: JUNO_PROJECT_PACKAGE_NAME,
protoPath: ProjectProtoFile,
},
},
]),
],
controllers: [AuthController],
})
export class AuthModule {}
export class AuthModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(CredentialsMiddleware)
.forRoutes({ path: 'auth/key', method: RequestMethod.POST });
}
}
23 changes: 21 additions & 2 deletions packages/api-gateway/src/modules/project/project.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
ProjectResponse,
} from 'src/models/project.dto';
import { AuthCommonProto, ProjectProto, UserProto } from 'juno-proto';
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
import { ApiHeader, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
import { User } from 'src/decorators/user.decorator';
import { ApiKey } from 'src/decorators/api_key.decorator';

Expand Down Expand Up @@ -92,6 +92,22 @@ export class ProjectController implements OnModuleInit {
@ApiOperation({
summary: 'Creates a new project with the specified parameters.',
})
@ApiHeader({
name: 'X-User-Email',
description: 'Email of an admin or superadmin user',
required: true,
schema: {
type: 'string',
},
})
@ApiHeader({
name: 'X-User-Password',
description: 'Password of the admin or superadmin user',
required: true,
schema: {
type: 'string',
},
})
@ApiResponse({
status: HttpStatus.BAD_REQUEST,
description: 'Name should not be empty',
Expand All @@ -106,7 +122,10 @@ export class ProjectController implements OnModuleInit {
@Body() params: CreateProjectModel,
) {
if (user.type !== UserProto.UserType.SUPERADMIN) {
throw new HttpException('Unauthorized', HttpStatus.UNAUTHORIZED);
throw new HttpException(
'Only Superadmins can create projects',
HttpStatus.UNAUTHORIZED,
);
}
const project = this.projectService.createProject({
name: params.name,
Expand Down
81 changes: 75 additions & 6 deletions packages/api-gateway/src/modules/user/user.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,18 @@ import {
Param,
Post,
Put,
UnauthorizedException,
} from '@nestjs/common';
import {
ApiResponse,
ApiTags,
ApiParam,
ApiBody,
ApiOperation,
ApiHeader,
} from '@nestjs/swagger';
import { ClientGrpc } from '@nestjs/microservices';
import { UserProto } from 'juno-proto';
import { ProjectProto, UserProto } from 'juno-proto';
import { lastValueFrom } from 'rxjs';
import {
CreateUserModel,
Expand All @@ -27,21 +29,31 @@ import {
UserResponse,
} from 'src/models/user.dto';
import { User } from 'src/decorators/user.decorator';
import { userLinkedToProject } from 'src/user_project_validator';

const { USER_SERVICE_NAME } = UserProto;
const { PROJECT_SERVICE_NAME } = ProjectProto;

@ApiTags('user')
@Controller('user')
export class UserController implements OnModuleInit {
private userService: UserProto.UserServiceClient;
private projectService: ProjectProto.ProjectServiceClient;

constructor(@Inject(USER_SERVICE_NAME) private userClient: ClientGrpc) {}
constructor(
@Inject(USER_SERVICE_NAME) private userClient: ClientGrpc,
@Inject(PROJECT_SERVICE_NAME) private projectClient: ClientGrpc,
) {}

onModuleInit() {
this.userService =
this.userClient.getService<UserProto.UserServiceClient>(
USER_SERVICE_NAME,
);
this.projectService =
this.projectClient.getService<ProjectProto.ProjectServiceClient>(
PROJECT_SERVICE_NAME,
);
}

@Get('id/:id')
Expand Down Expand Up @@ -79,6 +91,22 @@ export class UserController implements OnModuleInit {

@Post()
@ApiOperation({ summary: 'Create a new user.' })
@ApiHeader({
name: 'X-User-Email',
description: 'Email of an admin or superadmin user',
required: true,
schema: {
type: 'string',
},
})
@ApiHeader({
name: 'X-User-Password',
description: 'Password of the admin or superadmin user',
required: true,
schema: {
type: 'string',
},
})
@ApiBody({ type: CreateUserModel, description: 'The user details' })
@ApiResponse({
status: HttpStatus.CREATED,
Expand All @@ -97,7 +125,7 @@ export class UserController implements OnModuleInit {
@User() user: UserProto.User,
@Body() params: CreateUserModel,
) {
if (user.type !== UserProto.UserType.SUPERADMIN) {
if (user.type == UserProto.UserType.USER) {
throw new HttpException('Unauthorized', HttpStatus.UNAUTHORIZED);
}
const createdUser = this.userService.createUser({
Expand All @@ -114,6 +142,22 @@ export class UserController implements OnModuleInit {
description:
'Updates the user type for an existing user. User type can be thought of as a role with role-based permissions, e.g. SUPERADMIN could have permissions an ADMIN would not. Only SUPERADMIN users can set types',
})
@ApiHeader({
name: 'X-User-Email',
description: 'Email of an admin or superadmin user',
required: true,
schema: {
type: 'string',
},
})
@ApiHeader({
name: 'X-User-Password',
description: 'Password of the admin or superadmin user',
required: true,
schema: {
type: 'string',
},
})
@ApiBody({
type: SetUserTypeModel,
description: 'User ID, email, and the new type to be set',
Expand Down Expand Up @@ -161,6 +205,22 @@ export class UserController implements OnModuleInit {
description: 'User ID being linked to a project',
type: String,
})
@ApiHeader({
name: 'X-User-Email',
description: 'Email of an admin or superadmin user',
required: true,
schema: {
type: 'string',
},
})
@ApiHeader({
name: 'X-User-Password',
description: 'Password of the admin or superadmin user',
required: true,
schema: {
type: 'string',
},
})
@ApiBody({
type: LinkProjectModel,
description: 'Project details to link with the user',
Expand All @@ -182,13 +242,22 @@ export class UserController implements OnModuleInit {
@Param('id') idStr: string,
@Body() linkProjectBody: LinkProjectModel,
) {
if (user.type !== UserProto.UserType.SUPERADMIN) {
throw new HttpException('Unauthorized', HttpStatus.UNAUTHORIZED);
}
const id = parseInt(idStr);
if (Number.isNaN(id)) {
throw new HttpException('id must be a number', HttpStatus.BAD_REQUEST);
}
const linked = await userLinkedToProject({
project: {
id,
},
user,
projectClient: this.projectService,
});
if (!linked || user.type == UserProto.UserType.USER) {
throw new UnauthorizedException(
'Only Superadmins & Linked Admins can link Users to Projects',
);
}
const project = this.userService.linkProject({
user: {
id,
Expand Down
Loading
Loading