Skip to content

Commit 440ada4

Browse files
Merge pull request #3259 from Infisical/feat/automated-instance-bootstrapping
feat: automated bootstrapping
2 parents 3944e20 + 2079913 commit 440ada4

File tree

49 files changed

+1199
-114
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1199
-114
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Knex } from "knex";
2+
3+
import { TableName } from "../schemas/models";
4+
5+
export async function up(knex: Knex): Promise<void> {
6+
if (!(await knex.schema.hasColumn(TableName.SuperAdmin, "adminIdentityIds"))) {
7+
await knex.schema.alterTable(TableName.SuperAdmin, (t) => {
8+
t.specificType("adminIdentityIds", "text[]");
9+
});
10+
}
11+
}
12+
13+
export async function down(knex: Knex): Promise<void> {
14+
if (await knex.schema.hasColumn(TableName.SuperAdmin, "adminIdentityIds")) {
15+
await knex.schema.alterTable(TableName.SuperAdmin, (t) => {
16+
t.dropColumn("adminIdentityIds");
17+
});
18+
}
19+
}

backend/src/db/schemas/super-admin.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ export const SuperAdminSchema = z.object({
2525
encryptedSlackClientId: zodBuffer.nullable().optional(),
2626
encryptedSlackClientSecret: zodBuffer.nullable().optional(),
2727
authConsentContent: z.string().nullable().optional(),
28-
pageFrameContent: z.string().nullable().optional()
28+
pageFrameContent: z.string().nullable().optional(),
29+
adminIdentityIds: z.string().array().nullable().optional()
2930
});
3031

3132
export type TSuperAdmin = z.infer<typeof SuperAdminSchema>;

backend/src/server/plugins/auth/inject-identity.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { getConfig } from "@app/lib/config/env";
99
import { BadRequestError } from "@app/lib/errors";
1010
import { ActorType, AuthMethod, AuthMode, AuthModeJwtTokenPayload, AuthTokenType } from "@app/services/auth/auth-type";
1111
import { TIdentityAccessTokenJwtPayload } from "@app/services/identity-access-token/identity-access-token-types";
12+
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
1213

