mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-26 13:48:33 +02: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:
parent
ddb9b5c20f
commit
9688955d4b
@ -6,22 +6,24 @@ import { ALL, ApiTokenType } from '../types/models/api-token';
|
|||||||
import apiTokenMiddleware, {
|
import apiTokenMiddleware, {
|
||||||
TOKEN_TYPE_ERROR_MESSAGE,
|
TOKEN_TYPE_ERROR_MESSAGE,
|
||||||
} from './api-token-middleware';
|
} from './api-token-middleware';
|
||||||
|
import { ApiTokenService } from 'lib/services';
|
||||||
|
import { IUnleashConfig } from 'lib/types';
|
||||||
|
|
||||||
let config: any;
|
let config: IUnleashConfig;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
config = {
|
config = createTestConfig({
|
||||||
getLogger,
|
getLogger,
|
||||||
authentication: {
|
authentication: {
|
||||||
enableApiToken: true,
|
enableApiToken: true,
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should not do anything if request does not contain a authorization', async () => {
|
test('should not do anything if request does not contain a authorization', async () => {
|
||||||
const apiTokenService = {
|
const apiTokenService = {
|
||||||
getUserForToken: jest.fn(),
|
getUserForToken: jest.fn(),
|
||||||
};
|
} as unknown as ApiTokenService;
|
||||||
|
|
||||||
const func = apiTokenMiddleware(config, { 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 () => {
|
test('should not add user if unknown token', async () => {
|
||||||
const apiTokenService = {
|
const apiTokenService = {
|
||||||
getUserForToken: jest.fn(),
|
getUserForToken: jest.fn(),
|
||||||
};
|
} as unknown as ApiTokenService;
|
||||||
|
|
||||||
const func = apiTokenMiddleware(config, { 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 () => {
|
test('should not make database query when provided PAT format', async () => {
|
||||||
const apiTokenService = {
|
const apiTokenService = {
|
||||||
getUserForToken: jest.fn(),
|
getUserForToken: jest.fn(),
|
||||||
};
|
} as unknown as ApiTokenService;
|
||||||
|
|
||||||
const func = apiTokenMiddleware(config, { apiTokenService });
|
const func = apiTokenMiddleware(config, { apiTokenService });
|
||||||
|
|
||||||
@ -91,7 +93,7 @@ test('should add user if known token', async () => {
|
|||||||
});
|
});
|
||||||
const apiTokenService = {
|
const apiTokenService = {
|
||||||
getUserForToken: jest.fn().mockReturnValue(apiUser),
|
getUserForToken: jest.fn().mockReturnValue(apiUser),
|
||||||
};
|
} as unknown as ApiTokenService;
|
||||||
|
|
||||||
const func = apiTokenMiddleware(config, { apiTokenService });
|
const func = apiTokenMiddleware(config, { apiTokenService });
|
||||||
|
|
||||||
@ -124,7 +126,7 @@ test('should not add user if not /api/client', async () => {
|
|||||||
|
|
||||||
const apiTokenService = {
|
const apiTokenService = {
|
||||||
getUserForToken: jest.fn().mockReturnValue(apiUser),
|
getUserForToken: jest.fn().mockReturnValue(apiUser),
|
||||||
};
|
} as unknown as ApiTokenService;
|
||||||
|
|
||||||
const func = apiTokenMiddleware(config, { apiTokenService });
|
const func = apiTokenMiddleware(config, { apiTokenService });
|
||||||
const cb = jest.fn();
|
const cb = jest.fn();
|
||||||
@ -162,7 +164,7 @@ test('should not add user if disabled', async () => {
|
|||||||
});
|
});
|
||||||
const apiTokenService = {
|
const apiTokenService = {
|
||||||
getUserForToken: jest.fn().mockReturnValue(apiUser),
|
getUserForToken: jest.fn().mockReturnValue(apiUser),
|
||||||
};
|
} as unknown as ApiTokenService;
|
||||||
|
|
||||||
const disabledConfig = createTestConfig({
|
const disabledConfig = createTestConfig({
|
||||||
getLogger,
|
getLogger,
|
||||||
@ -203,7 +205,7 @@ test('should call next if apiTokenService throws', async () => {
|
|||||||
getUserForToken: () => {
|
getUserForToken: () => {
|
||||||
throw new Error('hi there, i am stupid');
|
throw new Error('hi there, i am stupid');
|
||||||
},
|
},
|
||||||
};
|
} as unknown as ApiTokenService;
|
||||||
|
|
||||||
const func = apiTokenMiddleware(config, { apiTokenService });
|
const func = apiTokenMiddleware(config, { apiTokenService });
|
||||||
|
|
||||||
@ -226,7 +228,7 @@ test('should call next if apiTokenService throws x2', async () => {
|
|||||||
getUserForToken: () => {
|
getUserForToken: () => {
|
||||||
throw new Error('hi there, i am stupid');
|
throw new Error('hi there, i am stupid');
|
||||||
},
|
},
|
||||||
};
|
} as unknown as ApiTokenService;
|
||||||
|
|
||||||
const func = apiTokenMiddleware(config, { apiTokenService });
|
const func = apiTokenMiddleware(config, { apiTokenService });
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
|
||||||
import { ApiTokenType } from '../types/models/api-token';
|
import { ApiTokenType } from '../types/models/api-token';
|
||||||
import { IUnleashConfig } from '../types/option';
|
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 }) => {
|
const isClientApi = ({ path }) => {
|
||||||
return path && path.indexOf('/api/client') > -1;
|
return path && path.indexOf('/api/client') > -1;
|
||||||
@ -33,7 +33,7 @@ const apiAccessMiddleware = (
|
|||||||
authentication,
|
authentication,
|
||||||
flagResolver,
|
flagResolver,
|
||||||
}: Pick<IUnleashConfig, 'getLogger' | 'authentication' | 'flagResolver'>,
|
}: Pick<IUnleashConfig, 'getLogger' | 'authentication' | 'flagResolver'>,
|
||||||
{ apiTokenService }: any,
|
{ apiTokenService }: Pick<IUnleashServices, 'apiTokenService'>,
|
||||||
): any => {
|
): any => {
|
||||||
const logger = getLogger('/middleware/api-token.ts');
|
const logger = getLogger('/middleware/api-token.ts');
|
||||||
logger.debug('Enabling api-token middleware');
|
logger.debug('Enabling api-token middleware');
|
||||||
@ -42,7 +42,7 @@ const apiAccessMiddleware = (
|
|||||||
return (req, res, next) => next();
|
return (req, res, next) => next();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (req: IAuthRequest, res, next) => {
|
return (req: IAuthRequest | IApiRequest, res, next) => {
|
||||||
if (req.user) {
|
if (req.user) {
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
@ -50,7 +50,9 @@ const apiAccessMiddleware = (
|
|||||||
try {
|
try {
|
||||||
const apiToken = req.header('authorization');
|
const apiToken = req.header('authorization');
|
||||||
if (!apiToken?.startsWith('user:')) {
|
if (!apiToken?.startsWith('user:')) {
|
||||||
const apiUser = apiTokenService.getUserForToken(apiToken);
|
const apiUser = apiToken
|
||||||
|
? apiTokenService.getUserForToken(apiToken)
|
||||||
|
: undefined;
|
||||||
const { CLIENT, FRONTEND } = ApiTokenType;
|
const { CLIENT, FRONTEND } = ApiTokenType;
|
||||||
|
|
||||||
if (apiUser) {
|
if (apiUser) {
|
||||||
@ -79,7 +81,6 @@ const apiAccessMiddleware = (
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.warn(error);
|
logger.warn(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
next();
|
next();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -2,7 +2,7 @@ import EventEmitter from 'events';
|
|||||||
import { RepositoryInterface } from 'unleash-client/lib/repository';
|
import { RepositoryInterface } from 'unleash-client/lib/repository';
|
||||||
import { Segment } from 'unleash-client/lib/strategy/strategy';
|
import { Segment } from 'unleash-client/lib/strategy/strategy';
|
||||||
import { FeatureInterface } from 'unleash-client/lib/feature';
|
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 { IUnleashConfig, IUnleashServices, IUnleashStores } from '../types';
|
||||||
import {
|
import {
|
||||||
mapFeaturesForClient,
|
mapFeaturesForClient,
|
||||||
@ -35,7 +35,7 @@ export class ProxyRepository extends EventEmitter implements RepositoryInterface
|
|||||||
|
|
||||||
private readonly configurationRevisionService: ConfigurationRevisionService;
|
private readonly configurationRevisionService: ConfigurationRevisionService;
|
||||||
|
|
||||||
private readonly token: ApiUser;
|
private readonly token: IApiUser;
|
||||||
|
|
||||||
private features: FeatureInterface[];
|
private features: FeatureInterface[];
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ export class ProxyRepository extends EventEmitter implements RepositoryInterface
|
|||||||
config: Config,
|
config: Config,
|
||||||
stores: Stores,
|
stores: Stores,
|
||||||
services: Services,
|
services: Services,
|
||||||
token: ApiUser,
|
token: IApiUser,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.config = config;
|
this.config = config;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Request } from 'express';
|
import { Request } from 'express';
|
||||||
import User from '../types/user';
|
import IUser from '../types/user';
|
||||||
|
import { IApiUser } from '../types';
|
||||||
|
|
||||||
export interface IAuthRequest<
|
export interface IAuthRequest<
|
||||||
PARAM = any,
|
PARAM = any,
|
||||||
@ -7,7 +8,18 @@ export interface IAuthRequest<
|
|||||||
ReqBody = any,
|
ReqBody = any,
|
||||||
ReqQuery = any,
|
ReqQuery = any,
|
||||||
> extends Request<PARAM, ResBody, ReqBody, ReqQuery> {
|
> 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);
|
logout: (() => void) | ((callback: (err?: any) => void) => void);
|
||||||
session: any;
|
session: any;
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ import { Logger } from '../logger';
|
|||||||
import { ADMIN, CLIENT, FRONTEND } from '../types/permissions';
|
import { ADMIN, CLIENT, FRONTEND } from '../types/permissions';
|
||||||
import { IUnleashStores } from '../types/stores';
|
import { IUnleashStores } from '../types/stores';
|
||||||
import { IUnleashConfig } from '../types/option';
|
import { IUnleashConfig } from '../types/option';
|
||||||
import ApiUser from '../types/api-user';
|
import ApiUser, { IApiUser } from '../types/api-user';
|
||||||
import {
|
import {
|
||||||
ApiTokenType,
|
ApiTokenType,
|
||||||
IApiToken,
|
IApiToken,
|
||||||
@ -121,7 +121,7 @@ export class ApiTokenService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public getUserForToken(secret: string): ApiUser | undefined {
|
public getUserForToken(secret: string): IApiUser | undefined {
|
||||||
if (!secret) {
|
if (!secret) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -138,7 +138,7 @@ export class ApiTokenService {
|
|||||||
token = this.activeTokens.find(
|
token = this.activeTokens.find(
|
||||||
(activeToken) =>
|
(activeToken) =>
|
||||||
Boolean(activeToken.alias) &&
|
Boolean(activeToken.alias) &&
|
||||||
constantTimeCompare(activeToken.alias, secret),
|
constantTimeCompare(activeToken.alias!, secret),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ import { ValidationError } from 'joi';
|
|||||||
|
|
||||||
import { CLIENT } from './permissions';
|
import { CLIENT } from './permissions';
|
||||||
|
|
||||||
interface IApiUserData {
|
export interface IApiUserData {
|
||||||
permissions?: string[];
|
permissions?: string[];
|
||||||
projects?: string[];
|
projects?: string[];
|
||||||
project?: string;
|
project?: string;
|
||||||
@ -13,7 +13,16 @@ interface IApiUserData {
|
|||||||
tokenName: string;
|
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 isAPI: boolean = true;
|
||||||
|
|
||||||
readonly permissions: string[];
|
readonly permissions: string[];
|
||||||
@ -26,6 +35,8 @@ export default class ApiUser {
|
|||||||
|
|
||||||
readonly secret: string;
|
readonly secret: string;
|
||||||
|
|
||||||
|
readonly username: string;
|
||||||
|
|
||||||
constructor({
|
constructor({
|
||||||
permissions = [CLIENT],
|
permissions = [CLIENT],
|
||||||
projects,
|
projects,
|
||||||
@ -38,6 +49,7 @@ export default class ApiUser {
|
|||||||
if (!tokenName) {
|
if (!tokenName) {
|
||||||
throw new ValidationError('tokenName is required', [], undefined);
|
throw new ValidationError('tokenName is required', [], undefined);
|
||||||
}
|
}
|
||||||
|
this.username = tokenName;
|
||||||
this.permissions = permissions;
|
this.permissions = permissions;
|
||||||
this.environment = environment;
|
this.environment = environment;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
@ -45,7 +57,7 @@ export default class ApiUser {
|
|||||||
if (projects && projects.length > 0) {
|
if (projects && projects.length > 0) {
|
||||||
this.projects = projects;
|
this.projects = projects;
|
||||||
} else {
|
} else {
|
||||||
this.projects = [project];
|
this.projects = project ? [project] : [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,9 @@ export interface IUser {
|
|||||||
email?: string;
|
email?: string;
|
||||||
inviteLink?: string;
|
inviteLink?: string;
|
||||||
seenAt?: Date;
|
seenAt?: Date;
|
||||||
createdAt: Date;
|
createdAt?: Date;
|
||||||
permissions: string[];
|
permissions: string[];
|
||||||
loginAttempts: number;
|
loginAttempts?: number;
|
||||||
isAPI: boolean;
|
isAPI: boolean;
|
||||||
imageUrl: string;
|
imageUrl: string;
|
||||||
accountType?: AccountType;
|
accountType?: AccountType;
|
||||||
@ -50,11 +50,11 @@ export default class User implements IUser {
|
|||||||
|
|
||||||
imageUrl: string;
|
imageUrl: string;
|
||||||
|
|
||||||
seenAt: Date;
|
seenAt?: Date;
|
||||||
|
|
||||||
loginAttempts: number;
|
loginAttempts?: number;
|
||||||
|
|
||||||
createdAt: Date;
|
createdAt?: Date;
|
||||||
|
|
||||||
accountType?: AccountType = 'User';
|
accountType?: AccountType = 'User';
|
||||||
|
|
||||||
@ -77,9 +77,9 @@ export default class User implements IUser {
|
|||||||
Joi.assert(name, Joi.string(), 'Name');
|
Joi.assert(name, Joi.string(), 'Name');
|
||||||
|
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.name = name;
|
this.name = name!;
|
||||||
this.username = username;
|
this.username = username!;
|
||||||
this.email = email;
|
this.email = email!;
|
||||||
this.imageUrl = imageUrl || this.generateImageUrl();
|
this.imageUrl = imageUrl || this.generateImageUrl();
|
||||||
this.seenAt = seenAt;
|
this.seenAt = seenAt;
|
||||||
this.loginAttempts = loginAttempts;
|
this.loginAttempts = loginAttempts;
|
||||||
|
@ -29,7 +29,7 @@ afterAll(async () => {
|
|||||||
await db.destroy();
|
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);
|
||||||
await app.request.get('////api/admin/tags').expect(401);
|
await app.request.get('////api/admin/tags').expect(401);
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user