1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-11 00:08:30 +01:00

chore: expose types so we can use them properly (#5251)

Expose types to be used in enterprise and cloud addons
This commit is contained in:
Gastón Fournier 2023-11-03 12:00:24 +01:00 committed by GitHub
parent ddb9b5c20f
commit 9688955d4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 64 additions and 37 deletions

View File

@ -6,22 +6,24 @@ import { ALL, ApiTokenType } from '../types/models/api-token';
import apiTokenMiddleware, {
TOKEN_TYPE_ERROR_MESSAGE,
} from './api-token-middleware';
import { ApiTokenService } from 'lib/services';
import { IUnleashConfig } from 'lib/types';
let config: any;
let config: IUnleashConfig;
beforeEach(() => {
config = {
config = createTestConfig({
getLogger,
authentication: {
enableApiToken: true,
},
};
});
});
test('should not do anything if request does not contain a authorization', async () => {
const apiTokenService = {
getUserForToken: jest.fn(),
};
} as unknown as ApiTokenService;
const func = apiTokenMiddleware(config, { apiTokenService });
@ -40,7 +42,7 @@ test('should not do anything if request does not contain a authorization', async
test('should not add user if unknown token', async () => {
const apiTokenService = {
getUserForToken: jest.fn(),
};
} as unknown as ApiTokenService;
const func = apiTokenMiddleware(config, { apiTokenService });
@ -61,7 +63,7 @@ test('should not add user if unknown token', async () => {
test('should not make database query when provided PAT format', async () => {
const apiTokenService = {
getUserForToken: jest.fn(),
};
} as unknown as ApiTokenService;
const func = apiTokenMiddleware(config, { apiTokenService });
@ -91,7 +93,7 @@ test('should add user if known token', async () => {
});
const apiTokenService = {
getUserForToken: jest.fn().mockReturnValue(apiUser),
};
} as unknown as ApiTokenService;
const func = apiTokenMiddleware(config, { apiTokenService });
@ -124,7 +126,7 @@ test('should not add user if not /api/client', async () => {
const apiTokenService = {
getUserForToken: jest.fn().mockReturnValue(apiUser),
};
} as unknown as ApiTokenService;
const func = apiTokenMiddleware(config, { apiTokenService });
const cb = jest.fn();
@ -162,7 +164,7 @@ test('should not add user if disabled', async () => {
});
const apiTokenService = {
getUserForToken: jest.fn().mockReturnValue(apiUser),
};
} as unknown as ApiTokenService;
const disabledConfig = createTestConfig({
getLogger,
@ -203,7 +205,7 @@ test('should call next if apiTokenService throws', async () => {
getUserForToken: () => {
throw new Error('hi there, i am stupid');
},
};
} as unknown as ApiTokenService;
const func = apiTokenMiddleware(config, { apiTokenService });
@ -226,7 +228,7 @@ test('should call next if apiTokenService throws x2', async () => {
getUserForToken: () => {
throw new Error('hi there, i am stupid');
},
};
} as unknown as ApiTokenService;
const func = apiTokenMiddleware(config, { apiTokenService });

View File

@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { ApiTokenType } from '../types/models/api-token';
import { IUnleashConfig } from '../types/option';
import { IAuthRequest } from '../routes/unleash-types';
import { IApiRequest, IAuthRequest } from '../routes/unleash-types';
import { IUnleashServices } from 'lib/server-impl';
const isClientApi = ({ path }) => {
return path && path.indexOf('/api/client') > -1;
@ -33,7 +33,7 @@ const apiAccessMiddleware = (
authentication,
flagResolver,
}: Pick<IUnleashConfig, 'getLogger' | 'authentication' | 'flagResolver'>,
{ apiTokenService }: any,
{ apiTokenService }: Pick<IUnleashServices, 'apiTokenService'>,
): any => {
const logger = getLogger('/middleware/api-token.ts');
logger.debug('Enabling api-token middleware');
@ -42,7 +42,7 @@ const apiAccessMiddleware = (
return (req, res, next) => next();
}
return (req: IAuthRequest, res, next) => {
return (req: IAuthRequest | IApiRequest, res, next) => {
if (req.user) {
return next();
}
@ -50,7 +50,9 @@ const apiAccessMiddleware = (
try {
const apiToken = req.header('authorization');
if (!apiToken?.startsWith('user:')) {
const apiUser = apiTokenService.getUserForToken(apiToken);
const apiUser = apiToken
? apiTokenService.getUserForToken(apiToken)
: undefined;
const { CLIENT, FRONTEND } = ApiTokenType;
if (apiUser) {
@ -79,7 +81,6 @@ const apiAccessMiddleware = (
} catch (error) {
logger.warn(error);
}
next();
};
};

View File

@ -2,7 +2,7 @@ import EventEmitter from 'events';
import { RepositoryInterface } from 'unleash-client/lib/repository';
import { Segment } from 'unleash-client/lib/strategy/strategy';
import { FeatureInterface } from 'unleash-client/lib/feature';
import ApiUser from '../types/api-user';
import { IApiUser } from '../types/api-user';
import { IUnleashConfig, IUnleashServices, IUnleashStores } from '../types';
import {
mapFeaturesForClient,
@ -35,7 +35,7 @@ export class ProxyRepository extends EventEmitter implements RepositoryInterface
private readonly configurationRevisionService: ConfigurationRevisionService;
private readonly token: ApiUser;
private readonly token: IApiUser;
private features: FeatureInterface[];
@ -51,7 +51,7 @@ export class ProxyRepository extends EventEmitter implements RepositoryInterface
config: Config,
stores: Stores,
services: Services,
token: ApiUser,
token: IApiUser,
) {
super();
this.config = config;

View File

@ -1,5 +1,6 @@
import { Request } from 'express';
import User from '../types/user';
import IUser from '../types/user';
import { IApiUser } from '../types';
export interface IAuthRequest<
PARAM = any,
@ -7,7 +8,18 @@ export interface IAuthRequest<
ReqBody = any,
ReqQuery = any,
> extends Request<PARAM, ResBody, ReqBody, ReqQuery> {
user: User;
user: IUser;
logout: (() => void) | ((callback: (err?: any) => void) => void);
session: any;
}
export interface IApiRequest<
PARAM = any,
ResBody = any,
ReqBody = any,
ReqQuery = any,
> extends Request<PARAM, ResBody, ReqBody, ReqQuery> {
user: IApiUser;
logout: (() => void) | ((callback: (err?: any) => void) => void);
session: any;
}

View File

@ -3,7 +3,7 @@ import { Logger } from '../logger';
import { ADMIN, CLIENT, FRONTEND } from '../types/permissions';
import { IUnleashStores } from '../types/stores';
import { IUnleashConfig } from '../types/option';
import ApiUser from '../types/api-user';
import ApiUser, { IApiUser } from '../types/api-user';
import {
ApiTokenType,
IApiToken,
@ -121,7 +121,7 @@ export class ApiTokenService {
}
}
public getUserForToken(secret: string): ApiUser | undefined {
public getUserForToken(secret: string): IApiUser | undefined {
if (!secret) {
return undefined;
}
@ -138,7 +138,7 @@ export class ApiTokenService {
token = this.activeTokens.find(
(activeToken) =>
Boolean(activeToken.alias) &&
constantTimeCompare(activeToken.alias, secret),
constantTimeCompare(activeToken.alias!, secret),
);
}

View File

@ -3,7 +3,7 @@ import { ValidationError } from 'joi';
import { CLIENT } from './permissions';
interface IApiUserData {
export interface IApiUserData {
permissions?: string[];
projects?: string[];
project?: string;
@ -13,7 +13,16 @@ interface IApiUserData {
tokenName: string;
}
export default class ApiUser {
export interface IApiUser {
username: string;
permissions: string[];
projects: string[];
environment: string;
type: ApiTokenType;
secret: string;
}
export default class ApiUser implements IApiUser {
readonly isAPI: boolean = true;
readonly permissions: string[];
@ -26,6 +35,8 @@ export default class ApiUser {
readonly secret: string;
readonly username: string;
constructor({
permissions = [CLIENT],
projects,
@ -38,6 +49,7 @@ export default class ApiUser {
if (!tokenName) {
throw new ValidationError('tokenName is required', [], undefined);
}
this.username = tokenName;
this.permissions = permissions;
this.environment = environment;
this.type = type;
@ -45,7 +57,7 @@ export default class ApiUser {
if (projects && projects.length > 0) {
this.projects = projects;
} else {
this.projects = [project];
this.projects = project ? [project] : [];
}
}
}

View File

@ -23,9 +23,9 @@ export interface IUser {
email?: string;
inviteLink?: string;
seenAt?: Date;
createdAt: Date;
createdAt?: Date;
permissions: string[];
loginAttempts: number;
loginAttempts?: number;
isAPI: boolean;
imageUrl: string;
accountType?: AccountType;
@ -50,11 +50,11 @@ export default class User implements IUser {
imageUrl: string;
seenAt: Date;
seenAt?: Date;
loginAttempts: number;
loginAttempts?: number;
createdAt: Date;
createdAt?: Date;
accountType?: AccountType = 'User';
@ -77,9 +77,9 @@ export default class User implements IUser {
Joi.assert(name, Joi.string(), 'Name');
this.id = id;
this.name = name;
this.username = username;
this.email = email;
this.name = name!;
this.username = username!;
this.email = email!;
this.imageUrl = imageUrl || this.generateImageUrl();
this.seenAt = seenAt;
this.loginAttempts = loginAttempts;

View File

@ -29,7 +29,7 @@ afterAll(async () => {
await db.destroy();
});
test('Access to//api/admin/tags are refused no matter how many leading slashes', async () => {
test('Access to //api/admin/tags are refused no matter how many leading slashes', async () => {
await app.request.get('//api/admin/tags').expect(401);
await app.request.get('////api/admin/tags').expect(401);
});