Add lockout-preventative step in saml config setup, add update org slug section in org settings, revise navigate to org flow to account for org-level auth enforced orgs

This commit is contained in:
Tuan Dang
2024-02-06 15:51:24 -08:00
parent c1aa5c840c
commit fc7015de83
29 changed files with 409 additions and 125 deletions
+21 -19
View File
@@ -56,13 +56,7 @@ export const authLoginServiceFactory = ({ userDAL, tokenService, smtpService }:
* Private
* Send mfa code via email
* */
const sendUserMfaCode = async ({
userId,
email
}: {
userId: string;
email: string;
}) => {
const sendUserMfaCode = async ({ userId, email }: { userId: string; email: string }) => {
const code = await tokenService.createTokenForUser({
type: TokenType.TOKEN_EMAIL_MFA,
userId
@@ -171,6 +165,10 @@ export const authLoginServiceFactory = ({ userDAL, tokenService, smtpService }:
if (!userEnc.authMethods?.includes(AuthMethod.EMAIL)) {
const { orgId } = validateProviderAuthToken(providerAuthToken as string, email);
organizationId = orgId;
} else if (providerAuthToken) {
// SAML SSO
const { orgId } = validateProviderAuthToken(providerAuthToken, email);
organizationId = orgId;
}
if (!userEnc.serverPrivateKey || !userEnc.clientPublicKey) throw new Error("Failed to authenticate. Try again?");
@@ -189,22 +187,26 @@ export const authLoginServiceFactory = ({ userDAL, tokenService, smtpService }:
});
// send multi factor auth token if they it enabled
if (userEnc.isMfaEnabled) {
const mfaToken = jwt.sign({
authTokenType: AuthTokenType.MFA_TOKEN,
userId: userEnc.userId,
organizationId
}, cfg.AUTH_SECRET, {
expiresIn: cfg.JWT_MFA_LIFETIME
});
const mfaToken = jwt.sign(
{
authTokenType: AuthTokenType.MFA_TOKEN,
userId: userEnc.userId,
organizationId
},
cfg.AUTH_SECRET,
{
expiresIn: cfg.JWT_MFA_LIFETIME
}
);
await sendUserMfaCode({
userId: userEnc.userId,
userId: userEnc.userId,
email: userEnc.email
});
return { isMfaEnabled: true, token: mfaToken } as const;
}
const token = await generateUserTokens({
user: {
...userEnc,
@@ -214,7 +216,7 @@ export const authLoginServiceFactory = ({ userDAL, tokenService, smtpService }:
userAgent,
organizationId
});
return { token, isMfaEnabled: false, user: userEnc } as const;
};
@@ -227,7 +229,7 @@ export const authLoginServiceFactory = ({ userDAL, tokenService, smtpService }:
if (!user) return;
await sendUserMfaCode({
userId: user.id,
email: user.email,
email: user.email
});
};
@@ -127,6 +127,7 @@ export const identityUaServiceFactory = ({
expiresIn: identityAccessToken.accessTokenMaxTTL === 0 ? undefined : identityAccessToken.accessTokenMaxTTL
}
);
return { accessToken, identityUa, validClientSecretInfo, identityAccessToken };
};
@@ -152,7 +153,12 @@ export const identityUaServiceFactory = ({
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
}
const { permission } = await permissionService.getOrgPermission(actor, actorId, identityMembershipOrg.orgId, actorOrgScope);
const { permission } = await permissionService.getOrgPermission(
actor,
actorId,
identityMembershipOrg.orgId,
actorOrgScope
);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Identity);
const plan = await licenseService.getPlan(identityMembershipOrg.orgId);
@@ -241,7 +247,12 @@ export const identityUaServiceFactory = ({
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
}
const { permission } = await permissionService.getOrgPermission(actor, actorId, identityMembershipOrg.orgId, actorOrgScope);
const { permission } = await permissionService.getOrgPermission(
actor,
actorId,
identityMembershipOrg.orgId,
actorOrgScope
);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Identity);
const plan = await licenseService.getPlan(identityMembershipOrg.orgId);
@@ -302,7 +313,12 @@ export const identityUaServiceFactory = ({
const uaIdentityAuth = await identityUaDAL.findOne({ identityId });
const { permission } = await permissionService.getOrgPermission(actor, actorId, identityMembershipOrg.orgId, actorOrgScope);
const { permission } = await permissionService.getOrgPermission(
actor,
actorId,
identityMembershipOrg.orgId,
actorOrgScope
);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Identity);
return { ...uaIdentityAuth, orgId: identityMembershipOrg.orgId };
};
@@ -322,7 +338,12 @@ export const identityUaServiceFactory = ({
throw new BadRequestError({
message: "The identity does not have universal auth"
});
const { permission } = await permissionService.getOrgPermission(actor, actorId, identityMembershipOrg.orgId, actorOrgScope);
const { permission } = await permissionService.getOrgPermission(
actor,
actorId,
identityMembershipOrg.orgId,
actorOrgScope
);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Identity);
const { permission: rolePermission } = await permissionService.getOrgPermission(
@@ -369,7 +390,12 @@ export const identityUaServiceFactory = ({
throw new BadRequestError({
message: "The identity does not have universal auth"
});
const { permission } = await permissionService.getOrgPermission(actor, actorId, identityMembershipOrg.orgId, actorOrgScope);
const { permission } = await permissionService.getOrgPermission(
actor,
actorId,
identityMembershipOrg.orgId,
actorOrgScope
);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Identity);
const { permission: rolePermission } = await permissionService.getOrgPermission(
@@ -395,14 +421,25 @@ export const identityUaServiceFactory = ({
return { clientSecrets, orgId: identityMembershipOrg.orgId };
};
const revokeUaClientSecret = async ({ identityId, actorId, actor, actorOrgScope, clientSecretId }: TRevokeUaClientSecretDTO) => {
const revokeUaClientSecret = async ({
identityId,
actorId,
actor,
actorOrgScope,
clientSecretId
}: TRevokeUaClientSecretDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new BadRequestError({ message: "Failed to find identity" });
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.Univeral)
throw new BadRequestError({
message: "The identity does not have universal auth"
});
const { permission } = await permissionService.getOrgPermission(actor, actorId, identityMembershipOrg.orgId, actorOrgScope);
const { permission } = await permissionService.getOrgPermission(
actor,
actorId,
identityMembershipOrg.orgId,
actorOrgScope
);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Identity);
const { permission: rolePermission } = await permissionService.getOrgPermission(
+17 -6
View File
@@ -29,6 +29,7 @@ import {
TDeleteOrgMembershipDTO,
TFindAllWorkspacesDTO,
TInviteUserToOrgDTO,
TUpdateOrgDTO,
TUpdateOrgMembershipDTO,
TVerifyUserToOrgDTO
} from "./org-types";
@@ -40,7 +41,7 @@ type TOrgServiceFactoryDep = {
userDAL: TUserDALFactory;
projectDAL: TProjectDALFactory;
incidentContactDAL: TIncidentContactsDALFactory;
samlConfigDAL: Pick<TSamlConfigDALFactory, "findOne">;
samlConfigDAL: Pick<TSamlConfigDALFactory, "findOne" | "findEnforceableSamlCfg">;
smtpService: TSmtpService;
tokenService: TAuthTokenServiceFactory;
permissionService: TPermissionServiceFactory;
@@ -118,12 +119,22 @@ export const orgServiceFactory = ({
};
/*
* Update organization settings
* Update organization details
* */
const updateOrgName = async (userId: string, orgId: string, name: string, actorOrgScope?: string) => {
const { permission } = await permissionService.getUserOrgPermission(userId, orgId, actorOrgScope);
const updateOrg = async ({ actor, actorId, actorOrgScope, orgId, data }: TUpdateOrgDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgScope);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Settings);
const org = await orgDAL.updateById(orgId, { name });
if (data.authEnforced) {
const samlCfg = await samlConfigDAL.findEnforceableSamlCfg(orgId);
if (!samlCfg)
throw new BadRequestError({
name: "No enforceable SAML config found",
message: "No enforceable SAML config found"
});
}
const org = await orgDAL.updateById(orgId, data);
if (!org) throw new BadRequestError({ name: "Org not found", message: "Organization not found" });
return org;
};
@@ -443,7 +454,7 @@ export const orgServiceFactory = ({
findAllOrganizationOfUser,
inviteUserToOrganization,
verifyUserToOrg,
updateOrgName,
updateOrg,
createOrganization,
deleteOrganizationById,
deleteOrgMembership,
+6
View File
@@ -1,3 +1,5 @@
import { TOrgPermission } from "@app/lib/types";
import { ActorType } from "../auth/auth-type";
export type TUpdateOrgMembershipDTO = {
@@ -34,3 +36,7 @@ export type TFindAllWorkspacesDTO = {
actorOrgScope?: string;
orgId: string;
};
export type TUpdateOrgDTO = {
data: Partial<{ name: string; slug: string; authEnforced: boolean }>;
} & TOrgPermission;