mirror of
https://github.com/Unleash/unleash.git
synced 2025-04-19 01:17:18 +02:00
Fix/oas response (#2068)
* bug fix * bug fix * remove doc file * store fixes * bug fix * rollback deleted file * fix test * add url to token * return all tokens not just active * add url fix * PR comment * PR comment * PR comment * add the flag to the experimental options * fix env var name
This commit is contained in:
parent
1426d5be33
commit
aa589b5ff5
@ -30,22 +30,32 @@ interface ITokenUserRow {
|
|||||||
user_id: number;
|
user_id: number;
|
||||||
created_at: Date;
|
created_at: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tokenRowReducer = (acc, tokenRow) => {
|
const tokenRowReducer = (acc, tokenRow) => {
|
||||||
const { userId, name, ...token } = tokenRow;
|
const { userId, userName, userUsername, roleId, roleName, ...token } =
|
||||||
|
tokenRow;
|
||||||
if (!acc[tokenRow.secret]) {
|
if (!acc[tokenRow.secret]) {
|
||||||
acc[tokenRow.secret] = {
|
acc[tokenRow.secret] = {
|
||||||
secret: token.secret,
|
secret: token.secret,
|
||||||
name: token.name,
|
name: token.name,
|
||||||
|
url: token.url,
|
||||||
expiresAt: token.expires_at,
|
expiresAt: token.expires_at,
|
||||||
createdAt: token.created_at,
|
createdAt: token.created_at,
|
||||||
createdBy: token.created_by,
|
createdBy: token.created_by,
|
||||||
roleId: token.role_id,
|
role: {
|
||||||
|
id: roleId,
|
||||||
|
name: roleName,
|
||||||
|
},
|
||||||
users: [],
|
users: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const currentToken = acc[tokenRow.secret];
|
const currentToken = acc[tokenRow.secret];
|
||||||
if (userId) {
|
if (userId) {
|
||||||
currentToken.users.push({ userId, name });
|
currentToken.users.push({
|
||||||
|
id: userId,
|
||||||
|
name: userName,
|
||||||
|
username: userUsername,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
};
|
};
|
||||||
@ -58,6 +68,7 @@ const toRow = (newToken: IPublicSignupTokenCreate) => {
|
|||||||
expires_at: newToken.expiresAt,
|
expires_at: newToken.expiresAt,
|
||||||
created_by: newToken.createdBy || null,
|
created_by: newToken.createdBy || null,
|
||||||
role_id: newToken.roleId,
|
role_id: newToken.roleId,
|
||||||
|
url: newToken.url,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -97,15 +108,19 @@ export class PublicSignupTokenStore implements IPublicSignupTokenStore {
|
|||||||
'token_project_users.secret',
|
'token_project_users.secret',
|
||||||
)
|
)
|
||||||
.leftJoin(`users`, 'token_project_users.user_id', 'users.id')
|
.leftJoin(`users`, 'token_project_users.user_id', 'users.id')
|
||||||
|
.leftJoin(`roles`, 'tokens.role_id', 'roles.id')
|
||||||
.select(
|
.select(
|
||||||
'tokens.secret',
|
'tokens.secret',
|
||||||
'tokens.name',
|
'tokens.name',
|
||||||
'tokens.expires_at',
|
'tokens.expires_at',
|
||||||
'tokens.created_at',
|
'tokens.created_at',
|
||||||
'tokens.created_by',
|
'tokens.created_by',
|
||||||
'tokens.role_id',
|
'tokens.url',
|
||||||
'token_project_users.user_id',
|
'token_project_users.user_id as userId',
|
||||||
'users.name',
|
'users.name as userName',
|
||||||
|
'users.username as userUsername',
|
||||||
|
'roles.id as roleId',
|
||||||
|
'roles.name as roleName',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,9 +152,9 @@ export class PublicSignupTokenStore implements IPublicSignupTokenStore {
|
|||||||
): Promise<PublicSignupTokenSchema> {
|
): Promise<PublicSignupTokenSchema> {
|
||||||
const response = await this.db<ITokenRow>(TABLE).insert(
|
const response = await this.db<ITokenRow>(TABLE).insert(
|
||||||
toRow(newToken),
|
toRow(newToken),
|
||||||
['created_at'],
|
['*'],
|
||||||
);
|
);
|
||||||
return toTokens([response])[0];
|
return toTokens(response)[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
async isValid(secret: string): Promise<boolean> {
|
async isValid(secret: string): Promise<boolean> {
|
||||||
@ -163,14 +178,15 @@ export class PublicSignupTokenStore implements IPublicSignupTokenStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async get(key: string): Promise<PublicSignupTokenSchema> {
|
async get(key: string): Promise<PublicSignupTokenSchema> {
|
||||||
const row = await this.makeTokenUsersQuery()
|
const rows = await this.makeTokenUsersQuery().where(
|
||||||
.where('secret', key)
|
'tokens.secret',
|
||||||
.first();
|
key,
|
||||||
|
);
|
||||||
|
|
||||||
if (!row)
|
if (rows.length > 0) {
|
||||||
throw new NotFoundError('Could not find a token with that key');
|
return toTokens(rows)[0];
|
||||||
|
}
|
||||||
return toTokens([row])[0];
|
throw new NotFoundError('Could not find public signup token.');
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete(secret: string): Promise<void> {
|
async delete(secret: string): Promise<void> {
|
||||||
|
@ -5,6 +5,7 @@ test('publicSignupTokenSchema', () => {
|
|||||||
const data: PublicSignupTokenSchema = {
|
const data: PublicSignupTokenSchema = {
|
||||||
name: 'Default',
|
name: 'Default',
|
||||||
secret: 'some-secret',
|
secret: 'some-secret',
|
||||||
|
url: 'http://localhost:4242/invite-link/some-secret',
|
||||||
expiresAt: new Date().toISOString(),
|
expiresAt: new Date().toISOString(),
|
||||||
users: [],
|
users: [],
|
||||||
role: { name: 'Viewer ', type: 'type', id: 1 },
|
role: { name: 'Viewer ', type: 'type', id: 1 },
|
||||||
|
@ -6,11 +6,22 @@ export const publicSignupTokenSchema = {
|
|||||||
$id: '#/components/schemas/publicSignupTokenSchema',
|
$id: '#/components/schemas/publicSignupTokenSchema',
|
||||||
type: 'object',
|
type: 'object',
|
||||||
additionalProperties: false,
|
additionalProperties: false,
|
||||||
required: ['secret', 'name', 'expiresAt', 'createdAt', 'createdBy', 'role'],
|
required: [
|
||||||
|
'secret',
|
||||||
|
'url',
|
||||||
|
'name',
|
||||||
|
'expiresAt',
|
||||||
|
'createdAt',
|
||||||
|
'createdBy',
|
||||||
|
'role',
|
||||||
|
],
|
||||||
properties: {
|
properties: {
|
||||||
secret: {
|
secret: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
},
|
},
|
||||||
|
url: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
name: {
|
name: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
},
|
},
|
||||||
|
@ -92,13 +92,14 @@ describe('Public Signup API', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should get All', async () => {
|
test('should get All', async () => {
|
||||||
expect.assertions(1);
|
expect.assertions(2);
|
||||||
const appName = '123!23';
|
const appName = '123!23';
|
||||||
|
|
||||||
stores.clientApplicationsStore.upsert({ appName });
|
stores.clientApplicationsStore.upsert({ appName });
|
||||||
stores.publicSignupTokenStore.create({
|
stores.publicSignupTokenStore.insert({
|
||||||
name: 'some-name',
|
name: 'some-name',
|
||||||
expiresAt: expireAt(),
|
expiresAt: expireAt(),
|
||||||
|
createdBy: 'johnDoe',
|
||||||
});
|
});
|
||||||
|
|
||||||
return request
|
return request
|
||||||
@ -107,6 +108,7 @@ describe('Public Signup API', () => {
|
|||||||
.expect((res) => {
|
.expect((res) => {
|
||||||
const { tokens } = res.body;
|
const { tokens } = res.body;
|
||||||
expect(tokens[0].name).toBe('some-name');
|
expect(tokens[0].name).toBe('some-name');
|
||||||
|
expect(tokens[0].createdBy).toBe('johnDoe');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -13,7 +13,10 @@ import {
|
|||||||
resourceCreatedResponseSchema,
|
resourceCreatedResponseSchema,
|
||||||
} from '../../openapi/util/create-response-schema';
|
} from '../../openapi/util/create-response-schema';
|
||||||
import { serializeDates } from '../../types/serialize-dates';
|
import { serializeDates } from '../../types/serialize-dates';
|
||||||
import { emptyResponse } from '../../openapi/util/standard-responses';
|
import {
|
||||||
|
emptyResponse,
|
||||||
|
getStandardResponses,
|
||||||
|
} from '../../openapi/util/standard-responses';
|
||||||
import { PublicSignupTokenService } from '../../services/public-signup-token-service';
|
import { PublicSignupTokenService } from '../../services/public-signup-token-service';
|
||||||
import UserService from '../../services/user-service';
|
import UserService from '../../services/user-service';
|
||||||
import {
|
import {
|
||||||
@ -28,6 +31,7 @@ import { PublicSignupTokenCreateSchema } from '../../openapi/spec/public-signup-
|
|||||||
import { PublicSignupTokenUpdateSchema } from '../../openapi/spec/public-signup-token-update-schema';
|
import { PublicSignupTokenUpdateSchema } from '../../openapi/spec/public-signup-token-update-schema';
|
||||||
import { CreateUserSchema } from '../../openapi/spec/create-user-schema';
|
import { CreateUserSchema } from '../../openapi/spec/create-user-schema';
|
||||||
import { UserSchema, userSchema } from '../../openapi/spec/user-schema';
|
import { UserSchema, userSchema } from '../../openapi/spec/user-schema';
|
||||||
|
import { extractUsername } from '../../util/extract-user';
|
||||||
|
|
||||||
interface TokenParam {
|
interface TokenParam {
|
||||||
token: string;
|
token: string;
|
||||||
@ -76,7 +80,7 @@ export class PublicSignupController extends Controller {
|
|||||||
tags: ['Public signup tokens'],
|
tags: ['Public signup tokens'],
|
||||||
operationId: 'getAllPublicSignupTokens',
|
operationId: 'getAllPublicSignupTokens',
|
||||||
responses: {
|
responses: {
|
||||||
200: createResponseSchema('publicSignupTokenSchema'),
|
200: createResponseSchema('publicSignupTokensSchema'),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
@ -115,6 +119,7 @@ export class PublicSignupController extends Controller {
|
|||||||
requestBody: createRequestSchema('createUserSchema'),
|
requestBody: createRequestSchema('createUserSchema'),
|
||||||
responses: {
|
responses: {
|
||||||
200: createResponseSchema('userSchema'),
|
200: createResponseSchema('userSchema'),
|
||||||
|
...getStandardResponses(409),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
@ -195,7 +200,7 @@ export class PublicSignupController extends Controller {
|
|||||||
req: IAuthRequest,
|
req: IAuthRequest,
|
||||||
res: Response<PublicSignupTokensSchema>,
|
res: Response<PublicSignupTokensSchema>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const tokens = await this.publicSignupTokenService.getAllActiveTokens();
|
const tokens = await this.publicSignupTokenService.getAllTokens();
|
||||||
this.openApiService.respondWithValidation(
|
this.openApiService.respondWithValidation(
|
||||||
200,
|
200,
|
||||||
res,
|
res,
|
||||||
@ -237,7 +242,6 @@ export class PublicSignupController extends Controller {
|
|||||||
token,
|
token,
|
||||||
req.body,
|
req.body,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.openApiService.respondWithValidation(
|
this.openApiService.respondWithValidation(
|
||||||
201,
|
201,
|
||||||
res,
|
res,
|
||||||
@ -250,15 +254,16 @@ export class PublicSignupController extends Controller {
|
|||||||
req: IAuthRequest<void, void, PublicSignupTokenCreateSchema>,
|
req: IAuthRequest<void, void, PublicSignupTokenCreateSchema>,
|
||||||
res: Response<PublicSignupTokenSchema>,
|
res: Response<PublicSignupTokenSchema>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
const username = extractUsername(req);
|
||||||
const token =
|
const token =
|
||||||
await this.publicSignupTokenService.createNewPublicSignupToken(
|
await this.publicSignupTokenService.createNewPublicSignupToken(
|
||||||
req.body,
|
req.body,
|
||||||
req.user.name,
|
username,
|
||||||
);
|
);
|
||||||
this.openApiService.respondWithValidation(
|
this.openApiService.respondWithValidation(
|
||||||
201,
|
201,
|
||||||
res,
|
res,
|
||||||
publicSignupTokensSchema.$id,
|
publicSignupTokenSchema.$id,
|
||||||
serializeDates(token),
|
serializeDates(token),
|
||||||
{ location: `tokens/${token.secret}` },
|
{ location: `tokens/${token.secret}` },
|
||||||
);
|
);
|
||||||
@ -288,8 +293,9 @@ export class PublicSignupController extends Controller {
|
|||||||
res: Response,
|
res: Response,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const { token } = req.params;
|
const { token } = req.params;
|
||||||
|
const username = extractUsername(req);
|
||||||
|
|
||||||
await this.publicSignupTokenService.delete(token, req.user.name);
|
await this.publicSignupTokenService.delete(token, username);
|
||||||
res.status(200).end();
|
res.status(200).end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ import {
|
|||||||
} from '../types/events';
|
} from '../types/events';
|
||||||
import UserService, { ICreateUser } from './user-service';
|
import UserService, { ICreateUser } from './user-service';
|
||||||
import { IUser } from '../types/user';
|
import { IUser } from '../types/user';
|
||||||
|
import { URL } from 'url';
|
||||||
|
|
||||||
export class PublicSignupTokenService {
|
export class PublicSignupTokenService {
|
||||||
private store: IPublicSignupTokenStore;
|
private store: IPublicSignupTokenStore;
|
||||||
@ -29,6 +30,8 @@ export class PublicSignupTokenService {
|
|||||||
|
|
||||||
private timer: NodeJS.Timeout;
|
private timer: NodeJS.Timeout;
|
||||||
|
|
||||||
|
private readonly unleashBase: string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
{
|
{
|
||||||
publicSignupTokenStore,
|
publicSignupTokenStore,
|
||||||
@ -38,7 +41,7 @@ export class PublicSignupTokenService {
|
|||||||
IUnleashStores,
|
IUnleashStores,
|
||||||
'publicSignupTokenStore' | 'roleStore' | 'eventStore'
|
'publicSignupTokenStore' | 'roleStore' | 'eventStore'
|
||||||
>,
|
>,
|
||||||
config: Pick<IUnleashConfig, 'getLogger' | 'authentication'>,
|
config: Pick<IUnleashConfig, 'getLogger' | 'authentication' | 'server'>,
|
||||||
userService: UserService,
|
userService: UserService,
|
||||||
) {
|
) {
|
||||||
this.store = publicSignupTokenStore;
|
this.store = publicSignupTokenStore;
|
||||||
@ -48,6 +51,13 @@ export class PublicSignupTokenService {
|
|||||||
this.logger = config.getLogger(
|
this.logger = config.getLogger(
|
||||||
'/services/public-signup-token-service.ts',
|
'/services/public-signup-token-service.ts',
|
||||||
);
|
);
|
||||||
|
this.unleashBase = config.server.unleashUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getUrl(secret: string): string {
|
||||||
|
return new URL(
|
||||||
|
`${this.unleashBase}/invite-link/${secret}/signup`,
|
||||||
|
).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async get(secret: string): Promise<PublicSignupTokenSchema> {
|
public async get(secret: string): Promise<PublicSignupTokenSchema> {
|
||||||
@ -77,11 +87,13 @@ export class PublicSignupTokenService {
|
|||||||
secret: string,
|
secret: string,
|
||||||
createUser: ICreateUser,
|
createUser: ICreateUser,
|
||||||
): Promise<IUser> {
|
): Promise<IUser> {
|
||||||
|
const token = await this.get(secret);
|
||||||
|
createUser.rootRole = token.role.id;
|
||||||
const user = await this.userService.createUser(createUser);
|
const user = await this.userService.createUser(createUser);
|
||||||
await this.store.addTokenUser(secret, user.id);
|
await this.store.addTokenUser(secret, user.id);
|
||||||
await this.eventStore.store(
|
await this.eventStore.store(
|
||||||
new PublicSignupTokenUserAddedEvent({
|
new PublicSignupTokenUserAddedEvent({
|
||||||
createdBy: 'userId',
|
createdBy: 'System',
|
||||||
data: { secret, userId: user.id },
|
data: { secret, userId: user.id },
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -109,26 +121,29 @@ export class PublicSignupTokenService {
|
|||||||
createdBy: string,
|
createdBy: string,
|
||||||
): Promise<PublicSignupTokenSchema> {
|
): Promise<PublicSignupTokenSchema> {
|
||||||
const viewerRole = await this.roleStore.getRoleByName(RoleName.VIEWER);
|
const viewerRole = await this.roleStore.getRoleByName(RoleName.VIEWER);
|
||||||
|
const secret = this.generateSecretKey();
|
||||||
|
const url = this.getUrl(secret);
|
||||||
const newToken: IPublicSignupTokenCreate = {
|
const newToken: IPublicSignupTokenCreate = {
|
||||||
name: tokenCreate.name,
|
name: tokenCreate.name,
|
||||||
expiresAt: new Date(tokenCreate.expiresAt),
|
expiresAt: new Date(tokenCreate.expiresAt),
|
||||||
secret: this.generateSecretKey(),
|
secret: secret,
|
||||||
roleId: viewerRole ? viewerRole.id : -1,
|
roleId: viewerRole ? viewerRole.id : -1,
|
||||||
createdBy: createdBy,
|
createdBy: createdBy,
|
||||||
|
url: url,
|
||||||
};
|
};
|
||||||
const token = await this.store.insert(newToken);
|
const token = await this.store.insert(newToken);
|
||||||
|
|
||||||
await this.eventStore.store(
|
await this.eventStore.store(
|
||||||
new PublicSignupTokenCreatedEvent({
|
new PublicSignupTokenCreatedEvent({
|
||||||
createdBy: createdBy,
|
createdBy: createdBy,
|
||||||
data: newToken,
|
data: token,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
private generateSecretKey(): string {
|
private generateSecretKey(): string {
|
||||||
return crypto.randomBytes(32).toString('hex');
|
return crypto.randomBytes(16).toString('hex');
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
|
@ -18,6 +18,10 @@ export const defaultExperimentalOptions = {
|
|||||||
process.env.UNLEASH_EXPERIMENTAL_BATCH_METRICS,
|
process.env.UNLEASH_EXPERIMENTAL_BATCH_METRICS,
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
|
publicSignup: parseEnvVarBoolean(
|
||||||
|
process.env.UNLEASH_EXPERIMENTAL_PUBLIC_SIGNUP,
|
||||||
|
false,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
externalResolver: { isEnabled: (): boolean => false },
|
externalResolver: { isEnabled: (): boolean => false },
|
||||||
};
|
};
|
||||||
|
@ -6,6 +6,7 @@ export interface IPublicSignupTokenCreate {
|
|||||||
roleId: number;
|
roleId: number;
|
||||||
secret: string;
|
secret: string;
|
||||||
createdBy: string;
|
createdBy: string;
|
||||||
|
url: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IPublicSignupToken extends IPublicSignupTokenCreate {
|
export interface IPublicSignupToken extends IPublicSignupTokenCreate {
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
exports.up = function (db, callback) {
|
||||||
|
db.runSql(
|
||||||
|
`
|
||||||
|
ALTER table public_signup_tokens
|
||||||
|
ADD COLUMN IF NOT EXISTS url text
|
||||||
|
`,
|
||||||
|
callback,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.down = function (db, callback) {
|
||||||
|
db.runSql(
|
||||||
|
`
|
||||||
|
ALTER table public_signup_tokens
|
||||||
|
DROP COLUMN url
|
||||||
|
`,
|
||||||
|
callback,
|
||||||
|
);
|
||||||
|
};
|
223
src/test/e2e/api/admin/public-signup-token.e2e.test.ts
Normal file
223
src/test/e2e/api/admin/public-signup-token.e2e.test.ts
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
import { setupAppWithCustomAuth } from '../../helpers/test-helper';
|
||||||
|
import dbInit from '../../helpers/database-init';
|
||||||
|
import getLogger from '../../../fixtures/no-logger';
|
||||||
|
import { RoleName } from '../../../../lib/types/model';
|
||||||
|
import { PublicSignupTokenCreateSchema } from '../../../../lib/openapi/spec/public-signup-token-create-schema';
|
||||||
|
import { CreateUserSchema } from '../../../../lib/openapi/spec/create-user-schema';
|
||||||
|
|
||||||
|
let stores;
|
||||||
|
let db;
|
||||||
|
|
||||||
|
jest.mock('../../../../lib/util/flag-resolver', () => {
|
||||||
|
return jest.fn().mockImplementation(() => {
|
||||||
|
return {
|
||||||
|
getAll: jest.fn(),
|
||||||
|
isEnabled: jest.fn().mockResolvedValue(true),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
db = await dbInit('test', getLogger);
|
||||||
|
stores = db.stores;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await stores.publicSignupTokenStore.deleteAll();
|
||||||
|
await stores.eventStore.deleteAll();
|
||||||
|
await stores.userStore.deleteAll();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
if (db) {
|
||||||
|
await db.destroy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const expireAt = (addDays: number = 7): Date => {
|
||||||
|
let now = new Date();
|
||||||
|
now.setDate(now.getDate() + addDays);
|
||||||
|
return now;
|
||||||
|
};
|
||||||
|
|
||||||
|
test('admin users should be able to create a token', async () => {
|
||||||
|
expect.assertions(3);
|
||||||
|
|
||||||
|
const preHook = (app, config, { userService, accessService }) => {
|
||||||
|
app.use('/api/admin/', async (req, res, next) => {
|
||||||
|
const role = await accessService.getRootRole(RoleName.ADMIN);
|
||||||
|
const user = await userService.createUser({
|
||||||
|
email: 'admin@example.com',
|
||||||
|
rootRole: role.id,
|
||||||
|
});
|
||||||
|
req.user = user;
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const { request, destroy } = await setupAppWithCustomAuth(stores, preHook);
|
||||||
|
|
||||||
|
const tokenCreate: PublicSignupTokenCreateSchema = {
|
||||||
|
name: 'some-name',
|
||||||
|
expiresAt: expireAt().toISOString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
await request
|
||||||
|
.post('/api/admin/invite-link/tokens')
|
||||||
|
.send(tokenCreate)
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.expect(201)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.name).toBe('some-name');
|
||||||
|
expect(res.body.secret).not.toBeNull();
|
||||||
|
expect(res.body.url).not.toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
await destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('no permission to validate a token', async () => {
|
||||||
|
const preHook = (app, config, { userService, accessService }) => {
|
||||||
|
app.use('/api/admin/', async (req, res, next) => {
|
||||||
|
const admin = await accessService.getRootRole(RoleName.ADMIN);
|
||||||
|
await userService.createUser({
|
||||||
|
email: 'admin@example.com',
|
||||||
|
username: 'admin@example.com',
|
||||||
|
rootRole: admin.id,
|
||||||
|
});
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const { request, destroy } = await setupAppWithCustomAuth(stores, preHook);
|
||||||
|
|
||||||
|
await stores.publicSignupTokenStore.insert({
|
||||||
|
name: 'some-name',
|
||||||
|
expiresAt: expireAt(),
|
||||||
|
secret: 'some-secret',
|
||||||
|
createAt: new Date(),
|
||||||
|
createdBy: 'admin@example.com',
|
||||||
|
roleId: 3,
|
||||||
|
});
|
||||||
|
await request
|
||||||
|
.post('/api/admin/invite-link/tokens/some-secret/validate')
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
await destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return 401 if token can not be validate', async () => {
|
||||||
|
const preHook = (app, config, { userService, accessService }) => {
|
||||||
|
app.use('/api/admin/', async (req, res, next) => {
|
||||||
|
const admin = await accessService.getRootRole(RoleName.ADMIN);
|
||||||
|
await userService.createUser({
|
||||||
|
email: 'admin@example.com',
|
||||||
|
username: 'admin@example.com',
|
||||||
|
rootRole: admin.id,
|
||||||
|
});
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const { request, destroy } = await setupAppWithCustomAuth(stores, preHook);
|
||||||
|
|
||||||
|
await request
|
||||||
|
.post('/api/admin/invite-link/tokens/some-invalid-secret/validate')
|
||||||
|
.expect(401);
|
||||||
|
|
||||||
|
await destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('users can signup with invite-link', async () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
|
||||||
|
const preHook = (app, config, { userService, accessService }) => {
|
||||||
|
app.use('/api/admin/', async (req, res, next) => {
|
||||||
|
const admin = await accessService.getRootRole(RoleName.ADMIN);
|
||||||
|
await userService.createUser({
|
||||||
|
email: 'admin@example.com',
|
||||||
|
username: 'admin@example.com',
|
||||||
|
rootRole: admin.id,
|
||||||
|
});
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const { request, destroy } = await setupAppWithCustomAuth(stores, preHook);
|
||||||
|
|
||||||
|
await stores.publicSignupTokenStore.insert({
|
||||||
|
name: 'some-name',
|
||||||
|
expiresAt: expireAt(),
|
||||||
|
secret: 'some-secret',
|
||||||
|
url: 'http://localhost:4242/invite-lint/some-secret/signup',
|
||||||
|
createAt: new Date(),
|
||||||
|
createdBy: 'admin@example.com',
|
||||||
|
roleId: 3,
|
||||||
|
});
|
||||||
|
|
||||||
|
const createUser: CreateUserSchema = {
|
||||||
|
username: 'some-username',
|
||||||
|
email: 'some@example.com',
|
||||||
|
password: 'eweggwEG',
|
||||||
|
sendEmail: false,
|
||||||
|
rootRole: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
await request
|
||||||
|
.post('/api/admin/invite-link/tokens/some-secret/signup')
|
||||||
|
.send(createUser)
|
||||||
|
.expect(201)
|
||||||
|
.expect((res) => {
|
||||||
|
const user = res.body;
|
||||||
|
expect(user.username).toBe('some-username');
|
||||||
|
});
|
||||||
|
|
||||||
|
await destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('can get a token with users', async () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
|
||||||
|
const preHook = (app, config, { userService, accessService }) => {
|
||||||
|
app.use('/api/admin/', async (req, res, next) => {
|
||||||
|
const role = await accessService.getRootRole(RoleName.ADMIN);
|
||||||
|
const user = await userService.createUser({
|
||||||
|
email: 'admin@example.com',
|
||||||
|
rootRole: role.id,
|
||||||
|
});
|
||||||
|
req.user = user;
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const { request, destroy } = await setupAppWithCustomAuth(stores, preHook);
|
||||||
|
|
||||||
|
await stores.publicSignupTokenStore.insert({
|
||||||
|
name: 'some-name',
|
||||||
|
expiresAt: expireAt(),
|
||||||
|
secret: 'some-secret',
|
||||||
|
createAt: new Date(),
|
||||||
|
createdBy: 'admin@example.com',
|
||||||
|
roleId: 3,
|
||||||
|
});
|
||||||
|
|
||||||
|
const user = await stores.userStore.insert({
|
||||||
|
username: 'some-username',
|
||||||
|
email: 'some@example.com',
|
||||||
|
password: 'eweggwEG',
|
||||||
|
sendEmail: false,
|
||||||
|
rootRole: 3,
|
||||||
|
});
|
||||||
|
|
||||||
|
await stores.publicSignupTokenStore.addTokenUser('some-secret', user.id);
|
||||||
|
|
||||||
|
await request
|
||||||
|
.get('/api/admin/invite-link/tokens/some-secret')
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
const token = res.body;
|
||||||
|
expect(token.users.length).toEqual(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
await destroy();
|
||||||
|
});
|
@ -2432,6 +2432,9 @@ exports[`should serve the OpenAPI spec 1`] = `
|
|||||||
"secret": {
|
"secret": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
"users": {
|
"users": {
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/components/schemas/userSchema",
|
"$ref": "#/components/schemas/userSchema",
|
||||||
@ -2442,6 +2445,7 @@ exports[`should serve the OpenAPI spec 1`] = `
|
|||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
"secret",
|
"secret",
|
||||||
|
"url",
|
||||||
"name",
|
"name",
|
||||||
"expiresAt",
|
"expiresAt",
|
||||||
"createdAt",
|
"createdAt",
|
||||||
@ -4512,11 +4516,11 @@ If the provided project does not exist, the list of events will be empty.",
|
|||||||
"content": {
|
"content": {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/components/schemas/publicSignupTokenSchema",
|
"$ref": "#/components/schemas/publicSignupTokensSchema",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"description": "publicSignupTokenSchema",
|
"description": "publicSignupTokensSchema",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"tags": [
|
"tags": [
|
||||||
@ -4680,6 +4684,9 @@ If the provided project does not exist, the list of events will be empty.",
|
|||||||
},
|
},
|
||||||
"description": "userSchema",
|
"description": "userSchema",
|
||||||
},
|
},
|
||||||
|
"409": {
|
||||||
|
"description": "The provided resource can not be created or updated because it would conflict with the current state of the resource or with an already existing resource, respectively.",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"tags": [
|
"tags": [
|
||||||
"Public signup tokens",
|
"Public signup tokens",
|
||||||
|
@ -47,13 +47,14 @@ export default class FakePublicSignupStore implements IPublicSignupTokenStore {
|
|||||||
expiresAt: newToken.expiresAt.toISOString(),
|
expiresAt: newToken.expiresAt.toISOString(),
|
||||||
createdAt: new Date().toISOString(),
|
createdAt: new Date().toISOString(),
|
||||||
users: [],
|
users: [],
|
||||||
|
url: 'some=url',
|
||||||
name: newToken.name,
|
name: newToken.name,
|
||||||
role: {
|
role: {
|
||||||
name: 'Viewer',
|
name: 'Viewer',
|
||||||
type: '',
|
type: '',
|
||||||
id: 1,
|
id: 1,
|
||||||
},
|
},
|
||||||
createdBy: null,
|
createdBy: newToken.createdBy,
|
||||||
};
|
};
|
||||||
this.tokens.push(token);
|
this.tokens.push(token);
|
||||||
return Promise.resolve(token);
|
return Promise.resolve(token);
|
||||||
@ -80,6 +81,6 @@ export default class FakePublicSignupStore implements IPublicSignupTokenStore {
|
|||||||
|
|
||||||
// eslint-disable-next-line no-unused-vars,@typescript-eslint/no-unused-vars
|
// eslint-disable-next-line no-unused-vars,@typescript-eslint/no-unused-vars
|
||||||
async getAll(query?: Object): Promise<PublicSignupTokenSchema[]> {
|
async getAll(query?: Object): Promise<PublicSignupTokenSchema[]> {
|
||||||
return Promise.resolve([]);
|
return Promise.resolve(this.tokens);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user