1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

feat: Remove orphaned tokens flags (#7714)

Cleanup of `allowOrphanedWildcardTokens` and `cleanApiTokenWhenOrphaned`
This commit is contained in:
Tymoteusz Czech 2024-08-01 13:31:52 +02:00 committed by GitHub
parent 5668bfb7d4
commit d1e70eefbe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 48 additions and 111 deletions

View File

@ -76,14 +76,12 @@ exports[`should create default config 1`] = `
"flagResolver": FlagResolver {
"experiments": {
"adminTokenKillSwitch": false,
"allowOrphanedWildcardTokens": true,
"anonymiseEventLog": false,
"anonymizeProjectOwners": false,
"automatedActions": false,
"caseInsensitiveInOperators": false,
"celebrateUnleash": false,
"changeRequestPlayground": false,
"cleanApiTokenWhenOrphaned": false,
"collectTrafficDataUsage": false,
"commandBarUI": false,
"demo": false,

View File

@ -36,45 +36,33 @@ interface ITokenRow extends ITokenInsert {
project: string;
}
const createTokenRowReducer =
(allowOrphanedWildcardTokens: boolean) => (acc, tokenRow) => {
const { project, ...token } = tokenRow;
if (!acc[tokenRow.secret]) {
if (
!allowOrphanedWildcardTokens &&
!tokenRow.project &&
tokenRow.secret.includes(':') && // Exclude v1 tokens
!tokenRow.secret.startsWith('*:') && // Exclude intentionally wildcard
(tokenRow.type === ApiTokenType.CLIENT ||
tokenRow.type === ApiTokenType.FRONTEND)
) {
return acc;
}
acc[tokenRow.secret] = {
secret: token.secret,
tokenName: token.token_name ? token.token_name : token.username,
type: token.type.toLowerCase(),
project: ALL,
projects: [ALL],
environment: token.environment ? token.environment : ALL,
expiresAt: token.expires_at,
createdAt: token.created_at,
alias: token.alias,
seenAt: token.seen_at,
username: token.token_name ? token.token_name : token.username,
};
const tokenRowReducer = (acc, tokenRow) => {
const { project, ...token } = tokenRow;
if (!acc[tokenRow.secret]) {
acc[tokenRow.secret] = {
secret: token.secret,
tokenName: token.token_name ? token.token_name : token.username,
type: token.type.toLowerCase(),
project: ALL,
projects: [ALL],
environment: token.environment ? token.environment : ALL,
expiresAt: token.expires_at,
createdAt: token.created_at,
alias: token.alias,
seenAt: token.seen_at,
username: token.token_name ? token.token_name : token.username,
};
}
const currentToken = acc[tokenRow.secret];
if (tokenRow.project) {
if (isAllProjects(currentToken.projects)) {
currentToken.projects = [];
}
const currentToken = acc[tokenRow.secret];
if (tokenRow.project) {
if (isAllProjects(currentToken.projects)) {
currentToken.projects = [];
}
currentToken.projects.push(tokenRow.project);
currentToken.project = currentToken.projects.join(',');
}
return acc;
};
currentToken.projects.push(tokenRow.project);
currentToken.project = currentToken.projects.join(',');
}
return acc;
};
const toRow = (newToken: IApiTokenCreate) => ({
username: newToken.tokenName ?? newToken.username,
@ -87,14 +75,8 @@ const toRow = (newToken: IApiTokenCreate) => ({
alias: newToken.alias || null,
});
const toTokens = (
rows: any[],
allowOrphanedWildcardTokens: boolean,
): IApiToken[] => {
const tokens = rows.reduce(
createTokenRowReducer(allowOrphanedWildcardTokens),
{},
);
const toTokens = (rows: any[]): IApiToken[] => {
const tokens = rows.reduce(tokenRowReducer, {});
return Object.values(tokens);
};
@ -147,10 +129,7 @@ export class ApiTokenStore implements IApiTokenStore {
const stopTimer = this.timer('getAll');
const rows = await this.makeTokenProjectQuery();
stopTimer();
const allowOrphanedWildcardTokens = this.flagResolver.isEnabled(
'allowOrphanedWildcardTokens',
);
return toTokens(rows, allowOrphanedWildcardTokens);
return toTokens(rows);
}
async getAllActive(): Promise<IApiToken[]> {
@ -159,10 +138,7 @@ export class ApiTokenStore implements IApiTokenStore {
.where('expires_at', 'IS', null)
.orWhere('expires_at', '>', 'now()');
stopTimer();
const allowOrphanedWildcardTokens = this.flagResolver.isEnabled(
'allowOrphanedWildcardTokens',
);
return toTokens(rows, allowOrphanedWildcardTokens);
return toTokens(rows);
}
private makeTokenProjectQuery() {
@ -233,10 +209,7 @@ export class ApiTokenStore implements IApiTokenStore {
key,
);
stopTimer();
const allowOrphanedWildcardTokens = this.flagResolver.isEnabled(
'allowOrphanedWildcardTokens',
);
return toTokens(row, allowOrphanedWildcardTokens)[0];
return toTokens(row)[0];
}
async delete(secret: string): Promise<void> {
@ -253,10 +226,7 @@ export class ApiTokenStore implements IApiTokenStore {
.where({ secret })
.returning('*');
if (rows.length > 0) {
const allowOrphanedWildcardTokens = this.flagResolver.isEnabled(
'allowOrphanedWildcardTokens',
);
return toTokens(rows, allowOrphanedWildcardTokens)[0];
return toTokens(rows)[0];
}
throw new NotFoundError('Could not find api-token.');
}

View File

@ -79,14 +79,7 @@ beforeAll(async () => {
email: 'test@example.com',
});
await stores.accessStore.addUserToRole(opsUser.id, 1, '');
const config = createTestConfig({
getLogger,
experimental: {
flags: {
cleanApiTokenWhenOrphaned: true,
},
},
});
const config = createTestConfig({ getLogger });
eventService = new EventService(stores, config);
accessService = createAccessService(db.rawDatabase, config);

View File

@ -564,26 +564,22 @@ export default class ProjectService {
auditUser,
);
if (this.flagResolver.isEnabled('cleanApiTokenWhenOrphaned')) {
const allTokens = await this.apiTokenService.getAllTokens();
const projectTokens = allTokens.filter(
(token) =>
(token.projects &&
token.projects.length === 1 &&
token.projects[0] === id) ||
token.project === id,
);
const allTokens = await this.apiTokenService.getAllTokens();
const projectTokens = allTokens.filter(
(token) =>
(token.projects &&
token.projects.length === 1 &&
token.projects[0] === id) ||
token.project === id,
);
await this.projectStore.delete(id);
await this.projectStore.delete(id);
await Promise.all(
projectTokens.map((token) =>
this.apiTokenService.delete(token.secret, auditUser),
),
);
} else {
await this.projectStore.delete(id);
}
await Promise.all(
projectTokens.map((token) =>
this.apiTokenService.delete(token.secret, auditUser),
),
);
await this.eventService.storeEvent(
new ProjectDeletedEvent({

View File

@ -64,8 +64,6 @@ export type IFlagKey =
| 'anonymizeProjectOwners'
| 'resourceLimits'
| 'extendedMetrics'
| 'cleanApiTokenWhenOrphaned'
| 'allowOrphanedWildcardTokens'
| 'removeUnsafeInlineStyleSrc'
| 'insightsV2'
| 'integrationEvents'
@ -310,18 +308,10 @@ const flags: IFlags = {
process.env.UNLEASH_EXPERIMENTAL_RESOURCE_LIMITS,
false,
),
allowOrphanedWildcardTokens: parseEnvVarBoolean(
process.env.UNLEASH_ORPHANED_TOKENS_KILL_SWITCH,
true,
),
extendedMetrics: parseEnvVarBoolean(
process.env.UNLEASH_EXPERIMENTAL_EXTENDED_METRICS,
false,
),
cleanApiTokenWhenOrphaned: parseEnvVarBoolean(
process.env.UNLEASH_EXPERIMENTAL_CLEAN_API_TOKEN_WHEN_ORPHANED,
false,
),
removeUnsafeInlineStyleSrc: parseEnvVarBoolean(
process.env.UNLEASH_EXPERIMENTAL_REMOVE_UNSAFE_INLINE_STYLE_SRC,
false,

View File

@ -23,17 +23,7 @@ const feature3 = 'f3.p2.token.access';
beforeAll(async () => {
db = await dbInit('feature_api_api_access_client_deletion', getLogger);
app = await setupAppWithAuth(
db.stores,
{
experimental: {
flags: {
cleanApiTokenWhenOrphaned: true,
},
},
},
db.rawDatabase,
);
app = await setupAppWithAuth(db.stores, {}, db.rawDatabase);
apiTokenService = app.services.apiTokenService;
const { featureToggleServiceV2, environmentService } = app.services;