Continue user aliases

This commit is contained in:
Tuan Dang
2024-04-26 09:02:10 -07:00
parent 71e309bbcb
commit d42f620e1b
6 changed files with 91 additions and 29 deletions
+2
View File
@@ -99,6 +99,7 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
// eslint-disable-next-line // eslint-disable-next-line
async (req, profile, cb) => { async (req, profile, cb) => {
try { try {
console.log("saml login profile: ", profile);
if (!profile) throw new BadRequestError({ message: "Missing profile" }); if (!profile) throw new BadRequestError({ message: "Missing profile" });
const email = profile?.email ?? (profile?.emailAddress as string); // emailRippling is added because in Rippling the field `email` reserved const email = profile?.email ?? (profile?.emailAddress as string); // emailRippling is added because in Rippling the field `email` reserved
@@ -107,6 +108,7 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
} }
const { isUserCompleted, providerAuthToken } = await server.services.saml.samlLogin({ const { isUserCompleted, providerAuthToken } = await server.services.saml.samlLogin({
externalId: profile.nameID,
username: profile.nameID ?? email, username: profile.nameID ?? email,
email, email,
firstName: profile.firstName as string, firstName: profile.firstName as string,
@@ -1,7 +1,13 @@
import { ForbiddenError } from "@casl/ability"; import { ForbiddenError } from "@casl/ability";
import jwt from "jsonwebtoken"; import jwt from "jsonwebtoken";
import { OrgMembershipRole, OrgMembershipStatus, SecretKeyEncoding, TLdapConfigsUpdate } from "@app/db/schemas"; import {
OrgMembershipRole,
OrgMembershipStatus,
SecretKeyEncoding,
TableName,
TLdapConfigsUpdate
} from "@app/db/schemas";
import { TGroupDALFactory } from "@app/ee/services/group/group-dal"; import { TGroupDALFactory } from "@app/ee/services/group/group-dal";
import { addUsersToGroupByUserIds, removeUsersFromGroupByUserIds } from "@app/ee/services/group/group-fns"; import { addUsersToGroupByUserIds, removeUsersFromGroupByUserIds } from "@app/ee/services/group/group-fns";
import { TUserGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal"; import { TUserGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal";
@@ -25,6 +31,7 @@ import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal
import { TUserDALFactory } from "@app/services/user/user-dal"; import { TUserDALFactory } from "@app/services/user/user-dal";
import { normalizeUsername } from "@app/services/user/user-fns"; import { normalizeUsername } from "@app/services/user/user-fns";
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal"; import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
import { UserAliasType } from "@app/services/user-alias/user-alias-types";
import { TLicenseServiceFactory } from "../license/license-service"; import { TLicenseServiceFactory } from "../license/license-service";
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission"; import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
@@ -388,7 +395,7 @@ export const ldapConfigServiceFactory = ({
let userAlias = await userAliasDAL.findOne({ let userAlias = await userAliasDAL.findOne({
externalId, externalId,
orgId, orgId,
aliasType: AuthMethod.LDAP aliasType: UserAliasType.LDAP
}); });
const organization = await orgDAL.findOrgById(orgId); const organization = await orgDAL.findOrgById(orgId);
@@ -396,7 +403,13 @@ export const ldapConfigServiceFactory = ({
if (userAlias) { if (userAlias) {
await userDAL.transaction(async (tx) => { await userDAL.transaction(async (tx) => {
const [orgMembership] = await orgDAL.findMembership({ userId: userAlias.userId }, { tx }); const [orgMembership] = await orgDAL.findMembership(
{
userId: userAlias.userId,
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
},
{ tx }
);
if (!orgMembership) { if (!orgMembership) {
await orgDAL.createMembership( await orgDAL.createMembership(
{ {
@@ -426,7 +439,7 @@ export const ldapConfigServiceFactory = ({
email: emails[0], email: emails[0],
firstName, firstName,
lastName, lastName,
authMethods: [AuthMethod.LDAP], authMethods: [AuthMethod.LDAP], // should this be empty?
isGhost: false isGhost: false
}, },
tx tx
@@ -435,7 +448,7 @@ export const ldapConfigServiceFactory = ({
{ {
userId: newUser.id, userId: newUser.id,
username, username,
aliasType: AuthMethod.LDAP, aliasType: UserAliasType.LDAP,
externalId, externalId,
emails, emails,
orgId orgId
@@ -7,7 +7,8 @@ import {
SecretKeyEncoding, SecretKeyEncoding,
TableName, TableName,
TSamlConfigs, TSamlConfigs,
TSamlConfigsUpdate TSamlConfigsUpdate,
TUsers
} from "@app/db/schemas"; } from "@app/db/schemas";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { import {
@@ -19,11 +20,13 @@ import {
infisicalSymmetricEncypt infisicalSymmetricEncypt
} from "@app/lib/crypto/encryption"; } from "@app/lib/crypto/encryption";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
import { AuthMethod, AuthTokenType } from "@app/services/auth/auth-type"; import { AuthTokenType } from "@app/services/auth/auth-type";
import { TOrgBotDALFactory } from "@app/services/org/org-bot-dal"; import { TOrgBotDALFactory } from "@app/services/org/org-bot-dal";
import { TOrgDALFactory } from "@app/services/org/org-dal"; import { TOrgDALFactory } from "@app/services/org/org-dal";
import { TUserDALFactory } from "@app/services/user/user-dal"; import { TUserDALFactory } from "@app/services/user/user-dal";
import { normalizeUsername } from "@app/services/user/user-fns";
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal"; import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
import { UserAliasType } from "@app/services/user-alias/user-alias-types";
import { TLicenseServiceFactory } from "../license/license-service"; import { TLicenseServiceFactory } from "../license/license-service";
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission"; import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
@@ -33,7 +36,7 @@ import { TCreateSamlCfgDTO, TGetSamlCfgDTO, TSamlLoginDTO, TUpdateSamlCfgDTO } f
type TSamlConfigServiceFactoryDep = { type TSamlConfigServiceFactoryDep = {
samlConfigDAL: TSamlConfigDALFactory; samlConfigDAL: TSamlConfigDALFactory;
userDAL: Pick<TUserDALFactory, "create" | "findOne" | "transaction" | "updateById">; userDAL: Pick<TUserDALFactory, "create" | "findOne" | "transaction" | "updateById" | "findById">;
userAliasDAL: Pick<TUserAliasDALFactory, "create" | "findOne">; userAliasDAL: Pick<TUserAliasDALFactory, "create" | "findOne">;
orgDAL: Pick< orgDAL: Pick<
TOrgDALFactory, TOrgDALFactory,
@@ -51,6 +54,7 @@ export const samlConfigServiceFactory = ({
orgBotDAL, orgBotDAL,
orgDAL, orgDAL,
userDAL, userDAL,
userAliasDAL,
permissionService, permissionService,
licenseService licenseService
}: TSamlConfigServiceFactoryDep) => { }: TSamlConfigServiceFactoryDep) => {
@@ -307,7 +311,8 @@ export const samlConfigServiceFactory = ({
}; };
const samlLogin = async ({ const samlLogin = async ({
username, externalId,
username, // what to do about this?
email, email,
firstName, firstName,
lastName, lastName,
@@ -315,22 +320,36 @@ export const samlConfigServiceFactory = ({
orgId, orgId,
relayState relayState
}: TSamlLoginDTO) => { }: TSamlLoginDTO) => {
console.log("samlLogin args: ", {
externalId,
username,
email,
firstName,
lastName,
authProvider,
orgId,
relayState
});
const appCfg = getConfig(); const appCfg = getConfig();
let user = await userDAL.findOne({ username }); const userAlias = await userAliasDAL.findOne({
externalId,
orgId,
aliasType: UserAliasType.SAML
});
console.log("found userAlias: ", userAlias);
const organization = await orgDAL.findOrgById(orgId); const organization = await orgDAL.findOrgById(orgId);
if (!organization) throw new BadRequestError({ message: "Org not found" }); if (!organization) throw new BadRequestError({ message: "Org not found" });
// TODO(dangtony98): remove this after aliases update let user: TUsers;
if (authProvider === AuthMethod.KEYCLOAK_SAML && appCfg.LICENSE_SERVER_KEY) { if (userAlias) {
throw new BadRequestError({ message: "Keycloak SAML is not yet available on Infisical Cloud" }); console.log("samlLogin A");
} user = await userDAL.transaction(async (tx) => {
const foundUser = await userDAL.findById(userAlias.userId, tx);
if (user) {
await userDAL.transaction(async (tx) => {
const [orgMembership] = await orgDAL.findMembership( const [orgMembership] = await orgDAL.findMembership(
{ {
userId: user.id, userId: foundUser.id,
[`${TableName.OrgMembership}.orgId` as "id"]: orgId [`${TableName.OrgMembership}.orgId` as "id"]: orgId
}, },
{ tx } { tx }
@@ -338,11 +357,10 @@ export const samlConfigServiceFactory = ({
if (!orgMembership) { if (!orgMembership) {
await orgDAL.createMembership( await orgDAL.createMembership(
{ {
userId: user.id, userId: userAlias.userId,
orgId, orgId,
inviteEmail: email,
role: OrgMembershipRole.Member, role: OrgMembershipRole.Member,
status: user.isAccepted ? OrgMembershipStatus.Accepted : OrgMembershipStatus.Invited // if user is fully completed, then set status to accepted, otherwise set it to invited so we can update it later status: foundUser.isAccepted ? OrgMembershipStatus.Accepted : OrgMembershipStatus.Invited // if user is fully completed, then set status to accepted, otherwise set it to invited so we can update it later
}, },
tx tx
); );
@@ -356,30 +374,53 @@ export const samlConfigServiceFactory = ({
tx tx
); );
} }
return foundUser;
}); });
} else { } else {
console.log("samlLogin B");
user = await userDAL.transaction(async (tx) => { user = await userDAL.transaction(async (tx) => {
const uniqueUsername = await normalizeUsername(username, userDAL);
const newUser = await userDAL.create( const newUser = await userDAL.create(
{ {
username, username: uniqueUsername,
email, email,
isEmailVerified: false, isEmailVerified: false,
firstName, firstName,
lastName, lastName,
authMethods: [AuthMethod.EMAIL], authMethods: [],
isGhost: false isGhost: false
}, },
tx tx
); );
await orgDAL.createMembership({ await userAliasDAL.create(
inviteEmail: email, {
orgId, userId: newUser.id,
role: OrgMembershipRole.Member, username,
status: OrgMembershipStatus.Invited aliasType: UserAliasType.SAML,
}); externalId,
emails: email ? [email] : [],
orgId
},
tx
);
await orgDAL.createMembership(
// note: this creates a duplicate membership atm
{
userId: newUser.id,
orgId,
role: OrgMembershipRole.Member,
status: OrgMembershipStatus.Invited
},
tx
);
return newUser; return newUser;
}); });
} }
console.log("samlLogin C");
const isUserCompleted = Boolean(user.isAccepted); const isUserCompleted = Boolean(user.isAccepted);
const providerAuthToken = jwt.sign( const providerAuthToken = jwt.sign(
{ {
@@ -45,6 +45,7 @@ export type TGetSamlCfgDTO =
}; };
export type TSamlLoginDTO = { export type TSamlLoginDTO = {
externalId: string;
username: string; username: string;
email?: string; email?: string;
firstName: string; firstName: string;
+1
View File
@@ -260,6 +260,7 @@ export const registerRoutes = async (
orgBotDAL, orgBotDAL,
orgDAL, orgDAL,
userDAL, userDAL,
userAliasDAL,
samlConfigDAL, samlConfigDAL,
licenseService licenseService
}); });
@@ -0,0 +1,4 @@
export enum UserAliasType {
LDAP = "ldap",
SAML = "saml"
}