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

feat(user-tenant-service):add idp controller to manage users in user tenant service #39

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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: 3 additions & 1 deletion facades/tenant-mgmt-facade/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,6 @@ AUTH0_CLIENT_SECRET=
# payment gateway id of payment gateway used for payment
GATEWAY_ACCOUNT_ID=
WEBHOOK_USERNAME=
WEBHOOK_PASSWORD=
WEBHOOK_PASSWORD=

USER_TENANT_SERVICE_URL=
156 changes: 156 additions & 0 deletions facades/tenant-mgmt-facade/src/controllers/tenant-user.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import {
RestBindings,
Request,
get,
getModelSchemaRef,
getFilterSchemaFor,
param,
del,
post,
requestBody,
patch,
getWhereSchemaFor,
} from '@loopback/rest';

import {inject} from '@loopback/context';
import {authenticate, STRATEGY} from 'loopback4-authentication';
import {authorize} from 'loopback4-authorization';

import {
CONTENT_TYPE,
ErrorCodes,
OPERATION_SECURITY_SPEC,
STATUS_CODE,
} from '@sourceloop/core';
import {AnyObject, Filter, Where} from '@loopback/repository';

import {
TenantMgmtProxyService,
UserTenantServiceProxy,
} from '../services/proxies';
import {PermissionKey} from '../enum/permission-key.enum';

import {UserView} from '../models/user-view.model';
import {UserDto} from '../models/user-dto.model';
import {User} from '../models/user.model';
import {IdpDetailsDTO} from '../models/idp-details-dto.model';
import {TenantHelperService} from '../services';
import {service} from '@loopback/core';
import { TenantUserService } from '../services/tenant-user.service';

const basePath = '/tenants/{id}/users';
export class TenantUserController {
private readonly currentUserToken;
constructor(
@inject(RestBindings.Http.REQUEST)
private readonly request: Request,
@inject('services.TenantMgmtProxyService')
private readonly tenantMgmtProxyService: TenantMgmtProxyService,
@inject('services.UserTenantServiceProxy')
private readonly utService: UserTenantServiceProxy,
@service(TenantUserService)
private readonly tenantUser: TenantUserService,
) {
this.currentUserToken = this.request.headers.authorization;
}

@authenticate(STRATEGY.BEARER, {
passReqToCallback: true,
})
@authorize({
permissions: [
PermissionKey.ViewTenantUser,
PermissionKey.ViewTenantUserNum,
],
})
@get(basePath, {
security: OPERATION_SECURITY_SPEC,
responses: {
...ErrorCodes,
[STATUS_CODE.OK]: {
description: 'Array of Tenant has many Users',
content: {
[CONTENT_TYPE.JSON]: {
schema: {type: 'array', items: getModelSchemaRef(User)},
},
},
},
},
})
async find(
@param.path.string('id') id: string,
@param.query.object('filter', getFilterSchemaFor(UserView))
filter?: Filter<UserView>,
): Promise<UserView[]> {
return this.utService.findTenantUser(
id,
this.currentUserToken,
JSON.stringify(filter),
);
}

@authenticate(STRATEGY.BEARER, {
passReqToCallback: true,
})
@authorize({
permissions: [
PermissionKey.ViewTenant,
PermissionKey.CreateTenantUser,
PermissionKey.CreateTenantUserNum,
],
})
@post(basePath, {
security: OPERATION_SECURITY_SPEC,
responses: {
...ErrorCodes,
[STATUS_CODE.OK]: {
description: 'tenant user model instance',
content: {
[CONTENT_TYPE.JSON]: {schema:Object},
},
},
},
})
async create(
@param.path.string('id') id: string,
@requestBody({
[CONTENT_TYPE.JSON]: {
schema: getModelSchemaRef(IdpDetailsDTO, {
title: 'NewUserInTenant',
}),
},
})
userData: IdpDetailsDTO,
): Promise<AnyObject> {
return this.tenantUser.createTenantUser(id,userData,this.currentUserToken);
}

@authenticate(STRATEGY.BEARER, {
passReqToCallback: true,
})
@authorize({
permissions: [
PermissionKey.DeleteTenantUser,
PermissionKey.DeleteTenantUserNum,
],
})
@del(`${basePath}/{userId}`, {
security: OPERATION_SECURITY_SPEC,
responses: {
[STATUS_CODE.NO_CONTENT]: {
description: 'User DELETE success',
},
},
})
async deleteById(
@param.path.string('id') id: string,
@param.path.string('userId') userId: string,
): Promise<void> {
return this.utService.deleteTenantUserById(
id,
userId,
this.currentUserToken,
);
}

}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './subscription-proxy.datasource';
export * from './tenant-mgmt-proxy.datasource';
export * from './notification-proxy.datasource';
export * from './user-tenant-service.datasource'
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import {inject, lifeCycleObserver, LifeCycleObserver} from '@loopback/core';
import {juggler} from '@loopback/repository';
import {CONTENT_TYPE} from '@sourceloop/core';