1314
export type TAuthMode =
1415
| {
@@ -44,6 +45,7 @@ export type TAuthMode =
4445
identityName: string;
4546
orgId: string;
4647
authMethod: null;
48+
isInstanceAdmin?: boolean;
4749
}
4850
| {
4951
authMode: AuthMode.SCIM_TOKEN;
@@ -130,13 +132,15 @@ export const injectIdentity = fp(async (server: FastifyZodProvider) => {
130132
}
131133
case AuthMode.IDENTITY_ACCESS_TOKEN: {
132134
const identity = await server.services.identityAccessToken.fnValidateIdentityAccessToken(token, req.realIp);
135+
const serverCfg = await getServerCfg();
133136
req.auth = {
134137
authMode: AuthMode.IDENTITY_ACCESS_TOKEN,
135138
actor,
136139
orgId: identity.orgId,
137140
identityId: identity.identityId,
138141
identityName: identity.name,
139-
authMethod: null
142+
authMethod: null,
143+
isInstanceAdmin: serverCfg?.adminIdentityIds?.includes(identity.identityId)
140144
};
141145
if (token?.identityAuth?.oidc) {
142146
requestContext.set("identityAuthInfo", {
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
import { FastifyReply, FastifyRequest, HookHandlerDoneFunction } from "fastify";
22

33
import { ForbiddenRequestError } from "@app/lib/errors";
4-
import { ActorType } from "@app/services/auth/auth-type";
4+
import { isSuperAdmin } from "@app/services/super-admin/super-admin-fns";
55

66
export const verifySuperAdmin = <T extends FastifyRequest>(
77
req: T,
88
_res: FastifyReply,
99
done: HookHandlerDoneFunction
1010
) => {
11-
if (req.auth.actor !== ActorType.USER || !req.auth.user.superAdmin)
12-
throw new ForbiddenRequestError({
13-
message: "Requires elevated super admin privileges"
14-
});
15-
done();
11+
if (isSuperAdmin(req.auth)) {
12+
return done();
13+
}
14+
15+
throw new ForbiddenRequestError({
16+
message: "Requires elevated super admin privileges"
17+
});
1618
};

backend/src/server/routes/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,9 @@ export const registerRoutes = async (
637637
userDAL,
638638
identityDAL,
639639
userAliasDAL,
640+
identityTokenAuthDAL,
641+
identityAccessTokenDAL,
642+
identityOrgMembershipDAL,
640643
authService: loginService,
641644
serverCfgDAL: superAdminDAL,
642645
kmsRootConfigDAL,

backend/src/server/routes/v1/admin-router.ts

+150-9
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
9898
}
9999
},
100100
onRequest: (req, res, done) => {
101-
verifyAuth([AuthMode.JWT, AuthMode.API_KEY])(req, res, () => {
101+
verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
102102
verifySuperAdmin(req, res, done);
103103
});
104104
},
@@ -139,7 +139,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
139139
}
140140
},
141141
onRequest: (req, res, done) => {
142-
verifyAuth([AuthMode.JWT])(req, res, () => {
142+
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
143143
verifySuperAdmin(req, res, done);
144144
});
145145
},
@@ -171,12 +171,16 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
171171
identities: IdentitiesSchema.pick({
172172
name: true,
173173
id: true
174-
}).array()
174+
})
175+
.extend({
176+
isInstanceAdmin: z.boolean()
177+
})
178+
.array()
175179
})
176180
}
177181
},
178182
onRequest: (req, res, done) => {
179-
verifyAuth([AuthMode.JWT])(req, res, () => {
183+
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
180184
verifySuperAdmin(req, res, done);
181185
});
182186
},
@@ -206,7 +210,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
206210
}
207211
},
208212
onRequest: (req, res, done) => {
209-
verifyAuth([AuthMode.JWT])(req, res, () => {
213+
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
210214
verifySuperAdmin(req, res, done);
211215
});
212216
},
@@ -240,7 +244,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
240244
}
241245
},
242246
onRequest: (req, res, done) => {
243-
verifyAuth([AuthMode.JWT])(req, res, () => {
247+
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
244248
verifySuperAdmin(req, res, done);
245249
});
246250
},
@@ -265,7 +269,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
265269
})
266270
},
267271
onRequest: (req, res, done) => {
268-
verifyAuth([AuthMode.JWT])(req, res, () => {
272+
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
269273
verifySuperAdmin(req, res, done);
270274
});
271275
},
@@ -293,7 +297,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
293297
}
294298
},
295299
onRequest: (req, res, done) => {
296-
verifyAuth([AuthMode.JWT])(req, res, () => {
300+
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
297301
verifySuperAdmin(req, res, done);
298302
});
299303
},
@@ -316,7 +320,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
316320
})
317321
},
318322
onRequest: (req, res, done) => {
319-
verifyAuth([AuthMode.JWT])(req, res, () => {
323+
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
320324
verifySuperAdmin(req, res, done);
321325
});
322326
},
@@ -394,4 +398,141 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
394398
};
395399
}
396400
});
401+
402+
server.route({
403+
method: "DELETE",
404+
url: "/identity-management/identities/:identityId/super-admin-access",
405+
config: {
406+
rateLimit: writeLimit
407+
},
408+
schema: {
409+
params: z.object({
410+
identityId: z.string()
411+
}),
412+
response: {
413+
200: z.object({
414+
identity: IdentitiesSchema.pick({
415+
name: true,
416+
id: true
417+
})
418+
})
419+
}
420+
},
421+
onRequest: (req, res, done) => {
422+
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
423+
verifySuperAdmin(req, res, done);
424+
});
425+
},
426+
handler: async (req) => {
427+
const identity = await server.services.superAdmin.deleteIdentitySuperAdminAccess(
428+
req.params.identityId,
429+
req.permission.id
430+
);
431+
432+
return {
433+
identity
434+
};
435+
}
436+
});
437+
438+
server.route({
439+
method: "DELETE",
440+
url: "/user-management/users/:userId/admin-access",
441+
config: {
442+
rateLimit: writeLimit
443+
},
444+
schema: {
445+
params: z.object({
446+
userId: z.string()
447+
}),
448+
response: {
449+
200: z.object({
450+
user: UsersSchema.pick({
451+
username: true,
452+
firstName: true,
453+
lastName: true,
454+
email: true,
455+
id: true
456+
})
457+
})
458+
}
459+
},
460+
onRequest: (req, res, done) => {
461+
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
462+
verifySuperAdmin(req, res, done);
463+
});
464+
},
465+
handler: async (req) => {
466+
const user = await server.services.superAdmin.deleteUserSuperAdminAccess(req.params.userId);
467+
468+
return {
469+
user
470+
};
471+
}
472+
});
473+
474+
server.route({
475+
method: "POST",
476+
url: "/bootstrap",
477+
config: {
478+
rateLimit: writeLimit
479+
},
480+
schema: {
481+
body: z.object({
482+
email: z.string().email().trim().min(1),
483+
password: z.string().trim().min(1),
484+
organization: z.string().trim().min(1)
485+
}),
486+
response: {
487+
200: z.object({
488+
message: z.string(),
489+
user: UsersSchema.pick({
490+
username: true,
491+
firstName: true,
492+
lastName: true,
493+
email: true,
494+
id: true,
495+
superAdmin: true
496+
}),
497+
organization: OrganizationsSchema.pick({
498+
id: true,
499+
name: true,
500+
slug: true
501+
}),
502+
identity: IdentitiesSchema.pick({
503+
id: true,
504+
name: true
505+
}).extend({
506+
credentials: z.object({
507+
token: z.string()
508+
}) // would just be Token AUTH for now
509+
})
510+
})
511+
}
512+
},
513+
handler: async (req) => {
514+
const { user, organization, machineIdentity } = await server.services.superAdmin.bootstrapInstance({
515+
...req.body,
516+
organizationName: req.body.organization
517+
});
518+
519+
await server.services.telemetry.sendPostHogEvents({
520+
event: PostHogEventTypes.AdminInit,
521+
distinctId: user.user.username ?? "",
522+
properties: {
523+
username: user.user.username,
524+
email: user.user.email ?? "",
525+
lastName: user.user.lastName || "",
526+
firstName: user.user.firstName || ""
527+
}
528+
});
529+
530+
return {
531+
message: "Successfully bootstrapped instance",
532+
user: user.user,
533+
organization,
534+
identity: machineIdentity
535+
};
536+
}
537+
});
397538
};

