mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	fix: Allow AuthType None to use valid API tokens (#6247)
Fixes ##5799 and #5785 When you do not provide a token we should resolve to the "default" environment to maintain backward compatibility. If you actually provide a token we should prefer that and even block the request if it is not valid. An interesting fact is that "default" environment is not available on a fresh installation of Unleash. This means that you need to provide a token to actually get access to toggle configurations. --------- Co-authored-by: Thomas Heartman <thomas@getunleash.io>
This commit is contained in:
		
							parent
							
								
									e5fe4a7766
								
							
						
					
					
						commit
						4a81f0932f
					
				@ -17,7 +17,7 @@ import IndexRouter from './routes';
 | 
				
			|||||||
import requestLogger from './middleware/request-logger';
 | 
					import requestLogger from './middleware/request-logger';
 | 
				
			||||||
import demoAuthentication from './middleware/demo-authentication';
 | 
					import demoAuthentication from './middleware/demo-authentication';
 | 
				
			||||||
import ossAuthentication from './middleware/oss-authentication';
 | 
					import ossAuthentication from './middleware/oss-authentication';
 | 
				
			||||||
import noAuthentication from './middleware/no-authentication';
 | 
					import noAuthentication, { noApiToken } from './middleware/no-authentication';
 | 
				
			||||||
import secureHeaders from './middleware/secure-headers';
 | 
					import secureHeaders from './middleware/secure-headers';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { loadIndexHTML } from './util/load-index-html';
 | 
					import { loadIndexHTML } from './util/load-index-html';
 | 
				
			||||||
@ -41,6 +41,7 @@ export default async function getApp(
 | 
				
			|||||||
    const baseUriPath = config.server.baseUriPath || '';
 | 
					    const baseUriPath = config.server.baseUriPath || '';
 | 
				
			||||||
    const publicFolder = config.publicFolder || findPublicFolder();
 | 
					    const publicFolder = config.publicFolder || findPublicFolder();
 | 
				
			||||||
    const indexHTML = await loadIndexHTML(config, publicFolder);
 | 
					    const indexHTML = await loadIndexHTML(config, publicFolder);
 | 
				
			||||||
 | 
					    const logger = config.getLogger('lib/app.ts');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    app.set('trust proxy', true);
 | 
					    app.set('trust proxy', true);
 | 
				
			||||||
    app.disable('x-powered-by');
 | 
					    app.disable('x-powered-by');
 | 
				
			||||||
@ -147,6 +148,11 @@ export default async function getApp(
 | 
				
			|||||||
            break;
 | 
					            break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        case IAuthType.NONE: {
 | 
					        case IAuthType.NONE: {
 | 
				
			||||||
 | 
					            logger.warn(
 | 
				
			||||||
 | 
					                'The AuthType=none option for Unleash is no longer recommended and will be removed in version 6.',
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            noApiToken(baseUriPath, app);
 | 
				
			||||||
 | 
					            app.use(baseUriPath, apiTokenMiddleware(config, services));
 | 
				
			||||||
            noAuthentication(baseUriPath, app);
 | 
					            noAuthentication(baseUriPath, app);
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,15 +1,53 @@
 | 
				
			|||||||
import { Application } from 'express';
 | 
					import { Application } from 'express';
 | 
				
			||||||
import NoAuthUser from '../types/no-auth-user';
 | 
					import NoAuthUser from '../types/no-auth-user';
 | 
				
			||||||
 | 
					import { ApiTokenType } from '../types/models/api-token';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					    ApiUser,
 | 
				
			||||||
 | 
					    IApiRequest,
 | 
				
			||||||
 | 
					    IAuthRequest,
 | 
				
			||||||
 | 
					    permissions,
 | 
				
			||||||
 | 
					} from '../server-impl';
 | 
				
			||||||
 | 
					import { DEFAULT_ENV } from '../util';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// eslint-disable-next-line
 | 
					// eslint-disable-next-line
 | 
				
			||||||
function noneAuthentication(basePath: string, app: Application): void {
 | 
					function noneAuthentication(baseUriPath: string, app: Application): void {
 | 
				
			||||||
    app.use(`${basePath || ''}/api/admin/`, (req, res, next) => {
 | 
					    app.use(
 | 
				
			||||||
        // @ts-expect-error
 | 
					        `${baseUriPath || ''}/api/admin/`,
 | 
				
			||||||
 | 
					        (req: IAuthRequest, res, next) => {
 | 
				
			||||||
            if (!req.user) {
 | 
					            if (!req.user) {
 | 
				
			||||||
            // @ts-expect-error
 | 
					 | 
				
			||||||
                req.user = new NoAuthUser();
 | 
					                req.user = new NoAuthUser();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            next();
 | 
					            next();
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function noApiToken(baseUriPath: string, app: Application) {
 | 
				
			||||||
 | 
					    app.use(`${baseUriPath}/api/frontend`, (req: IApiRequest, res, next) => {
 | 
				
			||||||
 | 
					        if (!req.headers.authorization && !req.user) {
 | 
				
			||||||
 | 
					            req.user = new ApiUser({
 | 
				
			||||||
 | 
					                tokenName: 'unknown',
 | 
				
			||||||
 | 
					                permissions: [permissions.FRONTEND],
 | 
				
			||||||
 | 
					                projects: ['*'],
 | 
				
			||||||
 | 
					                environment: DEFAULT_ENV,
 | 
				
			||||||
 | 
					                type: ApiTokenType.FRONTEND,
 | 
				
			||||||
 | 
					                secret: 'unknown',
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        next();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    app.use(`${baseUriPath}/api/client`, (req: IApiRequest, res, next) => {
 | 
				
			||||||
 | 
					        if (!req.headers.authorization && !req.user) {
 | 
				
			||||||
 | 
					            req.user = new ApiUser({
 | 
				
			||||||
 | 
					                tokenName: 'unknown',
 | 
				
			||||||
 | 
					                permissions: [permissions.CLIENT],
 | 
				
			||||||
 | 
					                projects: ['*'],
 | 
				
			||||||
 | 
					                environment: DEFAULT_ENV,
 | 
				
			||||||
 | 
					                type: ApiTokenType.CLIENT,
 | 
				
			||||||
 | 
					                secret: 'unknown',
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        next();
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
export default noneAuthentication;
 | 
					export default noneAuthentication;
 | 
				
			||||||
 | 
				
			|||||||
@ -11,7 +11,6 @@ export const userSchema = {
 | 
				
			|||||||
        id: {
 | 
					        id: {
 | 
				
			||||||
            description: 'The user id',
 | 
					            description: 'The user id',
 | 
				
			||||||
            type: 'integer',
 | 
					            type: 'integer',
 | 
				
			||||||
            minimum: 0,
 | 
					 | 
				
			||||||
            example: 123,
 | 
					            example: 123,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        isAPI: {
 | 
					        isAPI: {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
import { ADMIN } from './permissions';
 | 
					import { ADMIN } from './permissions';
 | 
				
			||||||
export default class NoAuthUser {
 | 
					import { IUser } from './user';
 | 
				
			||||||
 | 
					export default class NoAuthUser implements IUser {
 | 
				
			||||||
    isAPI: boolean;
 | 
					    isAPI: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    username: string;
 | 
					    username: string;
 | 
				
			||||||
@ -8,6 +9,15 @@ export default class NoAuthUser {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    permissions: string[];
 | 
					    permissions: string[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    name: string;
 | 
				
			||||||
 | 
					    email: string;
 | 
				
			||||||
 | 
					    inviteLink?: string | undefined;
 | 
				
			||||||
 | 
					    seenAt?: Date | undefined;
 | 
				
			||||||
 | 
					    createdAt?: Date | undefined;
 | 
				
			||||||
 | 
					    loginAttempts?: number | undefined;
 | 
				
			||||||
 | 
					    imageUrl: string;
 | 
				
			||||||
 | 
					    accountType?: 'User' | 'Service Account' | undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(
 | 
					    constructor(
 | 
				
			||||||
        username: string = 'unknown',
 | 
					        username: string = 'unknown',
 | 
				
			||||||
        id: number = -1,
 | 
					        id: number = -1,
 | 
				
			||||||
@ -15,6 +25,7 @@ export default class NoAuthUser {
 | 
				
			|||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
        this.isAPI = true;
 | 
					        this.isAPI = true;
 | 
				
			||||||
        this.username = username;
 | 
					        this.username = username;
 | 
				
			||||||
 | 
					        this.name = 'unknown';
 | 
				
			||||||
        this.id = id;
 | 
					        this.id = id;
 | 
				
			||||||
        this.permissions = permissions;
 | 
					        this.permissions = permissions;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										135
									
								
								src/test/e2e/api/client/feature.auth-none.e2e.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								src/test/e2e/api/client/feature.auth-none.e2e.test.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,135 @@
 | 
				
			|||||||
 | 
					import {
 | 
				
			||||||
 | 
					    IUnleashTest,
 | 
				
			||||||
 | 
					    setupAppWithCustomConfig,
 | 
				
			||||||
 | 
					} from '../../helpers/test-helper';
 | 
				
			||||||
 | 
					import dbInit, { ITestDb } from '../../helpers/database-init';
 | 
				
			||||||
 | 
					import getLogger from '../../../fixtures/no-logger';
 | 
				
			||||||
 | 
					import { DEFAULT_ENV } from '../../../../lib/util/constants';
 | 
				
			||||||
 | 
					import User from '../../../../lib/types/user';
 | 
				
			||||||
 | 
					import { ApiTokenType } from '../../../../lib/types/models/api-token';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let app: IUnleashTest;
 | 
				
			||||||
 | 
					let db: ITestDb;
 | 
				
			||||||
 | 
					const testUser = { name: 'test', id: -9999 } as User;
 | 
				
			||||||
 | 
					let clientSecret: string;
 | 
				
			||||||
 | 
					let frontendSecret: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					beforeAll(async () => {
 | 
				
			||||||
 | 
					    db = await dbInit('feature_api_client_auth_none', getLogger);
 | 
				
			||||||
 | 
					    app = await setupAppWithCustomConfig(
 | 
				
			||||||
 | 
					        db.stores,
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            authentication: {
 | 
				
			||||||
 | 
					                type: 'none',
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            experimental: {
 | 
				
			||||||
 | 
					                flags: {
 | 
				
			||||||
 | 
					                    strictSchemaValidation: true,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        db.rawDatabase,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    await app.services.featureToggleService.createFeatureToggle(
 | 
				
			||||||
 | 
					        'default',
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            name: 'feature_1',
 | 
				
			||||||
 | 
					            description: 'the #1 feature',
 | 
				
			||||||
 | 
					            impressionData: true,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'test',
 | 
				
			||||||
 | 
					        testUser.id,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    await app.services.featureToggleService.createFeatureToggle(
 | 
				
			||||||
 | 
					        'default',
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            name: 'feature_2',
 | 
				
			||||||
 | 
					            description: 'soon to be the #1 feature',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'test',
 | 
				
			||||||
 | 
					        testUser.id,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await app.services.featureToggleService.createFeatureToggle(
 | 
				
			||||||
 | 
					        'default',
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            name: 'feature_3',
 | 
				
			||||||
 | 
					            description: 'terrible feature',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'test',
 | 
				
			||||||
 | 
					        testUser.id,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const token = await app.services.apiTokenService.createApiTokenWithProjects(
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            tokenName: 'test',
 | 
				
			||||||
 | 
					            type: ApiTokenType.CLIENT,
 | 
				
			||||||
 | 
					            environment: DEFAULT_ENV,
 | 
				
			||||||
 | 
					            projects: ['default'],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    clientSecret = token.secret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const frontendToken =
 | 
				
			||||||
 | 
					        await app.services.apiTokenService.createApiTokenWithProjects({
 | 
				
			||||||
 | 
					            tokenName: 'test',
 | 
				
			||||||
 | 
					            type: ApiTokenType.FRONTEND,
 | 
				
			||||||
 | 
					            environment: DEFAULT_ENV,
 | 
				
			||||||
 | 
					            projects: ['default'],
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    frontendSecret = frontendToken.secret;
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					afterAll(async () => {
 | 
				
			||||||
 | 
					    await app.destroy();
 | 
				
			||||||
 | 
					    await db.destroy();
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test('returns three feature toggles', async () => {
 | 
				
			||||||
 | 
					    return app.request
 | 
				
			||||||
 | 
					        .get('/api/client/features')
 | 
				
			||||||
 | 
					        .expect('Content-Type', /json/)
 | 
				
			||||||
 | 
					        .expect(200)
 | 
				
			||||||
 | 
					        .expect((res) => {
 | 
				
			||||||
 | 
					            expect(res.body.features).toHaveLength(3);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test('returns 401 for incorrect api token', async () => {
 | 
				
			||||||
 | 
					    return app.request
 | 
				
			||||||
 | 
					        .get('/api/client/features')
 | 
				
			||||||
 | 
					        .set('Authorization', 'some-invalid-token')
 | 
				
			||||||
 | 
					        .expect('Content-Type', /json/)
 | 
				
			||||||
 | 
					        .expect(401);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test('returns success for correct api token', async () => {
 | 
				
			||||||
 | 
					    return app.request
 | 
				
			||||||
 | 
					        .get('/api/client/features')
 | 
				
			||||||
 | 
					        .set('Authorization', clientSecret)
 | 
				
			||||||
 | 
					        .expect('Content-Type', /json/)
 | 
				
			||||||
 | 
					        .expect(200);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test('returns successful for frontend API without token', async () => {
 | 
				
			||||||
 | 
					    return app.request
 | 
				
			||||||
 | 
					        .get('/api/frontend')
 | 
				
			||||||
 | 
					        .expect('Content-Type', /json/)
 | 
				
			||||||
 | 
					        .expect(200);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test('returns 401 for frontend API with invalid token', async () => {
 | 
				
			||||||
 | 
					    return app.request
 | 
				
			||||||
 | 
					        .get('/api/frontend')
 | 
				
			||||||
 | 
					        .expect('Content-Type', /json/)
 | 
				
			||||||
 | 
					        .set('Authorization', 'some-invalid-token')
 | 
				
			||||||
 | 
					        .expect(401);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test('returns 200 for frontend API with valid token', async () => {
 | 
				
			||||||
 | 
					    return app.request
 | 
				
			||||||
 | 
					        .get('/api/frontend')
 | 
				
			||||||
 | 
					        .expect('Content-Type', /json/)
 | 
				
			||||||
 | 
					        .set('Authorization', frontendSecret)
 | 
				
			||||||
 | 
					        .expect(200);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
@ -21,7 +21,7 @@ beforeAll(async () => {
 | 
				
			|||||||
            },
 | 
					            },
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    await app.services.featureToggleServiceV2.createFeatureToggle(
 | 
					    await app.services.featureToggleService.createFeatureToggle(
 | 
				
			||||||
        'default',
 | 
					        'default',
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            name: 'featureX',
 | 
					            name: 'featureX',
 | 
				
			||||||
@ -31,7 +31,7 @@ beforeAll(async () => {
 | 
				
			|||||||
        'test',
 | 
					        'test',
 | 
				
			||||||
        testUser.id,
 | 
					        testUser.id,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    await app.services.featureToggleServiceV2.createFeatureToggle(
 | 
					    await app.services.featureToggleService.createFeatureToggle(
 | 
				
			||||||
        'default',
 | 
					        'default',
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            name: 'featureY',
 | 
					            name: 'featureY',
 | 
				
			||||||
@ -40,7 +40,7 @@ beforeAll(async () => {
 | 
				
			|||||||
        'test',
 | 
					        'test',
 | 
				
			||||||
        testUser.id,
 | 
					        testUser.id,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    await app.services.featureToggleServiceV2.createFeatureToggle(
 | 
					    await app.services.featureToggleService.createFeatureToggle(
 | 
				
			||||||
        'default',
 | 
					        'default',
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            name: 'featureZ',
 | 
					            name: 'featureZ',
 | 
				
			||||||
@ -49,7 +49,7 @@ beforeAll(async () => {
 | 
				
			|||||||
        'test',
 | 
					        'test',
 | 
				
			||||||
        testUser.id,
 | 
					        testUser.id,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    await app.services.featureToggleServiceV2.createFeatureToggle(
 | 
					    await app.services.featureToggleService.createFeatureToggle(
 | 
				
			||||||
        'default',
 | 
					        'default',
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            name: 'featureArchivedX',
 | 
					            name: 'featureArchivedX',
 | 
				
			||||||
@ -59,12 +59,12 @@ beforeAll(async () => {
 | 
				
			|||||||
        testUser.id,
 | 
					        testUser.id,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    await app.services.featureToggleServiceV2.archiveToggle(
 | 
					    await app.services.featureToggleService.archiveToggle(
 | 
				
			||||||
        'featureArchivedX',
 | 
					        'featureArchivedX',
 | 
				
			||||||
        testUser,
 | 
					        testUser,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    await app.services.featureToggleServiceV2.createFeatureToggle(
 | 
					    await app.services.featureToggleService.createFeatureToggle(
 | 
				
			||||||
        'default',
 | 
					        'default',
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            name: 'featureArchivedY',
 | 
					            name: 'featureArchivedY',
 | 
				
			||||||
@ -74,11 +74,11 @@ beforeAll(async () => {
 | 
				
			|||||||
        testUser.id,
 | 
					        testUser.id,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    await app.services.featureToggleServiceV2.archiveToggle(
 | 
					    await app.services.featureToggleService.archiveToggle(
 | 
				
			||||||
        'featureArchivedY',
 | 
					        'featureArchivedY',
 | 
				
			||||||
        testUser,
 | 
					        testUser,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    await app.services.featureToggleServiceV2.createFeatureToggle(
 | 
					    await app.services.featureToggleService.createFeatureToggle(
 | 
				
			||||||
        'default',
 | 
					        'default',
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            name: 'featureArchivedZ',
 | 
					            name: 'featureArchivedZ',
 | 
				
			||||||
@ -87,11 +87,11 @@ beforeAll(async () => {
 | 
				
			|||||||
        'test',
 | 
					        'test',
 | 
				
			||||||
        testUser.id,
 | 
					        testUser.id,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    await app.services.featureToggleServiceV2.archiveToggle(
 | 
					    await app.services.featureToggleService.archiveToggle(
 | 
				
			||||||
        'featureArchivedZ',
 | 
					        'featureArchivedZ',
 | 
				
			||||||
        testUser,
 | 
					        testUser,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    await app.services.featureToggleServiceV2.createFeatureToggle(
 | 
					    await app.services.featureToggleService.createFeatureToggle(
 | 
				
			||||||
        'default',
 | 
					        'default',
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            name: 'feature.with.variants',
 | 
					            name: 'feature.with.variants',
 | 
				
			||||||
@ -100,7 +100,7 @@ beforeAll(async () => {
 | 
				
			|||||||
        'test',
 | 
					        'test',
 | 
				
			||||||
        testUser.id,
 | 
					        testUser.id,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    await app.services.featureToggleServiceV2.saveVariants(
 | 
					    await app.services.featureToggleService.saveVariants(
 | 
				
			||||||
        'feature.with.variants',
 | 
					        'feature.with.variants',
 | 
				
			||||||
        'default',
 | 
					        'default',
 | 
				
			||||||
        [
 | 
					        [
 | 
				
			||||||
@ -132,19 +132,19 @@ test('returns calculated hash', async () => {
 | 
				
			|||||||
        .get('/api/client/features')
 | 
					        .get('/api/client/features')
 | 
				
			||||||
        .expect('Content-Type', /json/)
 | 
					        .expect('Content-Type', /json/)
 | 
				
			||||||
        .expect(200);
 | 
					        .expect(200);
 | 
				
			||||||
    expect(res.headers.etag).toBe('"ae443048:16"');
 | 
					    expect(res.headers.etag).toBe('"61824cd0:16"');
 | 
				
			||||||
    expect(res.body.meta.etag).toBe('"ae443048:16"');
 | 
					    expect(res.body.meta.etag).toBe('"61824cd0:16"');
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
test('returns 304 for pre-calculated hash', async () => {
 | 
					test('returns 304 for pre-calculated hash', async () => {
 | 
				
			||||||
    return app.request
 | 
					    return app.request
 | 
				
			||||||
        .get('/api/client/features')
 | 
					        .get('/api/client/features')
 | 
				
			||||||
        .set('if-none-match', '"ae443048:16"')
 | 
					        .set('if-none-match', '"61824cd0:16"')
 | 
				
			||||||
        .expect(304);
 | 
					        .expect(304);
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
test('returns 200 when content updates and hash does not match anymore', async () => {
 | 
					test('returns 200 when content updates and hash does not match anymore', async () => {
 | 
				
			||||||
    await app.services.featureToggleServiceV2.createFeatureToggle(
 | 
					    await app.services.featureToggleService.createFeatureToggle(
 | 
				
			||||||
        'default',
 | 
					        'default',
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            name: 'featureNew304',
 | 
					            name: 'featureNew304',
 | 
				
			||||||
@ -160,6 +160,6 @@ test('returns 200 when content updates and hash does not match anymore', async (
 | 
				
			|||||||
        .set('if-none-match', 'ae443048:16')
 | 
					        .set('if-none-match', 'ae443048:16')
 | 
				
			||||||
        .expect(200);
 | 
					        .expect(200);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    expect(res.headers.etag).toBe('"ae443048:17"');
 | 
					    expect(res.headers.etag).toBe('"61824cd0:17"');
 | 
				
			||||||
    expect(res.body.meta.etag).toBe('"ae443048:17"');
 | 
					    expect(res.body.meta.etag).toBe('"61824cd0:17"');
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
				
			|||||||
@ -63,8 +63,8 @@ unleash.start(unleashOptions);
 | 
				
			|||||||
  - `type` / `AUTH_TYPE`: `string` —  What kind of authentication to use. Possible values
 | 
					  - `type` / `AUTH_TYPE`: `string` —  What kind of authentication to use. Possible values
 | 
				
			||||||
    - `open-source` - Sign in with username and password. This is the default value.
 | 
					    - `open-source` - Sign in with username and password. This is the default value.
 | 
				
			||||||
    - `custom` - If implementing your own authentication hook, use this
 | 
					    - `custom` - If implementing your own authentication hook, use this
 | 
				
			||||||
    - `none` - Turn off authentication all together
 | 
					 | 
				
			||||||
    - `demo` - Only requires an email to sign in (was default in v3)
 | 
					    - `demo` - Only requires an email to sign in (was default in v3)
 | 
				
			||||||
 | 
					    - `none` - _Deprecated_ Turn off authentication completely. If no API token is provided towards /`api/client` or `/api/frontend` you will receive configuration for the "default" environment. We generally recommend you use the `demo` type for simple, insecure usage of Unleash. This auth type has many known limitations, particularly related to personalized capabilities such as favorites and [notifications](../../reference/notifications.md). 
 | 
				
			||||||
  - `customAuthHandler`: function `(app: any, config: IUnleashConfig): void` — custom express middleware handling authentication. Used when type is set to `custom`. Can not be set via environment variables.
 | 
					  - `customAuthHandler`: function `(app: any, config: IUnleashConfig): void` — custom express middleware handling authentication. Used when type is set to `custom`. Can not be set via environment variables.
 | 
				
			||||||
  - `initialAdminUser`: `{ username: string, password: string} | null` — whether to create an admin user with default password - Defaults to using `admin` and `unleash4all` as the username and password. Can not be overridden by setting the `UNLEASH_DEFAULT_ADMIN_USERNAME` and `UNLEASH_DEFAULT_ADMIN_PASSWORD` environment variables.
 | 
					  - `initialAdminUser`: `{ username: string, password: string} | null` — whether to create an admin user with default password - Defaults to using `admin` and `unleash4all` as the username and password. Can not be overridden by setting the `UNLEASH_DEFAULT_ADMIN_USERNAME` and `UNLEASH_DEFAULT_ADMIN_PASSWORD` environment variables.
 | 
				
			||||||
    - `initApiTokens` / `INIT_ADMIN_API_TOKENS`, `INIT_CLIENT_API_TOKENS`, and `INIT_FRONTEND_API_TOKENS`: `ApiTokens[]` — Array of API tokens to create on startup. The tokens will only be created if the database doesn't already contain any API tokens. Example:
 | 
					    - `initApiTokens` / `INIT_ADMIN_API_TOKENS`, `INIT_CLIENT_API_TOKENS`, and `INIT_FRONTEND_API_TOKENS`: `ApiTokens[]` — Array of API tokens to create on startup. The tokens will only be created if the database doesn't already contain any API tokens. Example:
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user