const tokenKey = '{token}';
const config = {
name: 'UserTenantService',
connector: 'rest',
baseUrl: process.env.USER_TENANT_SERVICE_URL as string,
crud: true,
options: {
baseUrl: process.env.USER_TENANT_SERVICE_URL as string,
headers: {
accept: CONTENT_TYPE.JSON,
['content-type']: CONTENT_TYPE.JSON,
},
},
operations: [
{
template: {
method: 'POST',
url: '/create-user',
headers: {
Authorization: '{token}',
},
body: '{body}',
},
functions: {
configureIdpDetails: [ 'body', 'token'],
},
},
{
template: {
method: 'POST',
url: '/tenants/{id}/users',
headers: {
Authorization: '{token}',
},
body: '{body}',
},
functions: {
createTenantUser: ['id', 'body', 'token'],
},
},
{
template: {
method: 'DELETE',
url: `/tenants/{id}/users/{userId}`,
headers: {
Authorization: '{token}',
},
},
functions: {
deleteTenantUserById: ['id', 'userId', 'token'],
},
},
{
template: {
method: 'GET',
url: '/tenants/{id}/users',
headers: {
Authorization: tokenKey,
},
query: {
filter: '{filter}',
},
options: {
useQuerystring: true,
},
},
functions: {
findTenantUser: ['id', 'token', 'filter'],
},
},
],
};

