mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Add username field to users
This commit is contained in:
@@ -26,9 +26,11 @@ export async function up(knex: Knex): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await knex.schema.alterTable(TableName.Users, (t) => {
|
await knex.schema.alterTable(TableName.Users, (t) => {
|
||||||
t.string("username");
|
t.string("username").notNullable();
|
||||||
t.uuid("orgId");
|
t.uuid("orgId").nullable();
|
||||||
|
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
|
||||||
t.string("email").nullable().alter();
|
t.string("email").nullable().alter();
|
||||||
|
t.unique(["username", "orgId"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
await knex(TableName.Users).update("username", knex.ref("email"));
|
await knex(TableName.Users).update("username", knex.ref("email"));
|
||||||
@@ -21,7 +21,7 @@ export const UsersSchema = z.object({
|
|||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
isGhost: z.boolean().default(false),
|
isGhost: z.boolean().default(false),
|
||||||
username: z.string().nullable().optional(),
|
username: z.string(),
|
||||||
orgId: z.string().uuid().nullable().optional()
|
orgId: z.string().uuid().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -355,6 +355,7 @@ export const ldapConfigServiceFactory = ({
|
|||||||
const newUser = await userDAL.create(
|
const newUser = await userDAL.create(
|
||||||
{
|
{
|
||||||
username,
|
username,
|
||||||
|
orgId,
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
authMethods: [AuthMethod.EMAIL],
|
authMethods: [AuthMethod.EMAIL],
|
||||||
|
|||||||
@@ -334,6 +334,7 @@ export const samlConfigServiceFactory = ({
|
|||||||
user = await userDAL.transaction(async (tx) => {
|
user = await userDAL.transaction(async (tx) => {
|
||||||
const newUser = await userDAL.create(
|
const newUser = await userDAL.create(
|
||||||
{
|
{
|
||||||
|
username: email,
|
||||||
email,
|
email,
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ export const scimServiceFactory = ({
|
|||||||
|
|
||||||
return buildScimUser({
|
return buildScimUser({
|
||||||
userId: membership.userId as string,
|
userId: membership.userId as string,
|
||||||
username: membership.username as string,
|
username: membership.username,
|
||||||
email: membership.email ?? "",
|
email: membership.email ?? "",
|
||||||
firstName: membership.firstName as string,
|
firstName: membership.firstName as string,
|
||||||
lastName: membership.lastName as string,
|
lastName: membership.lastName as string,
|
||||||
@@ -205,6 +205,7 @@ export const scimServiceFactory = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: update SCIM endpoints to add username
|
||||||
const createScimUser = async ({ firstName, lastName, email, orgId }: TCreateScimUserDTO) => {
|
const createScimUser = async ({ firstName, lastName, email, orgId }: TCreateScimUserDTO) => {
|
||||||
const org = await orgDAL.findById(orgId);
|
const org = await orgDAL.findById(orgId);
|
||||||
|
|
||||||
@@ -250,6 +251,7 @@ export const scimServiceFactory = ({
|
|||||||
user = await userDAL.transaction(async (tx) => {
|
user = await userDAL.transaction(async (tx) => {
|
||||||
const newUser = await userDAL.create(
|
const newUser = await userDAL.create(
|
||||||
{
|
{
|
||||||
|
username: email,
|
||||||
email,
|
email,
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
@@ -286,7 +288,7 @@ export const scimServiceFactory = ({
|
|||||||
|
|
||||||
return buildScimUser({
|
return buildScimUser({
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
username: user.username as string,
|
username: user.username,
|
||||||
firstName: user.firstName as string,
|
firstName: user.firstName as string,
|
||||||
lastName: user.lastName as string,
|
lastName: user.lastName as string,
|
||||||
email: user.email ?? "",
|
email: user.email ?? "",
|
||||||
@@ -345,7 +347,7 @@ export const scimServiceFactory = ({
|
|||||||
|
|
||||||
return buildScimUser({
|
return buildScimUser({
|
||||||
userId: membership.userId as string,
|
userId: membership.userId as string,
|
||||||
username: membership.username as string,
|
username: membership.username,
|
||||||
email: membership.email ?? "",
|
email: membership.email ?? "",
|
||||||
firstName: membership.firstName as string,
|
firstName: membership.firstName as string,
|
||||||
lastName: membership.lastName as string,
|
lastName: membership.lastName as string,
|
||||||
@@ -391,7 +393,7 @@ export const scimServiceFactory = ({
|
|||||||
|
|
||||||
return buildScimUser({
|
return buildScimUser({
|
||||||
userId: membership.userId as string,
|
userId: membership.userId as string,
|
||||||
username: membership.username as string,
|
username: membership.username,
|
||||||
email: membership.email ?? "",
|
email: membership.email ?? "",
|
||||||
firstName: membership.firstName as string,
|
firstName: membership.firstName as string,
|
||||||
lastName: membership.lastName as string,
|
lastName: membership.lastName as string,
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
|||||||
users: OrgMembershipsSchema.merge(
|
users: OrgMembershipsSchema.merge(
|
||||||
z.object({
|
z.object({
|
||||||
user: UsersSchema.pick({
|
user: UsersSchema.pick({
|
||||||
|
username: true,
|
||||||
email: true,
|
email: true,
|
||||||
firstName: true,
|
firstName: true,
|
||||||
lastName: true,
|
lastName: true,
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
|||||||
users: ProjectMembershipsSchema.merge(
|
users: ProjectMembershipsSchema.merge(
|
||||||
z.object({
|
z.object({
|
||||||
user: UsersSchema.pick({
|
user: UsersSchema.pick({
|
||||||
|
username: true,
|
||||||
email: true,
|
email: true,
|
||||||
firstName: true,
|
firstName: true,
|
||||||
lastName: true,
|
lastName: true,
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
|||||||
users: OrgMembershipsSchema.merge(
|
users: OrgMembershipsSchema.merge(
|
||||||
z.object({
|
z.object({
|
||||||
user: UsersSchema.pick({
|
user: UsersSchema.pick({
|
||||||
|
username: true,
|
||||||
email: true,
|
email: true,
|
||||||
firstName: true,
|
firstName: true,
|
||||||
lastName: true,
|
lastName: true,
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
|
|||||||
projectId: z.string().describe("The ID of the project.")
|
projectId: z.string().describe("The ID of the project.")
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
emails: z.string().email().array().describe("Emails of the users to add to the project.")
|
emails: z.string().email().array().default([]).describe("Emails of the users to add to the project."),
|
||||||
|
usernames: z.string().email().array().default([]).describe("Usernames of the users to add to the project.")
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@@ -28,7 +29,8 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
|
|||||||
projectId: req.params.projectId,
|
projectId: req.params.projectId,
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
emails: req.body.emails
|
emails: req.body.emails,
|
||||||
|
usernames: req.body.usernames
|
||||||
});
|
});
|
||||||
|
|
||||||
await server.services.auditLog.createAuditLog({
|
await server.services.auditLog.createAuditLog({
|
||||||
|
|||||||
@@ -286,7 +286,14 @@ export const authLoginServiceFactory = ({ userDAL, tokenService, smtpService }:
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
user = await userDAL.create({ email, firstName, lastName, authMethods: [authMethod], isGhost: false });
|
user = await userDAL.create({
|
||||||
|
username: email,
|
||||||
|
email,
|
||||||
|
firstName,
|
||||||
|
lastName,
|
||||||
|
authMethods: [authMethod],
|
||||||
|
isGhost: false
|
||||||
|
});
|
||||||
}
|
}
|
||||||
const isLinkingRequired = !user?.authMethods?.includes(authMethod);
|
const isLinkingRequired = !user?.authMethods?.includes(authMethod);
|
||||||
const isUserCompleted = user.isAccepted;
|
const isUserCompleted = user.isAccepted;
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ export const authSignupServiceFactory = ({
|
|||||||
throw new Error("Failed to send verification code for complete account");
|
throw new Error("Failed to send verification code for complete account");
|
||||||
}
|
}
|
||||||
if (!user) {
|
if (!user) {
|
||||||
user = await userDAL.create({ authMethods: [AuthMethod.EMAIL], email, isGhost: false });
|
user = await userDAL.create({ authMethods: [AuthMethod.EMAIL], username: email, email, isGhost: false });
|
||||||
}
|
}
|
||||||
if (!user) throw new Error("Failed to create user");
|
if (!user) throw new Error("Failed to create user");
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export const orgDALFactory = (db: TDbClient) => {
|
|||||||
const findAllOrgMembers = async (orgId: string) => {
|
const findAllOrgMembers = async (orgId: string) => {
|
||||||
try {
|
try {
|
||||||
const members = await db(TableName.OrgMembership)
|
const members = await db(TableName.OrgMembership)
|
||||||
.where({ orgId })
|
.where(`${TableName.OrgMembership}.orgId`, orgId)
|
||||||
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
|
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
|
||||||
.leftJoin<TUserEncryptionKeys>(
|
.leftJoin<TUserEncryptionKeys>(
|
||||||
TableName.UserEncryptionKey,
|
TableName.UserEncryptionKey,
|
||||||
@@ -72,22 +72,24 @@ export const orgDALFactory = (db: TDbClient) => {
|
|||||||
db.ref("roleId").withSchema(TableName.OrgMembership),
|
db.ref("roleId").withSchema(TableName.OrgMembership),
|
||||||
db.ref("status").withSchema(TableName.OrgMembership),
|
db.ref("status").withSchema(TableName.OrgMembership),
|
||||||
db.ref("email").withSchema(TableName.Users),
|
db.ref("email").withSchema(TableName.Users),
|
||||||
|
db.ref("username").withSchema(TableName.Users),
|
||||||
db.ref("firstName").withSchema(TableName.Users),
|
db.ref("firstName").withSchema(TableName.Users),
|
||||||
db.ref("lastName").withSchema(TableName.Users),
|
db.ref("lastName").withSchema(TableName.Users),
|
||||||
db.ref("id").withSchema(TableName.Users).as("userId"),
|
db.ref("id").withSchema(TableName.Users).as("userId"),
|
||||||
db.ref("publicKey").withSchema(TableName.UserEncryptionKey)
|
db.ref("publicKey").withSchema(TableName.UserEncryptionKey)
|
||||||
)
|
)
|
||||||
.where({ isGhost: false }); // MAKE SURE USER IS NOT A GHOST USER
|
.where({ isGhost: false }); // MAKE SURE USER IS NOT A GHOST USER
|
||||||
return members.map(({ email, firstName, lastName, userId, publicKey, ...data }) => ({
|
|
||||||
|
return members.map(({ email, username, firstName, lastName, userId, publicKey, ...data }) => ({
|
||||||
...data,
|
...data,
|
||||||
user: { email, firstName, lastName, id: userId, publicKey }
|
user: { email, username, firstName, lastName, id: userId, publicKey }
|
||||||
}));
|
}));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new DatabaseError({ error, name: "Find all org members" });
|
throw new DatabaseError({ error, name: "Find all org members" });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const findOrgMembersByEmail = async (orgId: string, emails: string[]) => {
|
const findOrgMembersByUsername = async (orgId: string, usernames: string[]) => {
|
||||||
try {
|
try {
|
||||||
const members = await db(TableName.OrgMembership)
|
const members = await db(TableName.OrgMembership)
|
||||||
.where({ orgId })
|
.where({ orgId })
|
||||||
@@ -104,6 +106,7 @@ export const orgDALFactory = (db: TDbClient) => {
|
|||||||
db.ref("role").withSchema(TableName.OrgMembership),
|
db.ref("role").withSchema(TableName.OrgMembership),
|
||||||
db.ref("roleId").withSchema(TableName.OrgMembership),
|
db.ref("roleId").withSchema(TableName.OrgMembership),
|
||||||
db.ref("status").withSchema(TableName.OrgMembership),
|
db.ref("status").withSchema(TableName.OrgMembership),
|
||||||
|
db.ref("username").withSchema(TableName.Users),
|
||||||
db.ref("email").withSchema(TableName.Users),
|
db.ref("email").withSchema(TableName.Users),
|
||||||
db.ref("firstName").withSchema(TableName.Users),
|
db.ref("firstName").withSchema(TableName.Users),
|
||||||
db.ref("lastName").withSchema(TableName.Users),
|
db.ref("lastName").withSchema(TableName.Users),
|
||||||
@@ -111,7 +114,7 @@ export const orgDALFactory = (db: TDbClient) => {
|
|||||||
db.ref("publicKey").withSchema(TableName.UserEncryptionKey)
|
db.ref("publicKey").withSchema(TableName.UserEncryptionKey)
|
||||||
)
|
)
|
||||||
.where({ isGhost: false })
|
.where({ isGhost: false })
|
||||||
.whereIn("email", emails);
|
.whereIn("username", usernames);
|
||||||
return members.map(({ email, firstName, lastName, userId, publicKey, ...data }) => ({
|
return members.map(({ email, firstName, lastName, userId, publicKey, ...data }) => ({
|
||||||
...data,
|
...data,
|
||||||
user: { email, firstName, lastName, id: userId, publicKey }
|
user: { email, firstName, lastName, id: userId, publicKey }
|
||||||
@@ -267,7 +270,7 @@ export const orgDALFactory = (db: TDbClient) => {
|
|||||||
findOrgById,
|
findOrgById,
|
||||||
findAllOrgsByUserId,
|
findAllOrgsByUserId,
|
||||||
ghostUserExists,
|
ghostUserExists,
|
||||||
findOrgMembersByEmail,
|
findOrgMembersByUsername,
|
||||||
findOrgGhostUser,
|
findOrgGhostUser,
|
||||||
create,
|
create,
|
||||||
updateById,
|
updateById,
|
||||||
|
|||||||
@@ -97,11 +97,11 @@ export const orgServiceFactory = ({
|
|||||||
return members;
|
return members;
|
||||||
};
|
};
|
||||||
|
|
||||||
const findOrgMembersByEmail = async ({ actor, actorId, orgId, emails }: TFindOrgMembersByEmailDTO) => {
|
const findOrgMembersByUsername = async ({ actor, actorId, orgId, emails }: TFindOrgMembersByEmailDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId);
|
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Member);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Member);
|
||||||
|
|
||||||
const members = await orgDAL.findOrgMembersByEmail(orgId, emails);
|
const members = await orgDAL.findOrgMembersByUsername(orgId, emails);
|
||||||
|
|
||||||
return members;
|
return members;
|
||||||
};
|
};
|
||||||
@@ -139,6 +139,7 @@ export const orgServiceFactory = ({
|
|||||||
{
|
{
|
||||||
isGhost: true,
|
isGhost: true,
|
||||||
authMethods: [AuthMethod.EMAIL],
|
authMethods: [AuthMethod.EMAIL],
|
||||||
|
username: email,
|
||||||
email,
|
email,
|
||||||
isAccepted: true
|
isAccepted: true
|
||||||
},
|
},
|
||||||
@@ -405,6 +406,7 @@ export const orgServiceFactory = ({
|
|||||||
// not invited before
|
// not invited before
|
||||||
const user = await userDAL.create(
|
const user = await userDAL.create(
|
||||||
{
|
{
|
||||||
|
username: inviteeEmail,
|
||||||
email: inviteeEmail,
|
email: inviteeEmail,
|
||||||
isAccepted: false,
|
isAccepted: false,
|
||||||
authMethods: [AuthMethod.EMAIL],
|
authMethods: [AuthMethod.EMAIL],
|
||||||
@@ -557,7 +559,7 @@ export const orgServiceFactory = ({
|
|||||||
inviteUserToOrganization,
|
inviteUserToOrganization,
|
||||||
verifyUserToOrg,
|
verifyUserToOrg,
|
||||||
updateOrg,
|
updateOrg,
|
||||||
findOrgMembersByEmail,
|
findOrgMembersByUsername,
|
||||||
createOrganization,
|
createOrganization,
|
||||||
deleteOrganizationById,
|
deleteOrganizationById,
|
||||||
deleteOrgMembership,
|
deleteOrgMembership,
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export const projectMembershipDALFactory = (db: TDbClient) => {
|
|||||||
db.ref("role").withSchema(TableName.ProjectMembership),
|
db.ref("role").withSchema(TableName.ProjectMembership),
|
||||||
db.ref("roleId").withSchema(TableName.ProjectMembership),
|
db.ref("roleId").withSchema(TableName.ProjectMembership),
|
||||||
db.ref("isGhost").withSchema(TableName.Users),
|
db.ref("isGhost").withSchema(TableName.Users),
|
||||||
|
db.ref("username").withSchema(TableName.Users),
|
||||||
db.ref("email").withSchema(TableName.Users),
|
db.ref("email").withSchema(TableName.Users),
|
||||||
db.ref("publicKey").withSchema(TableName.UserEncryptionKey),
|
db.ref("publicKey").withSchema(TableName.UserEncryptionKey),
|
||||||
db.ref("firstName").withSchema(TableName.Users),
|
db.ref("firstName").withSchema(TableName.Users),
|
||||||
@@ -32,9 +33,9 @@ export const projectMembershipDALFactory = (db: TDbClient) => {
|
|||||||
db.ref("id").withSchema(TableName.Users).as("userId")
|
db.ref("id").withSchema(TableName.Users).as("userId")
|
||||||
)
|
)
|
||||||
.where({ isGhost: false });
|
.where({ isGhost: false });
|
||||||
return members.map(({ email, firstName, lastName, publicKey, isGhost, ...data }) => ({
|
return members.map(({ username, email, firstName, lastName, publicKey, isGhost, ...data }) => ({
|
||||||
...data,
|
...data,
|
||||||
user: { email, firstName, lastName, id: data.userId, publicKey, isGhost }
|
user: { username, email, firstName, lastName, id: data.userId, publicKey, isGhost }
|
||||||
}));
|
}));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new DatabaseError({ error, name: "Find all project members" });
|
throw new DatabaseError({ error, name: "Find all project members" });
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ type TProjectMembershipServiceFactoryDep = {
|
|||||||
projectMembershipDAL: TProjectMembershipDALFactory;
|
projectMembershipDAL: TProjectMembershipDALFactory;
|
||||||
userDAL: Pick<TUserDALFactory, "findById" | "findOne" | "findUserByProjectMembershipId" | "find">;
|
userDAL: Pick<TUserDALFactory, "findById" | "findOne" | "findUserByProjectMembershipId" | "find">;
|
||||||
projectRoleDAL: Pick<TProjectRoleDALFactory, "findOne">;
|
projectRoleDAL: Pick<TProjectRoleDALFactory, "findOne">;
|
||||||
orgDAL: Pick<TOrgDALFactory, "findMembership" | "findOrgMembersByEmail">;
|
orgDAL: Pick<TOrgDALFactory, "findMembership" | "findOrgMembersByUsername">;
|
||||||
projectDAL: Pick<TProjectDALFactory, "findById" | "findProjectGhostUser" | "transaction">;
|
projectDAL: Pick<TProjectDALFactory, "findById" | "findProjectGhostUser" | "transaction">;
|
||||||
projectKeyDAL: Pick<TProjectKeyDALFactory, "findLatestProjectKey" | "delete" | "insertMany">;
|
projectKeyDAL: Pick<TProjectKeyDALFactory, "findLatestProjectKey" | "delete" | "insertMany">;
|
||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||||
@@ -224,6 +224,7 @@ export const projectMembershipServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
actor,
|
actor,
|
||||||
emails,
|
emails,
|
||||||
|
usernames,
|
||||||
sendEmails = true
|
sendEmails = true
|
||||||
}: TAddUsersToWorkspaceNonE2EEDTO) => {
|
}: TAddUsersToWorkspaceNonE2EEDTO) => {
|
||||||
const project = await projectDAL.findById(projectId);
|
const project = await projectDAL.findById(projectId);
|
||||||
@@ -236,7 +237,9 @@ export const projectMembershipServiceFactory = ({
|
|||||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Member);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Member);
|
||||||
|
|
||||||
const orgMembers = await orgDAL.findOrgMembersByEmail(project.orgId, emails);
|
const orgMembers = await orgDAL.findOrgMembersByUsername(project.orgId, [
|
||||||
|
...new Set([...emails, ...usernames].map((element) => element.toLowerCase()))
|
||||||
|
]);
|
||||||
|
|
||||||
if (orgMembers.length !== emails.length) throw new BadRequestError({ message: "Some users are not part of org" });
|
if (orgMembers.length !== emails.length) throw new BadRequestError({ message: "Some users are not part of org" });
|
||||||
|
|
||||||
|
|||||||
@@ -33,4 +33,5 @@ export type TAddUsersToWorkspaceDTO = {
|
|||||||
export type TAddUsersToWorkspaceNonE2EEDTO = {
|
export type TAddUsersToWorkspaceNonE2EEDTO = {
|
||||||
sendEmails?: boolean;
|
sendEmails?: boolean;
|
||||||
emails: string[];
|
emails: string[];
|
||||||
|
usernames: string[];
|
||||||
} & TProjectPermission;
|
} & TProjectPermission;
|
||||||
|
|||||||
@@ -38,7 +38,8 @@ export type UserEnc = {
|
|||||||
export type OrgUser = {
|
export type OrgUser = {
|
||||||
id: string;
|
id: string;
|
||||||
user: {
|
user: {
|
||||||
email: string;
|
username: string;
|
||||||
|
email?: string;
|
||||||
firstName: string;
|
firstName: string;
|
||||||
lastName: string;
|
lastName: string;
|
||||||
id: string;
|
id: string;
|
||||||
|
|||||||
@@ -143,6 +143,7 @@ export const OrgMembersTable = ({ handlePopUpOpen, setCompleteInviteLink }: Prop
|
|||||||
({ user: u, inviteEmail }) =>
|
({ user: u, inviteEmail }) =>
|
||||||
u?.firstName?.toLowerCase().includes(searchMemberFilter.toLowerCase()) ||
|
u?.firstName?.toLowerCase().includes(searchMemberFilter.toLowerCase()) ||
|
||||||
u?.lastName?.toLowerCase().includes(searchMemberFilter.toLowerCase()) ||
|
u?.lastName?.toLowerCase().includes(searchMemberFilter.toLowerCase()) ||
|
||||||
|
u?.username?.toLowerCase().includes(searchMemberFilter.toLowerCase()) ||
|
||||||
u?.email?.toLowerCase().includes(searchMemberFilter.toLowerCase()) ||
|
u?.email?.toLowerCase().includes(searchMemberFilter.toLowerCase()) ||
|
||||||
inviteEmail?.includes(searchMemberFilter.toLowerCase())
|
inviteEmail?.includes(searchMemberFilter.toLowerCase())
|
||||||
),
|
),
|
||||||
@@ -162,7 +163,7 @@ export const OrgMembersTable = ({ handlePopUpOpen, setCompleteInviteLink }: Prop
|
|||||||
<THead>
|
<THead>
|
||||||
<Tr>
|
<Tr>
|
||||||
<Th>Name</Th>
|
<Th>Name</Th>
|
||||||
<Th>Email</Th>
|
<Th>Username</Th>
|
||||||
<Th>Role</Th>
|
<Th>Role</Th>
|
||||||
<Th className="w-5" />
|
<Th className="w-5" />
|
||||||
</Tr>
|
</Tr>
|
||||||
@@ -173,11 +174,11 @@ export const OrgMembersTable = ({ handlePopUpOpen, setCompleteInviteLink }: Prop
|
|||||||
filterdUser?.map(
|
filterdUser?.map(
|
||||||
({ user: u, inviteEmail, role, roleId, id: orgMembershipId, status }) => {
|
({ user: u, inviteEmail, role, roleId, id: orgMembershipId, status }) => {
|
||||||
const name = u && u.firstName ? `${u.firstName} ${u.lastName}` : "-";
|
const name = u && u.firstName ? `${u.firstName} ${u.lastName}` : "-";
|
||||||
const email = u?.email || inviteEmail;
|
const username = u?.username ?? inviteEmail ?? "-";
|
||||||
return (
|
return (
|
||||||
<Tr key={`org-membership-${orgMembershipId}`} className="w-full">
|
<Tr key={`org-membership-${orgMembershipId}`} className="w-full">
|
||||||
<Td>{name}</Td>
|
<Td>{name}</Td>
|
||||||
<Td>{email}</Td>
|
<Td>{username}</Td>
|
||||||
<Td>
|
<Td>
|
||||||
<OrgPermissionCan
|
<OrgPermissionCan
|
||||||
I={OrgPermissionActions.Edit}
|
I={OrgPermissionActions.Edit}
|
||||||
|
|||||||
@@ -9,10 +9,6 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||||
import {
|
|
||||||
decryptAssymmetric,
|
|
||||||
encryptAssymmetric
|
|
||||||
} from "@app/components/utilities/cryptography/crypto";
|
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
DeleteActionModal,
|
DeleteActionModal,
|
||||||
@@ -51,8 +47,7 @@ import {
|
|||||||
useGetProjectRoles,
|
useGetProjectRoles,
|
||||||
useGetUserWsKey,
|
useGetUserWsKey,
|
||||||
useGetWorkspaceUsers,
|
useGetWorkspaceUsers,
|
||||||
useUpdateUserWorkspaceRole,
|
useUpdateUserWorkspaceRole
|
||||||
useUploadWsKey
|
|
||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
|
import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
|
||||||
import { ProjectVersion } from "@app/hooks/api/workspace/types";
|
import { ProjectVersion } from "@app/hooks/api/workspace/types";
|
||||||
@@ -81,7 +76,7 @@ export const MemberListTab = () => {
|
|||||||
const { data: wsKey } = useGetUserWsKey(workspaceId);
|
const { data: wsKey } = useGetUserWsKey(workspaceId);
|
||||||
const { data: members, isLoading: isMembersLoading } = useGetWorkspaceUsers(workspaceId);
|
const { data: members, isLoading: isMembersLoading } = useGetWorkspaceUsers(workspaceId);
|
||||||
const { data: orgUsers } = useGetOrgUsers(orgId);
|
const { data: orgUsers } = useGetOrgUsers(orgId);
|
||||||
|
|
||||||
const [searchMemberFilter, setSearchMemberFilter] = useState("");
|
const [searchMemberFilter, setSearchMemberFilter] = useState("");
|
||||||
|
|
||||||
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
||||||
@@ -99,7 +94,6 @@ export const MemberListTab = () => {
|
|||||||
|
|
||||||
const { mutateAsync: addUserToWorkspace } = useAddUserToWsE2EE();
|
const { mutateAsync: addUserToWorkspace } = useAddUserToWsE2EE();
|
||||||
const { mutateAsync: addUserToWorkspaceNonE2EE } = useAddUserToWsNonE2EE();
|
const { mutateAsync: addUserToWorkspaceNonE2EE } = useAddUserToWsNonE2EE();
|
||||||
const { mutateAsync: uploadWsKey } = useUploadWsKey();
|
|
||||||
const { mutateAsync: removeUserFromWorkspace } = useDeleteUserFromWorkspace();
|
const { mutateAsync: removeUserFromWorkspace } = useDeleteUserFromWorkspace();
|
||||||
const { mutateAsync: updateUserWorkspaceRole } = useUpdateUserWorkspaceRole();
|
const { mutateAsync: updateUserWorkspaceRole } = useUpdateUserWorkspaceRole();
|
||||||
|
|
||||||
@@ -128,7 +122,7 @@ export const MemberListTab = () => {
|
|||||||
} else if (currentWorkspace.version === ProjectVersion.V2) {
|
} else if (currentWorkspace.version === ProjectVersion.V2) {
|
||||||
await addUserToWorkspaceNonE2EE({
|
await addUserToWorkspaceNonE2EE({
|
||||||
projectId: workspaceId,
|
projectId: workspaceId,
|
||||||
emails: [orgUser.user.email]
|
emails: [orgUser.user.username]
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
createNotification({
|
createNotification({
|
||||||
@@ -220,6 +214,7 @@ export const MemberListTab = () => {
|
|||||||
({ user: u, inviteEmail }) =>
|
({ user: u, inviteEmail }) =>
|
||||||
u?.firstName?.toLowerCase().includes(searchMemberFilter.toLowerCase()) ||
|
u?.firstName?.toLowerCase().includes(searchMemberFilter.toLowerCase()) ||
|
||||||
u?.lastName?.toLowerCase().includes(searchMemberFilter.toLowerCase()) ||
|
u?.lastName?.toLowerCase().includes(searchMemberFilter.toLowerCase()) ||
|
||||||
|
u?.username?.toLowerCase().includes(searchMemberFilter.toLowerCase()) ||
|
||||||
u?.email?.toLowerCase().includes(searchMemberFilter.toLowerCase()) ||
|
u?.email?.toLowerCase().includes(searchMemberFilter.toLowerCase()) ||
|
||||||
inviteEmail?.includes(searchMemberFilter.toLowerCase())
|
inviteEmail?.includes(searchMemberFilter.toLowerCase())
|
||||||
),
|
),
|
||||||
@@ -236,40 +231,6 @@ export const MemberListTab = () => {
|
|||||||
);
|
);
|
||||||
}, [orgUsers, members]);
|
}, [orgUsers, members]);
|
||||||
|
|
||||||
const onGrantAccess = async (grantedUserId: string, publicKey: string) => {
|
|
||||||
try {
|
|
||||||
const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY") as string;
|
|
||||||
if (!PRIVATE_KEY || !wsKey) return;
|
|
||||||
|
|
||||||
// assymmetrically decrypt symmetric key with local private key
|
|
||||||
const key = decryptAssymmetric({
|
|
||||||
ciphertext: wsKey.encryptedKey,
|
|
||||||
nonce: wsKey.nonce,
|
|
||||||
publicKey: wsKey.sender.publicKey,
|
|
||||||
privateKey: PRIVATE_KEY
|
|
||||||
});
|
|
||||||
|
|
||||||
const { ciphertext, nonce } = encryptAssymmetric({
|
|
||||||
plaintext: key,
|
|
||||||
publicKey,
|
|
||||||
privateKey: PRIVATE_KEY
|
|
||||||
});
|
|
||||||
|
|
||||||
await uploadWsKey({
|
|
||||||
userId: grantedUserId,
|
|
||||||
nonce,
|
|
||||||
encryptedKey: ciphertext,
|
|
||||||
workspaceId: currentWorkspace?.id || ""
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
createNotification({
|
|
||||||
text: "Failed to grant access to user",
|
|
||||||
type: "error"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const isLoading = isMembersLoading || isRolesLoading;
|
const isLoading = isMembersLoading || isRolesLoading;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -302,7 +263,7 @@ export const MemberListTab = () => {
|
|||||||
<THead>
|
<THead>
|
||||||
<Tr>
|
<Tr>
|
||||||
<Th>Name</Th>
|
<Th>Name</Th>
|
||||||
<Th>Email</Th>
|
<Th>Username</Th>
|
||||||
<Th>Role</Th>
|
<Th>Role</Th>
|
||||||
<Th className="w-5" />
|
<Th className="w-5" />
|
||||||
</Tr>
|
</Tr>
|
||||||
@@ -311,22 +272,20 @@ export const MemberListTab = () => {
|
|||||||
{isLoading && <TableSkeleton columns={4} innerKey="project-members" />}
|
{isLoading && <TableSkeleton columns={4} innerKey="project-members" />}
|
||||||
{!isLoading &&
|
{!isLoading &&
|
||||||
filterdUsers?.map(
|
filterdUsers?.map(
|
||||||
({ user: u, inviteEmail, id: membershipId, status, roleId, role }) => {
|
({ user: u, id: membershipId, roleId, role }) => {
|
||||||
const name = u ? `${u.firstName} ${u.lastName}` : "-";
|
const name = u ? `${u.firstName} ${u.lastName}` : "-";
|
||||||
const email = u?.email || inviteEmail;
|
const username = u?.username ?? "-";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tr key={`membership-${membershipId}`} className="w-full">
|
<Tr key={`membership-${membershipId}`} className="w-full">
|
||||||
<Td>{name}</Td>
|
<Td>{name}</Td>
|
||||||
<Td>{email}</Td>
|
<Td>{username}</Td>
|
||||||
<Td>
|
<Td>
|
||||||
<ProjectPermissionCan
|
<ProjectPermissionCan
|
||||||
I={ProjectPermissionActions.Edit}
|
I={ProjectPermissionActions.Edit}
|
||||||
a={ProjectPermissionSub.Member}
|
a={ProjectPermissionSub.Member}
|
||||||
>
|
>
|
||||||
{(isAllowed) => (
|
{(isAllowed) => (
|
||||||
<>
|
<Select
|
||||||
<Select
|
|
||||||
value={role === "custom" ? findRoleFromId(roleId)?.slug : role}
|
value={role === "custom" ? findRoleFromId(roleId)?.slug : role}
|
||||||
isDisabled={userId === u?.id || !isAllowed}
|
isDisabled={userId === u?.id || !isAllowed}
|
||||||
className="w-40 bg-mineshaft-600"
|
className="w-40 bg-mineshaft-600"
|
||||||
@@ -345,18 +304,6 @@ export const MemberListTab = () => {
|
|||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
{status === "completed" && user.email !== email && (
|
|
||||||
<div className="rounded-md border border-mineshaft-700 bg-white/5 text-white duration-200 hover:bg-primary hover:text-black">
|
|
||||||
<Button
|
|
||||||
colorSchema="secondary"
|
|
||||||
isDisabled={!isAllowed}
|
|
||||||
onClick={() => onGrantAccess(u?.id, u?.publicKey)}
|
|
||||||
>
|
|
||||||
Grant Access
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</ProjectPermissionCan>
|
</ProjectPermissionCan>
|
||||||
</Td>
|
</Td>
|
||||||
|
|||||||
Reference in New Issue
Block a user