backend/src/server/routes/v1/identity-aws-iam-auth-router.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
validateAccountIds,
1212
validatePrincipalArns
1313
} from "@app/services/identity-aws-auth/identity-aws-auth-validators";
14+
import { isSuperAdmin } from "@app/services/super-admin/super-admin-fns";
1415

1516
export const registerIdentityAwsAuthRouter = async (server: FastifyZodProvider) => {
1617
server.route({
@@ -130,7 +131,8 @@ export const registerIdentityAwsAuthRouter = async (server: FastifyZodProvider)
130131
actorAuthMethod: req.permission.authMethod,
131132
actorOrgId: req.permission.orgId,
132133
...req.body,
133-
identityId: req.params.identityId
134+
identityId: req.params.identityId,
135+
isActorSuperAdmin: isSuperAdmin(req.auth)
134136
});
135137

136138
await server.services.auditLog.createAuditLog({

backend/src/server/routes/v1/identity-azure-auth-router.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
88
import { AuthMode } from "@app/services/auth/auth-type";
99
import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
1010
import { validateAzureAuthField } from "@app/services/identity-azure-auth/identity-azure-auth-validators";
11-
12-
import {} from "../sanitizedSchemas";
11+
import { isSuperAdmin } from "@app/services/super-admin/super-admin-fns";
1312

1413
export const registerIdentityAzureAuthRouter = async (server: FastifyZodProvider) => {
1514
server.route({
@@ -127,7 +126,8 @@ export const registerIdentityAzureAuthRouter = async (server: FastifyZodProvider
127126
actorAuthMethod: req.permission.authMethod,
128127
actorOrgId: req.permission.orgId,
129128
...req.body,
130-
identityId: req.params.identityId
129+
identityId: req.params.identityId,
130+
isActorSuperAdmin: isSuperAdmin(req.auth)
131131
});
132132

133133
await server.services.auditLog.createAuditLog({

backend/src/server/routes/v1/identity-gcp-auth-router.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
88
import { AuthMode } from "@app/services/auth/auth-type";
99
import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
1010
import { validateGcpAuthField } from "@app/services/identity-gcp-auth/identity-gcp-auth-validators";
11+
import { isSuperAdmin } from "@app/services/super-admin/super-admin-fns";
1112

1213
export const registerIdentityGcpAuthRouter = async (server: FastifyZodProvider) => {
1314
server.route({
@@ -121,7 +122,8 @@ export const registerIdentityGcpAuthRouter = async (server: FastifyZodProvider)
121122
actorAuthMethod: req.permission.authMethod,
122123
actorOrgId: req.permission.orgId,
123124
...req.body,
124-
identityId: req.params.identityId
125+
identityId: req.params.identityId,
126+
isActorSuperAdmin: isSuperAdmin(req.auth)
125127
});
126128

127129
await server.services.auditLog.createAuditLog({

backend/src/server/routes/v1/identity-jwt-auth-router.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
validateJwtAuthAudiencesField,
1313
validateJwtBoundClaimsField
1414
} from "@app/services/identity-jwt-auth/identity-jwt-auth-validators";
15+
import { isSuperAdmin } from "@app/services/super-admin/super-admin-fns";
1516

1617
const IdentityJwtAuthResponseSchema = IdentityJwtAuthsSchema.omit({
1718
encryptedJwksCaCert: true,
@@ -169,7 +170,8 @@ export const registerIdentityJwtAuthRouter = async (server: FastifyZodProvider)
169170
actorAuthMethod: req.permission.authMethod,
170171
actorOrgId: req.permission.orgId,
171172
...req.body,
172-
identityId: req.params.identityId
173+
identityId: req.params.identityId,
174+
isActorSuperAdmin: isSuperAdmin(req.auth)
173175
});
174176

175177
await server.services.auditLog.createAuditLog({

0 commit comments

Comments
 (0)