@lifeCycleObserver('datasource')
export class UserTenantServiceDataSource
extends juggler.DataSource
implements LifeCycleObserver
{
static dataSourceName = 'UserTenantService';
static readonly defaultConfig = config;

constructor(
@inject('datasources.config.UserTenantService', {optional: true})
dsConfig: object = config,
) {
const dsConfigJson = {
...dsConfig,
options: {
baseUrl: process.env.USER_SERVICE_URL,
headers: {
accept: 'application/json',
'content-type': 'application/json',
},
},
};
super(dsConfigJson);
}
}
3 changes: 3 additions & 0 deletions facades/tenant-mgmt-facade/src/enum/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ export * from './invoice-status.enum';
export * from './webhook-types.enum';
export * from './subscription-status.enum';
export * from './notification-type.enum';
export * from './status-codes.enum';
export * from './permission-key.enum';
export * from './user-config-key.enum';
15 changes: 15 additions & 0 deletions facades/tenant-mgmt-facade/src/enum/permission-key.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export const enum PermissionKey {
ViewTenantUser = 'ViewTenantUser',
CreateTenantUser = 'CreateTenantUser',
UpdateTenantUser = 'UpdateTenantUser',
UpdateTenantUserRestricted = 'UpdateTenantUserRestricted',
DeleteTenantUser = 'DeleteTenantUser',
CreateTenant = 'CreateTenant',
ViewTenant = 'ViewTenant',
ViewTenantUserNum = '12',
CreateTenantUserNum = '13',
UpdateTenantUserNum = '14',
DeleteTenantUserNum = '15',
CreateTenantNum = '16',
ViewTenantNum = '17',
}
68 changes: 68 additions & 0 deletions facades/tenant-mgmt-facade/src/enum/status-codes.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/* eslint-disable-next-line @typescript-eslint/naming-convention */
export const enum STATUS_CODE {
// sonarignore:start
CONTINUE = 100,
SWITCHING_PROTOCOLS = 101,
PROCESSING = 102,
EARLYHINTS = 103,
OK = 200,
CREATED = 201,
ACCEPTED = 202,
NON_AUTHORITATIVE_INFORMATION = 203,
NO_CONTENT = 204,
RESET_CONTENT = 205,
PARTIAL_CONTENT = 206,
AMBIGUOUS = 300,
MOVED_PERMANENTLY = 301,
FOUND = 302,
SEE_OTHER = 303,
NOT_MODIFIED = 304,
TEMPORARY_REDIRECT = 307,
PERMANENT_REDIRECT = 308,
BAD_REQUEST = 400,
UNAUTHORISED = 401,
PAYMENT_REQUIRED = 402,
FORBIDDEN = 403,
NOT_FOUND = 404,
METHOD_NOT_ALLOWED = 405,
NOT_ACCEPTABLE = 406,
PROXY_AUTHENTICATION_REQUIRED = 407,
REQUEST_TIMEOUT = 408,
CONFLICT = 409,
GONE = 410,
LENGTH_REQUIRED = 411,
PRECONDITION_FAILED = 412,
PAYLOAD_TOO_LARGE = 413,
URI_TOO_LONG = 414,
UNSUPPORTED_MEDIA_TYPE = 415,
REQUESTED_RANGE_NOT_SATISFIABLE = 416,
EXPECTATION_FAILED = 417,
I_AM_A_TEAPOT = 418,
MISDIRECTED = 421,
UNPROCESSED_ENTITY = 422,
FAILED_DEPENDENCY = 424,
PRECONDITION_REQUIRED = 428,
TOO_MANY_REQUESTS = 429,
INTERNAL_SERVER_ERROR = 500,
NOT_IMPLEMENTED = 501,
BAD_GATEWAY = 502,
SERVICE_UNAVAILABLE = 503,
GATEWAY_TIMEOUT = 504,
HTTP_VERSION_NOT_SUPPORTED = 505,
// sonarignore:end
}

export const ErrorCodes = {
[STATUS_CODE.UNAUTHORISED]: {
description: 'Invalid Credentials.',
},
[STATUS_CODE.BAD_REQUEST]: {
description: 'The syntax of the request entity is incorrect.',
},
[STATUS_CODE.UNPROCESSED_ENTITY]: {
description: 'The syntax of the request entity is incorrect',
},
[STATUS_CODE.NOT_FOUND]: {
description: 'The entity requested does not exist.',
},
};
3 changes: 3 additions & 0 deletions facades/tenant-mgmt-facade/src/enum/user-config-key.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const enum UserConfigKey {
LastAccessedUrl = 'last-accessed-url',
}
20 changes: 20 additions & 0 deletions facades/tenant-mgmt-facade/src/models/idp-details-dto.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import {getJsonSchema} from '@loopback/openapi-v3';
import {AnyObject, Model, model, property} from '@loopback/repository';
import { UserDto } from './user-dto.model';
import { Tenant } from '../types';

@model({
description: 'model describing payload for IDP controller',
})
export class IdpDetailsDTO extends UserDto {
@property({
type: 'object',
description: 'Tenat object',
jsonSchema: getJsonSchema(Object),
})
tenant: Tenant;

constructor(data?: Partial<IdpDetailsDTO>) {
super(data);
}
}
Loading