mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-08 06:27:40 +00:00
feat: initialize base
This commit is contained in:
@@ -0,0 +1,19 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas/models";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.SuperAdmin, "adminIdentityIds"))) {
|
||||||
|
await knex.schema.alterTable(TableName.SuperAdmin, (t) => {
|
||||||
|
t.specificType("adminIdentityIds", "text[]");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
if (await knex.schema.hasColumn(TableName.SuperAdmin, "adminIdentityIds")) {
|
||||||
|
await knex.schema.alterTable(TableName.SuperAdmin, (t) => {
|
||||||
|
t.dropColumn("adminIdentityIds");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,7 +25,8 @@ export const SuperAdminSchema = z.object({
|
|||||||
encryptedSlackClientId: zodBuffer.nullable().optional(),
|
encryptedSlackClientId: zodBuffer.nullable().optional(),
|
||||||
encryptedSlackClientSecret: zodBuffer.nullable().optional(),
|
encryptedSlackClientSecret: zodBuffer.nullable().optional(),
|
||||||
authConsentContent: z.string().nullable().optional(),
|
authConsentContent: z.string().nullable().optional(),
|
||||||
pageFrameContent: z.string().nullable().optional()
|
pageFrameContent: z.string().nullable().optional(),
|
||||||
|
adminIdentityIds: z.string().array().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TSuperAdmin = z.infer<typeof SuperAdminSchema>;
|
export type TSuperAdmin = z.infer<typeof SuperAdminSchema>;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { getConfig } from "@app/lib/config/env";
|
|||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { ActorType, AuthMethod, AuthMode, AuthModeJwtTokenPayload, AuthTokenType } from "@app/services/auth/auth-type";
|
import { ActorType, AuthMethod, AuthMode, AuthModeJwtTokenPayload, AuthTokenType } from "@app/services/auth/auth-type";
|
||||||
import { TIdentityAccessTokenJwtPayload } from "@app/services/identity-access-token/identity-access-token-types";
|
import { TIdentityAccessTokenJwtPayload } from "@app/services/identity-access-token/identity-access-token-types";
|
||||||
|
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
||||||
|
|
||||||
export type TAuthMode =
|
export type TAuthMode =
|
||||||
| {
|
| {
|
||||||
@@ -43,6 +44,7 @@ export type TAuthMode =
|
|||||||
identityName: string;
|
identityName: string;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
authMethod: null;
|
authMethod: null;
|
||||||
|
isInstanceAdmin?: boolean;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
authMode: AuthMode.SCIM_TOKEN;
|
authMode: AuthMode.SCIM_TOKEN;
|
||||||
@@ -129,13 +131,15 @@ export const injectIdentity = fp(async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
case AuthMode.IDENTITY_ACCESS_TOKEN: {
|
case AuthMode.IDENTITY_ACCESS_TOKEN: {
|
||||||
const identity = await server.services.identityAccessToken.fnValidateIdentityAccessToken(token, req.realIp);
|
const identity = await server.services.identityAccessToken.fnValidateIdentityAccessToken(token, req.realIp);
|
||||||
|
const serverCfg = await getServerCfg();
|
||||||
req.auth = {
|
req.auth = {
|
||||||
authMode: AuthMode.IDENTITY_ACCESS_TOKEN,
|
authMode: AuthMode.IDENTITY_ACCESS_TOKEN,
|
||||||
actor,
|
actor,
|
||||||
orgId: identity.orgId,
|
orgId: identity.orgId,
|
||||||
identityId: identity.identityId,
|
identityId: identity.identityId,
|
||||||
identityName: identity.name,
|
identityName: identity.name,
|
||||||
authMethod: null
|
authMethod: null,
|
||||||
|
isInstanceAdmin: serverCfg?.adminIdentityIds?.includes(identity.identityId)
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
import { FastifyReply, FastifyRequest, HookHandlerDoneFunction } from "fastify";
|
import { FastifyReply, FastifyRequest, HookHandlerDoneFunction } from "fastify";
|
||||||
|
|
||||||
import { ForbiddenRequestError } from "@app/lib/errors";
|
import { ForbiddenRequestError } from "@app/lib/errors";
|
||||||
import { ActorType } from "@app/services/auth/auth-type";
|
import { isSuperAdmin } from "@app/services/super-admin/super-admin-fns";
|
||||||
|
|
||||||
export const verifySuperAdmin = <T extends FastifyRequest>(
|
export const verifySuperAdmin = <T extends FastifyRequest>(
|
||||||
req: T,
|
req: T,
|
||||||
_res: FastifyReply,
|
_res: FastifyReply,
|
||||||
done: HookHandlerDoneFunction
|
done: HookHandlerDoneFunction
|
||||||
) => {
|
) => {
|
||||||
if (req.auth.actor !== ActorType.USER || !req.auth.user.superAdmin)
|
if (isSuperAdmin(req.auth)) {
|
||||||
throw new ForbiddenRequestError({
|
return done();
|
||||||
message: "Requires elevated super admin privileges"
|
}
|
||||||
});
|
|
||||||
done();
|
throw new ForbiddenRequestError({
|
||||||
|
message: "Requires elevated super admin privileges"
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -637,6 +637,9 @@ export const registerRoutes = async (
|
|||||||
userDAL,
|
userDAL,
|
||||||
identityDAL,
|
identityDAL,
|
||||||
userAliasDAL,
|
userAliasDAL,
|
||||||
|
identityTokenAuthDAL,
|
||||||
|
identityAccessTokenDAL,
|
||||||
|
identityOrgMembershipDAL,
|
||||||
authService: loginService,
|
authService: loginService,
|
||||||
serverCfgDAL: superAdminDAL,
|
serverCfgDAL: superAdminDAL,
|
||||||
kmsRootConfigDAL,
|
kmsRootConfigDAL,
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: (req, res, done) => {
|
onRequest: (req, res, done) => {
|
||||||
verifyAuth([AuthMode.JWT, AuthMode.API_KEY])(req, res, () => {
|
verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||||
verifySuperAdmin(req, res, done);
|
verifySuperAdmin(req, res, done);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -139,7 +139,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: (req, res, done) => {
|
onRequest: (req, res, done) => {
|
||||||
verifyAuth([AuthMode.JWT])(req, res, () => {
|
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||||
verifySuperAdmin(req, res, done);
|
verifySuperAdmin(req, res, done);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -171,12 +171,16 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
|||||||
identities: IdentitiesSchema.pick({
|
identities: IdentitiesSchema.pick({
|
||||||
name: true,
|
name: true,
|
||||||
id: true
|
id: true
|
||||||
}).array()
|
})
|
||||||
|
.extend({
|
||||||
|
isInstanceAdmin: z.boolean()
|
||||||
|
})
|
||||||
|
.array()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: (req, res, done) => {
|
onRequest: (req, res, done) => {
|
||||||
verifyAuth([AuthMode.JWT])(req, res, () => {
|
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||||
verifySuperAdmin(req, res, done);
|
verifySuperAdmin(req, res, done);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -206,7 +210,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: (req, res, done) => {
|
onRequest: (req, res, done) => {
|
||||||
verifyAuth([AuthMode.JWT])(req, res, () => {
|
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||||
verifySuperAdmin(req, res, done);
|
verifySuperAdmin(req, res, done);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -240,7 +244,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: (req, res, done) => {
|
onRequest: (req, res, done) => {
|
||||||
verifyAuth([AuthMode.JWT])(req, res, () => {
|
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||||
verifySuperAdmin(req, res, done);
|
verifySuperAdmin(req, res, done);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -265,7 +269,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
onRequest: (req, res, done) => {
|
onRequest: (req, res, done) => {
|
||||||
verifyAuth([AuthMode.JWT])(req, res, () => {
|
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||||
verifySuperAdmin(req, res, done);
|
verifySuperAdmin(req, res, done);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -293,7 +297,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: (req, res, done) => {
|
onRequest: (req, res, done) => {
|
||||||
verifyAuth([AuthMode.JWT])(req, res, () => {
|
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||||
verifySuperAdmin(req, res, done);
|
verifySuperAdmin(req, res, done);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -316,7 +320,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
onRequest: (req, res, done) => {
|
onRequest: (req, res, done) => {
|
||||||
verifyAuth([AuthMode.JWT])(req, res, () => {
|
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||||
verifySuperAdmin(req, res, done);
|
verifySuperAdmin(req, res, done);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -394,4 +398,54 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "POST",
|
||||||
|
url: "/initialize",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
body: z.object({
|
||||||
|
email: z.string().email().trim(),
|
||||||
|
password: z.string().trim(),
|
||||||
|
organizationName: z.string().trim()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
message: z.string(),
|
||||||
|
user: UsersSchema,
|
||||||
|
organization: OrganizationsSchema,
|
||||||
|
machineIdentity: IdentitiesSchema.extend({
|
||||||
|
credentials: z.object({
|
||||||
|
token: z.string()
|
||||||
|
}) // would just be Token AUTH for now
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const { user, organization, machineIdentity } = await server.services.superAdmin.initializeInstance({
|
||||||
|
...req.body
|
||||||
|
});
|
||||||
|
|
||||||
|
await server.services.telemetry.sendPostHogEvents({
|
||||||
|
event: PostHogEventTypes.AdminInit,
|
||||||
|
distinctId: user.user.username ?? "",
|
||||||
|
properties: {
|
||||||
|
username: user.user.username,
|
||||||
|
email: user.user.email ?? "",
|
||||||
|
lastName: user.user.lastName || "",
|
||||||
|
firstName: user.user.firstName || ""
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
message: "Successfully initialized instance",
|
||||||
|
user: user.user,
|
||||||
|
organization,
|
||||||
|
machineIdentity
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export type TUpdateIdentityDTO = {
|
|||||||
role?: string;
|
role?: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
metadata?: { key: string; value: string }[];
|
metadata?: { key: string; value: string }[];
|
||||||
|
isActorSuperAdmin?: boolean;
|
||||||
} & Omit<TOrgPermission, "orgId">;
|
} & Omit<TOrgPermission, "orgId">;
|
||||||
|
|
||||||
export type TDeleteIdentityDTO = {
|
export type TDeleteIdentityDTO = {
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { TAuthMode } from "@app/server/plugins/auth/inject-identity";
|
||||||
|
|
||||||
|
import { ActorType } from "../auth/auth-type";
|
||||||
|
|
||||||
|
export const isSuperAdmin = (auth: TAuthMode) => {
|
||||||
|
if (auth.actor === ActorType.USER && auth.user.superAdmin) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auth.actor === ActorType.IDENTITY && auth.isInstanceAdmin) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
@@ -1,16 +1,21 @@
|
|||||||
import bcrypt from "bcrypt";
|
import bcrypt from "bcrypt";
|
||||||
|
import jwt from "jsonwebtoken";
|
||||||
|
|
||||||
import { TSuperAdmin, TSuperAdminUpdate } from "@app/db/schemas";
|
import { IdentityAuthMethod, OrgMembershipRole, TSuperAdmin, TSuperAdminUpdate } from "@app/db/schemas";
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
import { PgSqlLock, TKeyStoreFactory } from "@app/keystore/keystore";
|
import { PgSqlLock, TKeyStoreFactory } from "@app/keystore/keystore";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
|
import { infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
|
||||||
import { getUserPrivateKey } from "@app/lib/crypto/srp";
|
import { generateUserSrpKeys, getUserPrivateKey } from "@app/lib/crypto/srp";
|
||||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { TIdentityDALFactory } from "@app/services/identity/identity-dal";
|
import { TIdentityDALFactory } from "@app/services/identity/identity-dal";
|
||||||
|
|
||||||
import { TAuthLoginFactory } from "../auth/auth-login-service";
|
import { TAuthLoginFactory } from "../auth/auth-login-service";
|
||||||
import { AuthMethod } from "../auth/auth-type";
|
import { AuthMethod, AuthTokenType } from "../auth/auth-type";
|
||||||
|
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
||||||
|
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||||
|
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||||
|
import { TIdentityTokenAuthDALFactory } from "../identity-token-auth/identity-token-auth-dal";
|
||||||
import { KMS_ROOT_CONFIG_UUID } from "../kms/kms-fns";
|
import { KMS_ROOT_CONFIG_UUID } from "../kms/kms-fns";
|
||||||
import { TKmsRootConfigDALFactory } from "../kms/kms-root-config-dal";
|
import { TKmsRootConfigDALFactory } from "../kms/kms-root-config-dal";
|
||||||
import { TKmsServiceFactory } from "../kms/kms-service";
|
import { TKmsServiceFactory } from "../kms/kms-service";
|
||||||
@@ -20,10 +25,19 @@ import { TUserDALFactory } from "../user/user-dal";
|
|||||||
import { TUserAliasDALFactory } from "../user-alias/user-alias-dal";
|
import { TUserAliasDALFactory } from "../user-alias/user-alias-dal";
|
||||||
import { UserAliasType } from "../user-alias/user-alias-types";
|
import { UserAliasType } from "../user-alias/user-alias-types";
|
||||||
import { TSuperAdminDALFactory } from "./super-admin-dal";
|
import { TSuperAdminDALFactory } from "./super-admin-dal";
|
||||||
import { LoginMethod, TAdminGetIdentitiesDTO, TAdminGetUsersDTO, TAdminSignUpDTO } from "./super-admin-types";
|
import {
|
||||||
|
LoginMethod,
|
||||||
|
TAdminGetIdentitiesDTO,
|
||||||
|
TAdminGetUsersDTO,
|
||||||
|
TAdminInitializeInstanceDTO,
|
||||||
|
TAdminSignUpDTO
|
||||||
|
} from "./super-admin-types";
|
||||||
|
|
||||||
type TSuperAdminServiceFactoryDep = {
|
type TSuperAdminServiceFactoryDep = {
|
||||||
identityDAL: Pick<TIdentityDALFactory, "getIdentitiesByFilter">;
|
identityDAL: TIdentityDALFactory;
|
||||||
|
identityTokenAuthDAL: TIdentityTokenAuthDALFactory;
|
||||||
|
identityAccessTokenDAL: TIdentityAccessTokenDALFactory;
|
||||||
|
identityOrgMembershipDAL: TIdentityOrgDALFactory;
|
||||||
serverCfgDAL: TSuperAdminDALFactory;
|
serverCfgDAL: TSuperAdminDALFactory;
|
||||||
userDAL: TUserDALFactory;
|
userDAL: TUserDALFactory;
|
||||||
userAliasDAL: Pick<TUserAliasDALFactory, "findOne">;
|
userAliasDAL: Pick<TUserAliasDALFactory, "findOne">;
|
||||||
@@ -60,7 +74,10 @@ export const superAdminServiceFactory = ({
|
|||||||
keyStore,
|
keyStore,
|
||||||
kmsRootConfigDAL,
|
kmsRootConfigDAL,
|
||||||
kmsService,
|
kmsService,
|
||||||
licenseService
|
licenseService,
|
||||||
|
identityAccessTokenDAL,
|
||||||
|
identityTokenAuthDAL,
|
||||||
|
identityOrgMembershipDAL
|
||||||
}: TSuperAdminServiceFactoryDep) => {
|
}: TSuperAdminServiceFactoryDep) => {
|
||||||
const initServerCfg = async () => {
|
const initServerCfg = async () => {
|
||||||
// TODO(akhilmhdh): bad pattern time less change this later to me itself
|
// TODO(akhilmhdh): bad pattern time less change this later to me itself
|
||||||
@@ -274,6 +291,137 @@ export const superAdminServiceFactory = ({
|
|||||||
return { token, user: userInfo, organization };
|
return { token, user: userInfo, organization };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const initializeInstance = async ({ email, password, organizationName }: TAdminInitializeInstanceDTO) => {
|
||||||
|
const appCfg = getConfig();
|
||||||
|
const serverCfg = await serverCfgDAL.findById(ADMIN_CONFIG_DB_UUID);
|
||||||
|
if (serverCfg?.initialized) {
|
||||||
|
throw new BadRequestError({ message: "Instance has already been set up" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const existingUser = await userDAL.findOne({ email });
|
||||||
|
if (existingUser) throw new BadRequestError({ name: "Instance initialization", message: "User already exists" });
|
||||||
|
|
||||||
|
const userInfo = await userDAL.transaction(async (tx) => {
|
||||||
|
const newUser = await userDAL.create(
|
||||||
|
{
|
||||||
|
firstName: "Admin",
|
||||||
|
lastName: "User",
|
||||||
|
username: email,
|
||||||
|
email,
|
||||||
|
superAdmin: true,
|
||||||
|
isGhost: false,
|
||||||
|
isAccepted: true,
|
||||||
|
authMethods: [AuthMethod.EMAIL],
|
||||||
|
isEmailVerified: true
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
const { tag, encoding, ciphertext, iv } = infisicalSymmetricEncypt(password);
|
||||||
|
const encKeys = await generateUserSrpKeys(email, password);
|
||||||
|
|
||||||
|
const userEnc = await userDAL.createUserEncryption(
|
||||||
|
{
|
||||||
|
userId: newUser.id,
|
||||||
|
encryptionVersion: 2,
|
||||||
|
protectedKey: encKeys.protectedKey,
|
||||||
|
protectedKeyIV: encKeys.protectedKeyIV,
|
||||||
|
protectedKeyTag: encKeys.protectedKeyTag,
|
||||||
|
publicKey: encKeys.publicKey,
|
||||||
|
encryptedPrivateKey: encKeys.encryptedPrivateKey,
|
||||||
|
iv: encKeys.encryptedPrivateKeyIV,
|
||||||
|
tag: encKeys.encryptedPrivateKeyTag,
|
||||||
|
salt: encKeys.salt,
|
||||||
|
verifier: encKeys.verifier,
|
||||||
|
serverEncryptedPrivateKeyEncoding: encoding,
|
||||||
|
serverEncryptedPrivateKeyTag: tag,
|
||||||
|
serverEncryptedPrivateKeyIV: iv,
|
||||||
|
serverEncryptedPrivateKey: ciphertext
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
|
||||||
|
return { user: newUser, enc: userEnc };
|
||||||
|
});
|
||||||
|
|
||||||
|
const initialOrganizationName = organizationName ?? "Admin Org";
|
||||||
|
|
||||||
|
const organization = await orgService.createOrganization({
|
||||||
|
userId: userInfo.user.id,
|
||||||
|
userEmail: userInfo.user.email,
|
||||||
|
orgName: initialOrganizationName
|
||||||
|
});
|
||||||
|
|
||||||
|
const { identity, credentials } = await identityDAL.transaction(async (tx) => {
|
||||||
|
const newIdentity = await identityDAL.create({ name: "Admin Identity" }, tx);
|
||||||
|
await identityOrgMembershipDAL.create(
|
||||||
|
{
|
||||||
|
identityId: newIdentity.id,
|
||||||
|
orgId: organization.id,
|
||||||
|
role: OrgMembershipRole.Admin
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
|
||||||
|
const tokenAuth = await identityTokenAuthDAL.create(
|
||||||
|
{
|
||||||
|
identityId: newIdentity.id,
|
||||||
|
accessTokenMaxTTL: 0,
|
||||||
|
accessTokenTTL: 0,
|
||||||
|
accessTokenNumUsesLimit: 0,
|
||||||
|
accessTokenTrustedIps: JSON.stringify([
|
||||||
|
{
|
||||||
|
type: "ipv4",
|
||||||
|
prefix: 0,
|
||||||
|
ipAddress: "0.0.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "ipv6",
|
||||||
|
prefix: 0,
|
||||||
|
ipAddress: "::"
|
||||||
|
}
|
||||||
|
])
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
|
||||||
|
const newToken = await identityAccessTokenDAL.create(
|
||||||
|
{
|
||||||
|
identityId: newIdentity.id,
|
||||||
|
isAccessTokenRevoked: false,
|
||||||
|
accessTokenTTL: tokenAuth.accessTokenTTL,
|
||||||
|
accessTokenMaxTTL: tokenAuth.accessTokenMaxTTL,
|
||||||
|
accessTokenNumUses: 0,
|
||||||
|
accessTokenNumUsesLimit: tokenAuth.accessTokenNumUsesLimit,
|
||||||
|
name: "Instance Admin Token",
|
||||||
|
authMethod: IdentityAuthMethod.TOKEN_AUTH
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
|
||||||
|
const generatedAccessToken = jwt.sign(
|
||||||
|
{
|
||||||
|
identityId: newIdentity.id,
|
||||||
|
identityAccessTokenId: newToken.id,
|
||||||
|
authTokenType: AuthTokenType.IDENTITY_ACCESS_TOKEN
|
||||||
|
} as TIdentityAccessTokenJwtPayload,
|
||||||
|
appCfg.AUTH_SECRET
|
||||||
|
);
|
||||||
|
|
||||||
|
return { identity: newIdentity, auth: tokenAuth, credentials: { token: generatedAccessToken } };
|
||||||
|
});
|
||||||
|
|
||||||
|
await updateServerCfg({ initialized: true, adminIdentityIds: [identity.id] }, userInfo.user.id);
|
||||||
|
|
||||||
|
return {
|
||||||
|
user: userInfo,
|
||||||
|
organization,
|
||||||
|
machineIdentity: {
|
||||||
|
...identity,
|
||||||
|
credentials
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const getUsers = ({ offset, limit, searchTerm, adminsOnly }: TAdminGetUsersDTO) => {
|
const getUsers = ({ offset, limit, searchTerm, adminsOnly }: TAdminGetUsersDTO) => {
|
||||||
return userDAL.getUsersByFilter({
|
return userDAL.getUsersByFilter({
|
||||||
limit,
|
limit,
|
||||||
@@ -289,13 +437,19 @@ export const superAdminServiceFactory = ({
|
|||||||
return user;
|
return user;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getIdentities = ({ offset, limit, searchTerm }: TAdminGetIdentitiesDTO) => {
|
const getIdentities = async ({ offset, limit, searchTerm }: TAdminGetIdentitiesDTO) => {
|
||||||
return identityDAL.getIdentitiesByFilter({
|
const identities = await identityDAL.getIdentitiesByFilter({
|
||||||
limit,
|
limit,
|
||||||
offset,
|
offset,
|
||||||
searchTerm,
|
searchTerm,
|
||||||
sortBy: "name"
|
sortBy: "name"
|
||||||
});
|
});
|
||||||
|
const serverCfg = await getServerCfg();
|
||||||
|
|
||||||
|
return identities.map((identity) => ({
|
||||||
|
...identity,
|
||||||
|
isInstanceAdmin: Boolean(serverCfg?.adminIdentityIds?.includes(identity.id))
|
||||||
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const grantServerAdminAccessToUser = async (userId: string) => {
|
const grantServerAdminAccessToUser = async (userId: string) => {
|
||||||
@@ -393,6 +547,7 @@ export const superAdminServiceFactory = ({
|
|||||||
initServerCfg,
|
initServerCfg,
|
||||||
updateServerCfg,
|
updateServerCfg,
|
||||||
adminSignUp,
|
adminSignUp,
|
||||||
|
initializeInstance,
|
||||||
getUsers,
|
getUsers,
|
||||||
deleteUser,
|
deleteUser,
|
||||||
getIdentities,
|
getIdentities,
|
||||||
|
|||||||
@@ -16,6 +16,12 @@ export type TAdminSignUpDTO = {
|
|||||||
userAgent: string;
|
userAgent: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type TAdminInitializeInstanceDTO = {
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
organizationName: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type TAdminGetUsersDTO = {
|
export type TAdminGetUsersDTO = {
|
||||||
offset: number;
|
offset: number;
|
||||||
limit: number;
|
limit: number;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useInfiniteQuery, useQuery, UseQueryOptions } from "@tanstack/react-query";
|
import { useInfiniteQuery, useQuery, UseQueryOptions } from "@tanstack/react-query";
|
||||||
|
|
||||||
import { apiRequest } from "@app/config/request";
|
import { apiRequest } from "@app/config/request";
|
||||||
|
import { Identity } from "@app/hooks/api/identities/types";
|
||||||
|
|
||||||
import { User } from "../types";
|
import { User } from "../types";
|
||||||
import {
|
import {
|
||||||
@@ -10,7 +11,6 @@ import {
|
|||||||
TGetServerRootKmsEncryptionDetails,
|
TGetServerRootKmsEncryptionDetails,
|
||||||
TServerConfig
|
TServerConfig
|
||||||
} from "./types";
|
} from "./types";
|
||||||
import { Identity } from "@app/hooks/api/identities/types";
|
|
||||||
|
|
||||||
export const adminStandaloneKeys = {
|
export const adminStandaloneKeys = {
|
||||||
getUsers: "get-users",
|
getUsers: "get-users",
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export type Identity = {
|
|||||||
authMethods: IdentityAuthMethod[];
|
authMethods: IdentityAuthMethod[];
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
|
isInstanceAdmin?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type IdentityAccessToken = {
|
export type IdentityAccessToken = {
|
||||||
|
|||||||
@@ -28,13 +28,13 @@ import {
|
|||||||
useGetServerRootKmsEncryptionDetails,
|
useGetServerRootKmsEncryptionDetails,
|
||||||
useUpdateServerConfig
|
useUpdateServerConfig
|
||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
|
import { IdentityPanel } from "@app/pages/admin/OverviewPage/components/IdentityPanel";
|
||||||
|
|
||||||
import { AuthPanel } from "./components/AuthPanel";
|
import { AuthPanel } from "./components/AuthPanel";
|
||||||
import { EncryptionPanel } from "./components/EncryptionPanel";
|
import { EncryptionPanel } from "./components/EncryptionPanel";
|
||||||
import { IntegrationPanel } from "./components/IntegrationPanel";
|
import { IntegrationPanel } from "./components/IntegrationPanel";
|
||||||
import { RateLimitPanel } from "./components/RateLimitPanel";
|
import { RateLimitPanel } from "./components/RateLimitPanel";
|
||||||
import { UserPanel } from "./components/UserPanel";
|
import { UserPanel } from "./components/UserPanel";
|
||||||
import { IdentityPanel } from "@app/pages/admin/OverviewPage/components/IdentityPanel";
|
|
||||||
|
|
||||||
enum TabSections {
|
enum TabSections {
|
||||||
Settings = "settings",
|
Settings = "settings",
|
||||||
@@ -59,8 +59,8 @@ const formSchema = z.object({
|
|||||||
trustLdapEmails: z.boolean(),
|
trustLdapEmails: z.boolean(),
|
||||||
trustOidcEmails: z.boolean(),
|
trustOidcEmails: z.boolean(),
|
||||||
defaultAuthOrgId: z.string(),
|
defaultAuthOrgId: z.string(),
|
||||||
authConsentContent: z.string().optional(),
|
authConsentContent: z.string().optional().default(""),
|
||||||
pageFrameContent: z.string().optional()
|
pageFrameContent: z.string().optional().default("")
|
||||||
});
|
});
|
||||||
|
|
||||||
type TDashboardForm = z.infer<typeof formSchema>;
|
type TDashboardForm = z.infer<typeof formSchema>;
|
||||||
@@ -86,8 +86,8 @@ export const OverviewPage = () => {
|
|||||||
trustLdapEmails: config.trustLdapEmails,
|
trustLdapEmails: config.trustLdapEmails,
|
||||||
trustOidcEmails: config.trustOidcEmails,
|
trustOidcEmails: config.trustOidcEmails,
|
||||||
defaultAuthOrgId: config.defaultAuthOrgId ?? "",
|
defaultAuthOrgId: config.defaultAuthOrgId ?? "",
|
||||||
authConsentContent: config.authConsentContent,
|
authConsentContent: config.authConsentContent ?? "",
|
||||||
pageFrameContent: config.pageFrameContent
|
pageFrameContent: config.pageFrameContent ?? ""
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { faMagnifyingGlass, faServer } from "@fortawesome/free-solid-svg-icons";
|
|||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
Badge,
|
||||||
Button,
|
Button,
|
||||||
EmptyState,
|
EmptyState,
|
||||||
Input,
|
Input,
|
||||||
@@ -54,9 +55,16 @@ const IdentityPanelTable = () => {
|
|||||||
{isPending && <TableSkeleton columns={2} innerKey="identities" />}
|
{isPending && <TableSkeleton columns={2} innerKey="identities" />}
|
||||||
{!isPending &&
|
{!isPending &&
|
||||||
data?.pages?.map((identities) =>
|
data?.pages?.map((identities) =>
|
||||||
identities.map(({ name, id }) => (
|
identities.map(({ name, id, isInstanceAdmin }) => (
|
||||||
<Tr key={`identity-${id}`} className="w-full">
|
<Tr key={`identity-${id}`} className="w-full">
|
||||||
<Td>{name}</Td>
|
<Td>
|
||||||
|
{name}
|
||||||
|
{isInstanceAdmin && (
|
||||||
|
<Badge variant="primary" className="ml-2">
|
||||||
|
Server Admin
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
</Td>
|
||||||
</Tr>
|
</Tr>
|
||||||
))
|
))
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user