mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 16:27:46 +00:00
Merge pull request #1067 from Infisical/delete-org
Delete user, organization, project capabilities feature/update
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import { IUser, Key, Membership, MembershipOrg, User, Workspace } from "../../models";
|
import { IUser, Key, Membership, MembershipOrg, User, Workspace } from "../../models";
|
||||||
import { EventType } from "../../ee/models";
|
import { EventType, Role } from "../../ee/models";
|
||||||
import { deleteMembership as deleteMember, findMembership } from "../../helpers/membership";
|
import { deleteMembership as deleteMember, findMembership } from "../../helpers/membership";
|
||||||
import { sendMail } from "../../helpers/nodemailer";
|
import { sendMail } from "../../helpers/nodemailer";
|
||||||
import { ACCEPTED, ADMIN, CUSTOM, MEMBER, VIEWER } from "../../variables";
|
import { ACCEPTED, ADMIN, CUSTOM, MEMBER, VIEWER } from "../../variables";
|
||||||
@@ -15,7 +15,6 @@ import {
|
|||||||
getUserProjectPermissions
|
getUserProjectPermissions
|
||||||
} from "../../ee/services/ProjectRoleService";
|
} from "../../ee/services/ProjectRoleService";
|
||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import Role from "../../ee/models/role";
|
|
||||||
import { BadRequestError } from "../../utils/errors";
|
import { BadRequestError } from "../../utils/errors";
|
||||||
import { InviteUserToWorkspaceV1 } from "../../validation/workspace";
|
import { InviteUserToWorkspaceV1 } from "../../validation/workspace";
|
||||||
|
|
||||||
|
|||||||
@@ -6,13 +6,11 @@ import {
|
|||||||
Organization,
|
Organization,
|
||||||
Workspace
|
Workspace
|
||||||
} from "../../models";
|
} from "../../models";
|
||||||
import { createOrganization as create } from "../../helpers/organization";
|
|
||||||
import { addMembershipsOrg } from "../../helpers/membershipOrg";
|
|
||||||
import { ACCEPTED, ADMIN } from "../../variables";
|
|
||||||
import { getLicenseServerUrl, getSiteURL } from "../../config";
|
import { getLicenseServerUrl, getSiteURL } from "../../config";
|
||||||
import { licenseServerKeyRequest } from "../../config/request";
|
import { licenseServerKeyRequest } from "../../config/request";
|
||||||
import { validateRequest } from "../../helpers/validation";
|
import { validateRequest } from "../../helpers/validation";
|
||||||
import * as reqValidator from "../../validation/organization";
|
import * as reqValidator from "../../validation/organization";
|
||||||
|
import { ACCEPTED } from "../../variables";
|
||||||
import {
|
import {
|
||||||
OrgPermissionActions,
|
OrgPermissionActions,
|
||||||
OrgPermissionSubjects,
|
OrgPermissionSubjects,
|
||||||
@@ -34,36 +32,6 @@ export const getOrganizations = async (req: Request, res: Response) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Create new organization named [organizationName]
|
|
||||||
* and add user as owner
|
|
||||||
* @param req
|
|
||||||
* @param res
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export const createOrganization = async (req: Request, res: Response) => {
|
|
||||||
const {
|
|
||||||
body: { organizationName }
|
|
||||||
} = await validateRequest(reqValidator.CreateOrgv1, req);
|
|
||||||
|
|
||||||
// create organization and add user as member
|
|
||||||
const organization = await create({
|
|
||||||
email: req.user.email,
|
|
||||||
name: organizationName
|
|
||||||
});
|
|
||||||
|
|
||||||
await addMembershipsOrg({
|
|
||||||
userIds: [req.user._id.toString()],
|
|
||||||
organizationId: organization._id.toString(),
|
|
||||||
roles: [ADMIN],
|
|
||||||
statuses: [ACCEPTED]
|
|
||||||
});
|
|
||||||
|
|
||||||
return res.status(200).send({
|
|
||||||
organization
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return organization with id [organizationId]
|
* Return organization with id [organizationId]
|
||||||
* @param req
|
* @param req
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import GitAppInstallationSession from "../../ee/models/gitAppInstallationSession";
|
import {
|
||||||
|
GitAppInstallationSession,
|
||||||
|
GitAppOrganizationInstallation,
|
||||||
|
GitRisks
|
||||||
|
} from "../../ee/models";
|
||||||
import crypto from "crypto";
|
import crypto from "crypto";
|
||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import { OrganizationNotFoundError, UnauthorizedRequestError } from "../../utils/errors";
|
import { OrganizationNotFoundError, UnauthorizedRequestError } from "../../utils/errors";
|
||||||
import GitAppOrganizationInstallation from "../../ee/models/gitAppOrganizationInstallation";
|
|
||||||
import { scanGithubFullRepoForSecretLeaks } from "../../queues/secret-scanning/githubScanFullRepository";
|
import { scanGithubFullRepoForSecretLeaks } from "../../queues/secret-scanning/githubScanFullRepository";
|
||||||
import { getSecretScanningGitAppId, getSecretScanningPrivateKey } from "../../config";
|
import { getSecretScanningGitAppId, getSecretScanningPrivateKey } from "../../config";
|
||||||
import GitRisks, {
|
import {
|
||||||
STATUS_RESOLVED_FALSE_POSITIVE,
|
STATUS_RESOLVED_FALSE_POSITIVE,
|
||||||
STATUS_RESOLVED_NOT_REVOKED,
|
STATUS_RESOLVED_NOT_REVOKED,
|
||||||
STATUS_RESOLVED_REVOKED
|
STATUS_RESOLVED_REVOKED
|
||||||
|
|||||||
@@ -202,12 +202,12 @@ export const deleteWorkspace = async (req: Request, res: Response) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// delete workspace
|
// delete workspace
|
||||||
await deleteWork({
|
const workspace = await deleteWork({
|
||||||
id: workspaceId
|
workspaceId: new Types.ObjectId(workspaceId)
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: "Successfully deleted workspace"
|
workspace
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,25 @@
|
|||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import { Membership, MembershipOrg, ServiceAccount, Workspace } from "../../models";
|
import {
|
||||||
|
Membership,
|
||||||
|
MembershipOrg,
|
||||||
|
ServiceAccount,
|
||||||
|
Workspace
|
||||||
|
} from "../../models";
|
||||||
|
import { Role } from "../../ee/models";
|
||||||
import { deleteMembershipOrg } from "../../helpers/membershipOrg";
|
import { deleteMembershipOrg } from "../../helpers/membershipOrg";
|
||||||
import { updateSubscriptionOrgQuantity } from "../../helpers/organization";
|
import {
|
||||||
import Role from "../../ee/models/role";
|
createOrganization as create,
|
||||||
import { BadRequestError } from "../../utils/errors";
|
deleteOrganization,
|
||||||
import { CUSTOM } from "../../variables";
|
updateSubscriptionOrgQuantity
|
||||||
|
} from "../../helpers/organization";
|
||||||
|
import { addMembershipsOrg } from "../../helpers/membershipOrg";
|
||||||
|
import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors";
|
||||||
|
import {
|
||||||
|
ACCEPTED,
|
||||||
|
ADMIN,
|
||||||
|
CUSTOM
|
||||||
|
} from "../../variables";
|
||||||
import * as reqValidator from "../../validation/organization";
|
import * as reqValidator from "../../validation/organization";
|
||||||
import { validateRequest } from "../../helpers/validation";
|
import { validateRequest } from "../../helpers/validation";
|
||||||
import {
|
import {
|
||||||
@@ -332,3 +346,60 @@ export const getOrganizationServiceAccounts = async (req: Request, res: Response
|
|||||||
serviceAccounts
|
serviceAccounts
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create new organization named [organizationName]
|
||||||
|
* and add user as owner
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const createOrganization = async (req: Request, res: Response) => {
|
||||||
|
const {
|
||||||
|
body: { name }
|
||||||
|
} = await validateRequest(reqValidator.CreateOrgv2, req);
|
||||||
|
|
||||||
|
// create organization and add user as member
|
||||||
|
const organization = await create({
|
||||||
|
email: req.user.email,
|
||||||
|
name
|
||||||
|
});
|
||||||
|
|
||||||
|
await addMembershipsOrg({
|
||||||
|
userIds: [req.user._id.toString()],
|
||||||
|
organizationId: organization._id.toString(),
|
||||||
|
roles: [ADMIN],
|
||||||
|
statuses: [ACCEPTED]
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
organization
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete organization with id [organizationId]
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
*/
|
||||||
|
export const deleteOrganizationById = async (req: Request, res: Response) => {
|
||||||
|
const {
|
||||||
|
params: { organizationId }
|
||||||
|
} = await validateRequest(reqValidator.DeleteOrgv2, req);
|
||||||
|
|
||||||
|
const membershipOrg = await MembershipOrg.findOne({
|
||||||
|
user: req.user._id,
|
||||||
|
organization: new Types.ObjectId(organizationId),
|
||||||
|
role: ADMIN
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!membershipOrg) throw UnauthorizedRequestError();
|
||||||
|
|
||||||
|
const organization = await deleteOrganization({
|
||||||
|
organizationId: new Types.ObjectId(organizationId)
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
organization
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -5,49 +5,9 @@ import bcrypt from "bcrypt";
|
|||||||
import { APIKeyData, AuthMethod, MembershipOrg, TokenVersion, User } from "../../models";
|
import { APIKeyData, AuthMethod, MembershipOrg, TokenVersion, User } from "../../models";
|
||||||
import { getSaltRounds } from "../../config";
|
import { getSaltRounds } from "../../config";
|
||||||
import { validateRequest } from "../../helpers/validation";
|
import { validateRequest } from "../../helpers/validation";
|
||||||
|
import { deleteUser } from "../../helpers/user";
|
||||||
import * as reqValidator from "../../validation";
|
import * as reqValidator from "../../validation";
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the current user.
|
|
||||||
* @param req
|
|
||||||
* @param res
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export const getMe = async (req: Request, res: Response) => {
|
|
||||||
/*
|
|
||||||
#swagger.summary = "Retrieve the current user on the request"
|
|
||||||
#swagger.description = "Retrieve the current user on the request"
|
|
||||||
|
|
||||||
#swagger.security = [{
|
|
||||||
"apiKeyAuth": []
|
|
||||||
}]
|
|
||||||
|
|
||||||
#swagger.responses[200] = {
|
|
||||||
content: {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"user": {
|
|
||||||
"type": "object",
|
|
||||||
$ref: "#/components/schemas/CurrentUser",
|
|
||||||
"description": "Current user on request"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
const user = await User.findById(req.user._id).select(
|
|
||||||
"+salt +publicKey +encryptedPrivateKey +iv +tag +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag"
|
|
||||||
);
|
|
||||||
|
|
||||||
return res.status(200).send({
|
|
||||||
user
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the current user's MFA-enabled status [isMfaEnabled].
|
* Update the current user's MFA-enabled status [isMfaEnabled].
|
||||||
* Note: Infisical currently only supports email-based 2FA only; this will expand to
|
* Note: Infisical currently only supports email-based 2FA only; this will expand to
|
||||||
@@ -296,3 +256,59 @@ export const deleteMySessions = async (req: Request, res: Response) => {
|
|||||||
message: "Successfully revoked all sessions"
|
message: "Successfully revoked all sessions"
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the current user.
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const getMe = async (req: Request, res: Response) => {
|
||||||
|
/*
|
||||||
|
#swagger.summary = "Retrieve the current user on the request"
|
||||||
|
#swagger.description = "Retrieve the current user on the request"
|
||||||
|
|
||||||
|
#swagger.security = [{
|
||||||
|
"apiKeyAuth": []
|
||||||
|
}]
|
||||||
|
|
||||||
|
#swagger.responses[200] = {
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"user": {
|
||||||
|
"type": "object",
|
||||||
|
$ref: "#/components/schemas/CurrentUser",
|
||||||
|
"description": "Current user on request"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
const user = await User.findById(req.user._id).select(
|
||||||
|
"+salt +publicKey +encryptedPrivateKey +iv +tag +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag"
|
||||||
|
);
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
user
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the current user.
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
*/
|
||||||
|
export const deleteMe = async (req: Request, res: Response) => {
|
||||||
|
const user = await deleteUser({
|
||||||
|
userId: req.user._id
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
user
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -23,7 +23,7 @@ import {
|
|||||||
memberPermissions
|
memberPermissions
|
||||||
} from "../../services/RoleService";
|
} from "../../services/RoleService";
|
||||||
import { BadRequestError } from "../../../utils/errors";
|
import { BadRequestError } from "../../../utils/errors";
|
||||||
import Role from "../../models/role";
|
import { Role } from "../../models";
|
||||||
import { validateRequest } from "../../../helpers/validation";
|
import { validateRequest } from "../../../helpers/validation";
|
||||||
import { packRules } from "@casl/ability/extra";
|
import { packRules } from "@casl/ability/extra";
|
||||||
|
|
||||||
@@ -212,6 +212,7 @@ export const getUserPermissions = async (req: Request, res: Response) => {
|
|||||||
const {
|
const {
|
||||||
params: { orgId }
|
params: { orgId }
|
||||||
} = await validateRequest(GetUserPermission, req);
|
} = await validateRequest(GetUserPermission, req);
|
||||||
|
|
||||||
const { permission } = await getUserOrgPermissions(req.user._id, orgId);
|
const { permission } = await getUserOrgPermissions(req.user._id, orgId);
|
||||||
|
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
|
|||||||
@@ -298,6 +298,10 @@ export const deleteServiceTokenData = async (req: Request, res: Response) => {
|
|||||||
message: "Failed to delete service token"
|
message: "Failed to delete service token"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await ServiceTokenDataV3Key.findOneAndDelete({
|
||||||
|
serviceTokenData: serviceTokenData._id
|
||||||
|
});
|
||||||
|
|
||||||
await EEAuditLogService.createAuditLog(
|
await EEAuditLogService.createAuditLog(
|
||||||
req.authData,
|
req.authData,
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -29,6 +29,4 @@ const gitAppInstallationSession = new Schema<GitAppInstallationSession>({
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const GitAppInstallationSession = model<GitAppInstallationSession>("git_app_installation_session", gitAppInstallationSession);
|
export const GitAppInstallationSession = model<GitAppInstallationSession>("git_app_installation_session", gitAppInstallationSession);
|
||||||
|
|
||||||
export default GitAppInstallationSession;
|
|
||||||
@@ -26,6 +26,4 @@ const gitAppOrganizationInstallation = new Schema<Installation>({
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const GitAppOrganizationInstallation = model<Installation>("git_app_organization_installation", gitAppOrganizationInstallation);
|
export const GitAppOrganizationInstallation = model<Installation>("git_app_organization_installation", gitAppOrganizationInstallation);
|
||||||
|
|
||||||
export default GitAppOrganizationInstallation;
|
|
||||||
@@ -5,7 +5,7 @@ export const STATUS_RESOLVED_REVOKED = "RESOLVED_REVOKED";
|
|||||||
export const STATUS_RESOLVED_NOT_REVOKED = "RESOLVED_NOT_REVOKED";
|
export const STATUS_RESOLVED_NOT_REVOKED = "RESOLVED_NOT_REVOKED";
|
||||||
export const STATUS_UNRESOLVED = "UNRESOLVED";
|
export const STATUS_UNRESOLVED = "UNRESOLVED";
|
||||||
|
|
||||||
export type GitRisks = {
|
export type IGitRisks = {
|
||||||
id: string;
|
id: string;
|
||||||
description: string;
|
description: string;
|
||||||
startLine: string;
|
startLine: string;
|
||||||
@@ -42,7 +42,7 @@ export type GitRisks = {
|
|||||||
organization: Schema.Types.ObjectId,
|
organization: Schema.Types.ObjectId,
|
||||||
}
|
}
|
||||||
|
|
||||||
const gitRisks = new Schema<GitRisks>({
|
const gitRisks = new Schema<IGitRisks>({
|
||||||
id: {
|
id: {
|
||||||
type: String,
|
type: String,
|
||||||
},
|
},
|
||||||
@@ -147,6 +147,4 @@ const gitRisks = new Schema<GitRisks>({
|
|||||||
}
|
}
|
||||||
}, { timestamps: true });
|
}, { timestamps: true });
|
||||||
|
|
||||||
const GitRisks = model<GitRisks>("GitRisks", gitRisks);
|
export const GitRisks = model<IGitRisks>("GitRisks", gitRisks);
|
||||||
|
|
||||||
export default GitRisks;
|
|
||||||
@@ -2,6 +2,7 @@ export * from "./secretSnapshot";
|
|||||||
export * from "./secretVersion";
|
export * from "./secretVersion";
|
||||||
export * from "./folderVersion";
|
export * from "./folderVersion";
|
||||||
export * from "./log";
|
export * from "./log";
|
||||||
|
export * from "./role";
|
||||||
export * from "./action";
|
export * from "./action";
|
||||||
export * from "./ssoConfig";
|
export * from "./ssoConfig";
|
||||||
export * from "./trustedIp";
|
export * from "./trustedIp";
|
||||||
@@ -9,3 +10,5 @@ export * from "./auditLog";
|
|||||||
export * from "./gitRisks";
|
export * from "./gitRisks";
|
||||||
export * from "./gitAppOrganizationInstallation";
|
export * from "./gitAppOrganizationInstallation";
|
||||||
export * from "./gitAppInstallationSession";
|
export * from "./gitAppInstallationSession";
|
||||||
|
export * from "./secretApprovalPolicy";
|
||||||
|
export * from "./secretApprovalRequest";
|
||||||
|
|||||||
@@ -50,6 +50,4 @@ const roleSchema = new Schema<IRole>(
|
|||||||
|
|
||||||
roleSchema.index({ organization: 1, workspace: 1 });
|
roleSchema.index({ organization: 1, workspace: 1 });
|
||||||
|
|
||||||
const Role = model<IRole>("Role", roleSchema);
|
export const Role = model<IRole>("Role", roleSchema);
|
||||||
|
|
||||||
export default Role;
|
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
import { Probot } from "probot";
|
import { Probot } from "probot";
|
||||||
import GitRisks from "../../models/gitRisks";
|
import {
|
||||||
import GitAppOrganizationInstallation from "../../models/gitAppOrganizationInstallation";
|
GitAppOrganizationInstallation,
|
||||||
|
GitRisks
|
||||||
|
} from "../../models";
|
||||||
import { scanGithubPushEventForSecretLeaks } from "../../../queues/secret-scanning/githubScanPushEvent";
|
import { scanGithubPushEventForSecretLeaks } from "../../../queues/secret-scanning/githubScanPushEvent";
|
||||||
export default async (app: Probot) => {
|
export default async (app: Probot) => {
|
||||||
app.on("installation.deleted", async (context) => {
|
app.on("installation.deleted", async (context) => {
|
||||||
|
|||||||
@@ -1,5 +1,43 @@
|
|||||||
import { Types } from "mongoose";
|
import mongoose, { Types } from "mongoose";
|
||||||
import { MembershipOrg, Organization } from "../models";
|
import {
|
||||||
|
Bot,
|
||||||
|
BotKey,
|
||||||
|
BotOrg,
|
||||||
|
Folder,
|
||||||
|
IncidentContactOrg,
|
||||||
|
Integration,
|
||||||
|
IntegrationAuth,
|
||||||
|
Key,
|
||||||
|
Membership,
|
||||||
|
MembershipOrg,
|
||||||
|
Organization,
|
||||||
|
Secret,
|
||||||
|
SecretBlindIndexData,
|
||||||
|
SecretImport,
|
||||||
|
ServiceToken,
|
||||||
|
ServiceTokenData,
|
||||||
|
ServiceTokenDataV3,
|
||||||
|
ServiceTokenDataV3Key,
|
||||||
|
Tag,
|
||||||
|
Webhook,
|
||||||
|
Workspace
|
||||||
|
} from "../models";
|
||||||
|
import {
|
||||||
|
Action,
|
||||||
|
AuditLog,
|
||||||
|
FolderVersion,
|
||||||
|
GitAppInstallationSession,
|
||||||
|
GitAppOrganizationInstallation,
|
||||||
|
GitRisks,
|
||||||
|
Log,
|
||||||
|
Role,
|
||||||
|
SSOConfig,
|
||||||
|
SecretApprovalPolicy,
|
||||||
|
SecretApprovalRequest,
|
||||||
|
SecretSnapshot,
|
||||||
|
SecretVersion,
|
||||||
|
TrustedIP
|
||||||
|
} from "../ee/models";
|
||||||
import {
|
import {
|
||||||
ACCEPTED,
|
ACCEPTED,
|
||||||
} from "../variables";
|
} from "../variables";
|
||||||
@@ -17,6 +55,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
createBotOrg
|
createBotOrg
|
||||||
} from "./botOrg";
|
} from "./botOrg";
|
||||||
|
import { InternalServerError, ResourceNotFoundError } from "../utils/errors";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an organization with name [name]
|
* Create an organization with name [name]
|
||||||
@@ -65,6 +104,227 @@ export const createOrganization = async ({
|
|||||||
return organization;
|
return organization;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete organization with id [organizationId]
|
||||||
|
* @param {Object} obj
|
||||||
|
* @param {Types.ObjectId} obj.organizationId - id of organization to delete
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const deleteOrganization = async ({
|
||||||
|
organizationId
|
||||||
|
}: {
|
||||||
|
organizationId: Types.ObjectId;
|
||||||
|
}) => {
|
||||||
|
const session = await mongoose.startSession();
|
||||||
|
session.startTransaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const organization = await Organization.findByIdAndDelete(organizationId);
|
||||||
|
|
||||||
|
if (!organization) throw ResourceNotFoundError();
|
||||||
|
|
||||||
|
await MembershipOrg.deleteMany({
|
||||||
|
organization: organization._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await BotOrg.deleteMany({
|
||||||
|
organization: organization._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await SSOConfig.deleteMany({
|
||||||
|
organization: organization._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await Role.deleteMany({
|
||||||
|
organization: organization._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await IncidentContactOrg.deleteMany({
|
||||||
|
organization: organization._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await GitRisks.deleteMany({
|
||||||
|
organization: organization._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await GitAppInstallationSession.deleteMany({
|
||||||
|
organization: organization._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await GitAppOrganizationInstallation.deleteMany({
|
||||||
|
organization: organization._id
|
||||||
|
});
|
||||||
|
|
||||||
|
const workspaceIds = await Workspace.distinct("_id", {
|
||||||
|
organization: organization._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await Workspace.deleteMany({
|
||||||
|
organization: organization._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await Membership.deleteMany({
|
||||||
|
workspace: {
|
||||||
|
$in: workspaceIds
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await Key.deleteMany({
|
||||||
|
workspace: {
|
||||||
|
$in: workspaceIds
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await Bot.deleteMany({
|
||||||
|
workspace: {
|
||||||
|
$in: workspaceIds
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await BotKey.deleteMany({
|
||||||
|
workspace: {
|
||||||
|
$in: workspaceIds
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await SecretBlindIndexData.deleteMany({
|
||||||
|
workspace: {
|
||||||
|
$in: workspaceIds
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await Secret.deleteMany({
|
||||||
|
workspace: {
|
||||||
|
$in: workspaceIds
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await SecretVersion.deleteMany({
|
||||||
|
workspace: {
|
||||||
|
$in: workspaceIds
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await SecretSnapshot.deleteMany({
|
||||||
|
workspace: {
|
||||||
|
$in: workspaceIds
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
await SecretImport.deleteMany({
|
||||||
|
workspace: {
|
||||||
|
$in: workspaceIds
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await Folder.deleteMany({
|
||||||
|
workspace: {
|
||||||
|
$in: workspaceIds
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await FolderVersion.deleteMany({
|
||||||
|
workspace: {
|
||||||
|
$in: workspaceIds
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await Webhook.deleteMany({
|
||||||
|
workspace: {
|
||||||
|
$in: workspaceIds
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await TrustedIP.deleteMany({
|
||||||
|
workspace: {
|
||||||
|
$in: workspaceIds
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await Tag.deleteMany({
|
||||||
|
workspace: {
|
||||||
|
$in: workspaceIds
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await IntegrationAuth.deleteMany({
|
||||||
|
workspace: {
|
||||||
|
$in: workspaceIds
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await Integration.deleteMany({
|
||||||
|
workspace: {
|
||||||
|
$in: workspaceIds
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await ServiceToken.deleteMany({
|
||||||
|
workspace: {
|
||||||
|
$in: workspaceIds
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await ServiceTokenData.deleteMany({
|
||||||
|
workspace: {
|
||||||
|
$in: workspaceIds
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await ServiceTokenDataV3.deleteMany({
|
||||||
|
workspace: {
|
||||||
|
$in: workspaceIds
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await ServiceTokenDataV3Key.deleteMany({
|
||||||
|
workspace: {
|
||||||
|
$in: workspaceIds
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await AuditLog.deleteMany({
|
||||||
|
workspace: {
|
||||||
|
$in: workspaceIds
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await Log.deleteMany({
|
||||||
|
workspace: {
|
||||||
|
$in: workspaceIds
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await Action.deleteMany({
|
||||||
|
workspace: {
|
||||||
|
$in: workspaceIds
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await SecretApprovalPolicy.deleteMany({
|
||||||
|
workspace: {
|
||||||
|
$in: workspaceIds
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await SecretApprovalRequest.deleteMany({
|
||||||
|
workspace: {
|
||||||
|
$in: workspaceIds
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return organization;
|
||||||
|
} catch (err) {
|
||||||
|
await session.abortTransaction();
|
||||||
|
throw InternalServerError({
|
||||||
|
message: "Failed to delete organization"
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
session.endSession();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update organization subscription quantity to reflect number of members in
|
* Update organization subscription quantity to reflect number of members in
|
||||||
* the organization.
|
* the organization.
|
||||||
|
|||||||
+189
-1
@@ -1,5 +1,27 @@
|
|||||||
import { IUser, User } from "../models";
|
import mongoose, { Types } from "mongoose";
|
||||||
|
import {
|
||||||
|
APIKeyData,
|
||||||
|
BackupPrivateKey,
|
||||||
|
IUser,
|
||||||
|
Key,
|
||||||
|
Membership,
|
||||||
|
MembershipOrg,
|
||||||
|
TokenVersion,
|
||||||
|
User,
|
||||||
|
UserAction
|
||||||
|
} from "../models";
|
||||||
|
import {
|
||||||
|
Action,
|
||||||
|
Log
|
||||||
|
} from "../ee/models";
|
||||||
import { sendMail } from "./nodemailer";
|
import { sendMail } from "./nodemailer";
|
||||||
|
import {
|
||||||
|
InternalServerError,
|
||||||
|
ResourceNotFoundError
|
||||||
|
} from "../utils/errors";
|
||||||
|
import { ADMIN } from "../variables";
|
||||||
|
import { deleteOrganization } from "../helpers/organization";
|
||||||
|
import { deleteWorkspace } from "../helpers/workspace";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize a user under email [email]
|
* Initialize a user under email [email]
|
||||||
@@ -134,3 +156,169 @@ export const checkUserDevice = async ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that if we delete user with id [userId] then
|
||||||
|
* there won't be any admin-less organizations or projects
|
||||||
|
* @param {Object} obj
|
||||||
|
* @param {String} obj.userId - id of user to check deletion conditions for
|
||||||
|
*/
|
||||||
|
const checkDeleteUserConditions = async ({
|
||||||
|
userId
|
||||||
|
}: {
|
||||||
|
userId: Types.ObjectId;
|
||||||
|
}) => {
|
||||||
|
const memberships = await Membership.find({
|
||||||
|
user: userId
|
||||||
|
});
|
||||||
|
|
||||||
|
const membershipOrgs = await MembershipOrg.find({
|
||||||
|
user: userId
|
||||||
|
});
|
||||||
|
|
||||||
|
// delete organizations where user is only member
|
||||||
|
for await (const membershipOrg of membershipOrgs) {
|
||||||
|
const orgMemberCount = await MembershipOrg.countDocuments({
|
||||||
|
organization: membershipOrg.organization,
|
||||||
|
});
|
||||||
|
|
||||||
|
const otherOrgAdminCount = await MembershipOrg.countDocuments({
|
||||||
|
organization: membershipOrg.organization,
|
||||||
|
user: { $ne: userId },
|
||||||
|
role: ADMIN
|
||||||
|
});
|
||||||
|
|
||||||
|
if (orgMemberCount > 1 && otherOrgAdminCount === 0) {
|
||||||
|
throw InternalServerError({
|
||||||
|
message: "Failed to delete account because an org would be admin-less"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete workspaces where user is only member
|
||||||
|
for await (const membership of memberships) {
|
||||||
|
const workspaceMemberCount = await Membership.countDocuments({
|
||||||
|
workspace: membership.workspace
|
||||||
|
});
|
||||||
|
|
||||||
|
const otherWorkspaceAdminCount = await Membership.countDocuments({
|
||||||
|
workspace: membership.workspace,
|
||||||
|
user: { $ne: userId },
|
||||||
|
role: ADMIN
|
||||||
|
});
|
||||||
|
|
||||||
|
if (workspaceMemberCount > 1 && otherWorkspaceAdminCount === 0) {
|
||||||
|
throw InternalServerError({
|
||||||
|
message: "Failed to delete account because a workspace would be admin-less"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete account with id [userId]
|
||||||
|
* @param {Object} obj
|
||||||
|
* @param {Types.ObjectId} obj.userId - id of user to delete
|
||||||
|
* @returns {User} user - deleted user
|
||||||
|
*/
|
||||||
|
export const deleteUser = async ({
|
||||||
|
userId
|
||||||
|
}: {
|
||||||
|
userId: Types.ObjectId
|
||||||
|
}) => {
|
||||||
|
const session = await mongoose.startSession();
|
||||||
|
session.startTransaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const user = await User.findByIdAndDelete(userId);
|
||||||
|
|
||||||
|
if (!user) throw ResourceNotFoundError();
|
||||||
|
|
||||||
|
await checkDeleteUserConditions({
|
||||||
|
userId: user._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await UserAction.deleteMany({
|
||||||
|
user: user._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await BackupPrivateKey.deleteMany({
|
||||||
|
user: user._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await APIKeyData.deleteMany({
|
||||||
|
user: user._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await Action.deleteMany({
|
||||||
|
user: user._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await Log.deleteMany({
|
||||||
|
user: user._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await TokenVersion.deleteMany({
|
||||||
|
user: user._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await Key.deleteMany({
|
||||||
|
receiver: user._id
|
||||||
|
});
|
||||||
|
|
||||||
|
const membershipOrgs = await MembershipOrg.find({
|
||||||
|
user: userId
|
||||||
|
});
|
||||||
|
|
||||||
|
// delete organizations where user is only member
|
||||||
|
for await (const membershipOrg of membershipOrgs) {
|
||||||
|
const memberCount = await MembershipOrg.countDocuments({
|
||||||
|
organization: membershipOrg.organization
|
||||||
|
});
|
||||||
|
|
||||||
|
if (memberCount === 1) {
|
||||||
|
// organization only has 1 member (the current user)
|
||||||
|
|
||||||
|
await deleteOrganization({
|
||||||
|
organizationId: membershipOrg.organization
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const memberships = await Membership.find({
|
||||||
|
user: userId
|
||||||
|
});
|
||||||
|
|
||||||
|
// delete workspaces where user is only member
|
||||||
|
for await (const membership of memberships) {
|
||||||
|
const memberCount = await Membership.countDocuments({
|
||||||
|
workspace: membership.workspace
|
||||||
|
});
|
||||||
|
|
||||||
|
if (memberCount === 1) {
|
||||||
|
// workspace only has 1 member (the current user) -> delete workspace
|
||||||
|
|
||||||
|
await deleteWorkspace({
|
||||||
|
workspaceId: membership.workspace
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await MembershipOrg.deleteMany({
|
||||||
|
user: userId
|
||||||
|
});
|
||||||
|
|
||||||
|
await Membership.deleteMany({
|
||||||
|
user: userId
|
||||||
|
});
|
||||||
|
|
||||||
|
return user;
|
||||||
|
} catch (err) {
|
||||||
|
await session.abortTransaction();
|
||||||
|
throw InternalServerError({
|
||||||
|
message: "Failed to delete account"
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
session.endSession();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +1,42 @@
|
|||||||
import { Types } from "mongoose";
|
import mongoose, { Types } from "mongoose";
|
||||||
import {
|
import {
|
||||||
Bot,
|
Bot,
|
||||||
|
BotKey,
|
||||||
|
Folder,
|
||||||
|
Integration,
|
||||||
|
IntegrationAuth,
|
||||||
Key,
|
Key,
|
||||||
Membership,
|
Membership,
|
||||||
Secret,
|
Secret,
|
||||||
Workspace,
|
SecretBlindIndexData,
|
||||||
|
SecretImport,
|
||||||
|
ServiceToken,
|
||||||
|
ServiceTokenData,
|
||||||
|
ServiceTokenDataV3,
|
||||||
|
ServiceTokenDataV3Key,
|
||||||
|
Tag,
|
||||||
|
Webhook,
|
||||||
|
Workspace
|
||||||
} from "../models";
|
} from "../models";
|
||||||
import {
|
import {
|
||||||
|
Action,
|
||||||
|
AuditLog,
|
||||||
|
FolderVersion,
|
||||||
IPType,
|
IPType,
|
||||||
|
Log,
|
||||||
|
SecretApprovalPolicy,
|
||||||
|
SecretApprovalRequest,
|
||||||
|
SecretSnapshot,
|
||||||
|
SecretVersion,
|
||||||
TrustedIP
|
TrustedIP
|
||||||
} from "../ee/models";
|
} from "../ee/models";
|
||||||
import { createBot } from "../helpers/bot";
|
import { createBot } from "../helpers/bot";
|
||||||
import { EELicenseService } from "../ee/services";
|
import { EELicenseService } from "../ee/services";
|
||||||
import { SecretService } from "../services";
|
import { SecretService } from "../services";
|
||||||
|
import {
|
||||||
|
InternalServerError,
|
||||||
|
ResourceNotFoundError
|
||||||
|
} from "../utils/errors";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a workspace with name [name] in organization with id [organizationId]
|
* Create a workspace with name [name] in organization with id [organizationId]
|
||||||
@@ -77,18 +101,126 @@ export const createWorkspace = async ({
|
|||||||
* @param {Object} obj
|
* @param {Object} obj
|
||||||
* @param {String} obj.id - id of workspace to delete
|
* @param {String} obj.id - id of workspace to delete
|
||||||
*/
|
*/
|
||||||
export const deleteWorkspace = async ({ id }: { id: string }) => {
|
export const deleteWorkspace = async ({
|
||||||
await Workspace.deleteOne({ _id: id });
|
workspaceId
|
||||||
await Bot.deleteOne({
|
}: {
|
||||||
workspace: id,
|
workspaceId: Types.ObjectId;
|
||||||
});
|
}) => {
|
||||||
await Membership.deleteMany({
|
const session = await mongoose.startSession();
|
||||||
workspace: id,
|
session.startTransaction();
|
||||||
});
|
|
||||||
await Secret.deleteMany({
|
try {
|
||||||
workspace: id,
|
const workspace = await Workspace.findByIdAndDelete(workspaceId);
|
||||||
});
|
|
||||||
await Key.deleteMany({
|
if (!workspace) throw ResourceNotFoundError();
|
||||||
workspace: id,
|
|
||||||
});
|
await Membership.deleteMany({
|
||||||
|
workspace: workspace._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await Key.deleteMany({
|
||||||
|
workspace: workspace._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await Bot.deleteMany({
|
||||||
|
workspace: workspace._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await BotKey.deleteMany({
|
||||||
|
workspace: workspace._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await SecretBlindIndexData.deleteMany({
|
||||||
|
workspace: workspace._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await Secret.deleteMany({
|
||||||
|
workspace: workspace._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await SecretVersion.deleteMany({
|
||||||
|
workspace: workspace._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await SecretSnapshot.deleteMany({
|
||||||
|
workspace: workspace._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await SecretImport.deleteMany({
|
||||||
|
workspace: workspace._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await Folder.deleteMany({
|
||||||
|
workspace: workspace._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await FolderVersion.deleteMany({
|
||||||
|
workspace: workspace._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await Webhook.deleteMany({
|
||||||
|
workspace: workspace._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await TrustedIP.deleteMany({
|
||||||
|
workspace: workspace._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await Tag.deleteMany({
|
||||||
|
workspace: workspace._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await IntegrationAuth.deleteMany({
|
||||||
|
workspace: workspace._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await Integration.deleteMany({
|
||||||
|
workspace: workspace._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await ServiceToken.deleteMany({
|
||||||
|
workspace: workspace._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await ServiceTokenData.deleteMany({
|
||||||
|
workspace: workspace._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await ServiceTokenDataV3.deleteMany({
|
||||||
|
workspace: workspace._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await ServiceTokenDataV3Key.deleteMany({
|
||||||
|
workspace: workspace._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await AuditLog.deleteMany({
|
||||||
|
workspace: workspace._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await Log.deleteMany({
|
||||||
|
workspace: workspace._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await Action.deleteMany({
|
||||||
|
workspace: workspace._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await SecretApprovalPolicy.deleteMany({
|
||||||
|
workspace: workspace._id
|
||||||
|
});
|
||||||
|
|
||||||
|
await SecretApprovalRequest.deleteMany({
|
||||||
|
workspace: workspace._id
|
||||||
|
});
|
||||||
|
|
||||||
|
return workspace;
|
||||||
|
} catch (err) {
|
||||||
|
await session.abortTransaction();
|
||||||
|
throw InternalServerError({
|
||||||
|
message: "Failed to delete organization"
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
session.endSession();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import Queue, { Job } from "bull";
|
|||||||
import { ProbotOctokit } from "probot"
|
import { ProbotOctokit } from "probot"
|
||||||
import TelemetryService from "../../services/TelemetryService";
|
import TelemetryService from "../../services/TelemetryService";
|
||||||
import { sendMail } from "../../helpers";
|
import { sendMail } from "../../helpers";
|
||||||
import GitRisks from "../../ee/models/gitRisks";
|
import { GitRisks } from "../../ee/models";
|
||||||
import { MembershipOrg, User } from "../../models";
|
import { MembershipOrg, User } from "../../models";
|
||||||
import { ADMIN } from "../../variables";
|
import { ADMIN } from "../../variables";
|
||||||
import { convertKeysToLowercase, scanFullRepoContentAndGetFindings } from "../../ee/services/GithubSecretScanning/helper";
|
import { convertKeysToLowercase, scanFullRepoContentAndGetFindings } from "../../ee/services/GithubSecretScanning/helper";
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { ProbotOctokit } from "probot"
|
|||||||
import { Commit } from "@octokit/webhooks-types";
|
import { Commit } from "@octokit/webhooks-types";
|
||||||
import TelemetryService from "../../services/TelemetryService";
|
import TelemetryService from "../../services/TelemetryService";
|
||||||
import { sendMail } from "../../helpers";
|
import { sendMail } from "../../helpers";
|
||||||
import GitRisks from "../../ee/models/gitRisks";
|
import { GitRisks } from "../../ee/models";
|
||||||
import { MembershipOrg, User } from "../../models";
|
import { MembershipOrg, User } from "../../models";
|
||||||
import { ADMIN } from "../../variables";
|
import { ADMIN } from "../../variables";
|
||||||
import { convertKeysToLowercase, scanContentAndGetFindings } from "../../ee/services/GithubSecretScanning/helper";
|
import { convertKeysToLowercase, scanContentAndGetFindings } from "../../ee/services/GithubSecretScanning/helper";
|
||||||
|
|||||||
@@ -13,15 +13,6 @@ router.get(
|
|||||||
organizationController.getOrganizations
|
organizationController.getOrganizations
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
|
||||||
// not used on frontend
|
|
||||||
"/",
|
|
||||||
requireAuth({
|
|
||||||
acceptedAuthModes: [AuthMode.JWT]
|
|
||||||
}),
|
|
||||||
organizationController.createOrganization
|
|
||||||
);
|
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/:organizationId",
|
"/:organizationId",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
|
|||||||
@@ -54,4 +54,20 @@ router.get(
|
|||||||
organizationsController.getOrganizationServiceAccounts
|
organizationsController.getOrganizationServiceAccounts
|
||||||
);
|
);
|
||||||
|
|
||||||
|
router.post(
|
||||||
|
"/",
|
||||||
|
requireAuth({
|
||||||
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
|
}),
|
||||||
|
organizationsController.createOrganization
|
||||||
|
);
|
||||||
|
|
||||||
|
router.delete(
|
||||||
|
"/:organizationId",
|
||||||
|
requireAuth({
|
||||||
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||||
|
}),
|
||||||
|
organizationsController.deleteOrganizationById
|
||||||
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
@@ -4,14 +4,6 @@ import { requireAuth } from "../../middleware";
|
|||||||
import { usersController } from "../../controllers/v2";
|
import { usersController } from "../../controllers/v2";
|
||||||
import { AuthMode } from "../../variables";
|
import { AuthMode } from "../../variables";
|
||||||
|
|
||||||
router.get(
|
|
||||||
"/me",
|
|
||||||
requireAuth({
|
|
||||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
|
||||||
}),
|
|
||||||
usersController.getMe
|
|
||||||
);
|
|
||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
"/me/mfa",
|
"/me/mfa",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
@@ -84,4 +76,20 @@ router.delete(
|
|||||||
usersController.deleteMySessions
|
usersController.deleteMySessions
|
||||||
);
|
);
|
||||||
|
|
||||||
|
router.get(
|
||||||
|
"/me",
|
||||||
|
requireAuth({
|
||||||
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||||
|
}),
|
||||||
|
usersController.getMe
|
||||||
|
);
|
||||||
|
|
||||||
|
router.delete(
|
||||||
|
"/me",
|
||||||
|
requireAuth({
|
||||||
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||||
|
}),
|
||||||
|
usersController.deleteMe
|
||||||
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
@@ -4,7 +4,14 @@ import { Types } from "mongoose";
|
|||||||
import { encryptSymmetric128BitHexKeyUTF8 } from "../crypto";
|
import { encryptSymmetric128BitHexKeyUTF8 } from "../crypto";
|
||||||
import { EESecretService } from "../../ee/services";
|
import { EESecretService } from "../../ee/services";
|
||||||
import { redisClient } from "../../services/RedisService"
|
import { redisClient } from "../../services/RedisService"
|
||||||
import { IPType, ISecretVersion, SecretSnapshot, SecretVersion, TrustedIP } from "../../ee/models";
|
import {
|
||||||
|
IPType,
|
||||||
|
ISecretVersion,
|
||||||
|
Role,
|
||||||
|
SecretSnapshot,
|
||||||
|
SecretVersion,
|
||||||
|
TrustedIP
|
||||||
|
} from "../../ee/models";
|
||||||
import {
|
import {
|
||||||
AuthMethod,
|
AuthMethod,
|
||||||
BackupPrivateKey,
|
BackupPrivateKey,
|
||||||
@@ -34,14 +41,12 @@ import {
|
|||||||
MEMBER,
|
MEMBER,
|
||||||
OWNER
|
OWNER
|
||||||
} from "../../variables";
|
} from "../../variables";
|
||||||
|
|
||||||
import { InternalServerError } from "../errors";
|
import { InternalServerError } from "../errors";
|
||||||
import {
|
import {
|
||||||
ProjectPermissionActions,
|
ProjectPermissionActions,
|
||||||
ProjectPermissionSub,
|
ProjectPermissionSub,
|
||||||
memberProjectPermissions
|
memberProjectPermissions
|
||||||
} from "../../ee/services/ProjectRoleService";
|
} from "../../ee/services/ProjectRoleService";
|
||||||
import Role from "../../ee/models/role";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Backfill secrets to ensure that they're all versioned and have
|
* Backfill secrets to ensure that they're all versioned and have
|
||||||
|
|||||||
@@ -136,12 +136,6 @@ export const GetOrgLicencesv1 = z.object({
|
|||||||
params: z.object({ organizationId: z.string().trim() })
|
params: z.object({ organizationId: z.string().trim() })
|
||||||
});
|
});
|
||||||
|
|
||||||
export const CreateOrgv1 = z.object({
|
|
||||||
body: z.object({
|
|
||||||
organizationName: z.string().trim()
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
export const GetOrgv1 = z.object({
|
export const GetOrgv1 = z.object({
|
||||||
params: z.object({
|
params: z.object({
|
||||||
organizationId: z.string().trim()
|
organizationId: z.string().trim()
|
||||||
@@ -209,3 +203,13 @@ export const VerfiyUserToOrganizationV1 = z.object({
|
|||||||
code: z.string().trim()
|
code: z.string().trim()
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const CreateOrgv2 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
name: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const DeleteOrgv2 = z.object({
|
||||||
|
params: z.object({ organizationId: z.string().trim() })
|
||||||
|
});
|
||||||
@@ -2,8 +2,6 @@
|
|||||||
import jsrp from "jsrp";
|
import jsrp from "jsrp";
|
||||||
|
|
||||||
import { login1, login2 } from "@app/hooks/api/auth/queries";
|
import { login1, login2 } from "@app/hooks/api/auth/queries";
|
||||||
import { fetchOrganizations } from "@app/hooks/api/organization/queries";
|
|
||||||
import { fetchMyOrganizationProjects } from "@app/hooks/api/users/queries";
|
|
||||||
import KeyService from "@app/services/KeyService";
|
import KeyService from "@app/services/KeyService";
|
||||||
|
|
||||||
import Telemetry from "./telemetry/Telemetry";
|
import Telemetry from "./telemetry/Telemetry";
|
||||||
@@ -122,17 +120,7 @@ const attemptLogin = async (
|
|||||||
iv,
|
iv,
|
||||||
tag,
|
tag,
|
||||||
privateKey
|
privateKey
|
||||||
});
|
});
|
||||||
|
|
||||||
const userOrgs = await fetchOrganizations();
|
|
||||||
const orgId = userOrgs[0]._id;
|
|
||||||
localStorage.setItem("orgData.id", orgId);
|
|
||||||
|
|
||||||
const orgUserProjects = await fetchMyOrganizationProjects(orgId);
|
|
||||||
|
|
||||||
if (orgUserProjects.length > 0) {
|
|
||||||
localStorage.setItem("projectData.id", orgUserProjects[0]._id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (email) {
|
if (email) {
|
||||||
telemetry.identify(email, email);
|
telemetry.identify(email, email);
|
||||||
|
|||||||
@@ -2,9 +2,6 @@
|
|||||||
import jsrp from "jsrp";
|
import jsrp from "jsrp";
|
||||||
|
|
||||||
import { login1 , verifyMfaToken } from "@app/hooks/api/auth/queries";
|
import { login1 , verifyMfaToken } from "@app/hooks/api/auth/queries";
|
||||||
import { fetchOrganizations } from "@app/hooks/api/organization/queries";
|
|
||||||
import { fetchMyOrganizationProjects } from "@app/hooks/api/users/queries";
|
|
||||||
// import verifyMfaToken from "@app/pages/api/auth/verifyMfaToken";
|
|
||||||
import KeyService from "@app/services/KeyService";
|
import KeyService from "@app/services/KeyService";
|
||||||
|
|
||||||
import { saveTokenToLocalStorage } from "./saveTokenToLocalStorage";
|
import { saveTokenToLocalStorage } from "./saveTokenToLocalStorage";
|
||||||
@@ -19,7 +16,6 @@ interface IsMfaLoginSuccessful {
|
|||||||
privateKey: string;
|
privateKey: string;
|
||||||
JTWToken: string;
|
JTWToken: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -93,16 +89,6 @@ const attemptLoginMfa = async ({
|
|||||||
privateKey
|
privateKey
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: in the future - move this logic elsewhere
|
|
||||||
// because this function is about logging the user in
|
|
||||||
// and not initializing the login details
|
|
||||||
const userOrgs = await fetchOrganizations();
|
|
||||||
const orgId = userOrgs[0]._id;
|
|
||||||
localStorage.setItem("orgData.id", orgId);
|
|
||||||
|
|
||||||
const orgUserProjects = await fetchMyOrganizationProjects(orgId);
|
|
||||||
localStorage.setItem("projectData.id", orgUserProjects[0]._id);
|
|
||||||
|
|
||||||
resolve({
|
resolve({
|
||||||
success: true,
|
success: true,
|
||||||
loginResponse:{
|
loginResponse:{
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
import jsrp from "jsrp";
|
import jsrp from "jsrp";
|
||||||
|
|
||||||
import { login1, login2 } from "@app/hooks/api/auth/queries";
|
import { login1, login2 } from "@app/hooks/api/auth/queries";
|
||||||
import { fetchOrganizations } from "@app/hooks/api/organization/queries";
|
|
||||||
import { fetchMyOrganizationProjects } from "@app/hooks/api/users/queries";
|
|
||||||
import KeyService from "@app/services/KeyService";
|
import KeyService from "@app/services/KeyService";
|
||||||
|
|
||||||
import Telemetry from "./telemetry/Telemetry";
|
import Telemetry from "./telemetry/Telemetry";
|
||||||
@@ -119,20 +117,6 @@ const attemptLogin = async (
|
|||||||
tag,
|
tag,
|
||||||
privateKey
|
privateKey
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: in the future - move this logic elsewhere
|
|
||||||
// because this function is about logging the user in
|
|
||||||
// and not initializing the login details
|
|
||||||
const userOrgs = await fetchOrganizations();
|
|
||||||
|
|
||||||
const orgId = userOrgs[0]._id;
|
|
||||||
localStorage.setItem("orgData.id", orgId);
|
|
||||||
|
|
||||||
const orgUserProjects = await fetchMyOrganizationProjects(orgId);
|
|
||||||
|
|
||||||
if (orgUserProjects.length > 0) {
|
|
||||||
localStorage.setItem("projectData.id", orgUserProjects[0]._id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (email) {
|
if (email) {
|
||||||
telemetry.identify(email, email);
|
telemetry.identify(email, email);
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
import jsrp from "jsrp";
|
import jsrp from "jsrp";
|
||||||
|
|
||||||
import { login1 , verifyMfaToken } from "@app/hooks/api/auth/queries";
|
import { login1 , verifyMfaToken } from "@app/hooks/api/auth/queries";
|
||||||
import { fetchOrganizations } from "@app/hooks/api/organization/queries";
|
|
||||||
import { fetchMyOrganizationProjects } from "@app/hooks/api/users/queries";
|
|
||||||
import KeyService from "@app/services/KeyService";
|
import KeyService from "@app/services/KeyService";
|
||||||
|
|
||||||
import { saveTokenToLocalStorage } from "./saveTokenToLocalStorage";
|
import { saveTokenToLocalStorage } from "./saveTokenToLocalStorage";
|
||||||
@@ -83,16 +81,6 @@ const attemptLoginMfa = async ({
|
|||||||
privateKey
|
privateKey
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: in the future - move this logic elsewhere
|
|
||||||
// because this function is about logging the user in
|
|
||||||
// and not initializing the login details
|
|
||||||
const userOrgs = await fetchOrganizations();
|
|
||||||
const orgId = userOrgs[0]._id;
|
|
||||||
localStorage.setItem("orgData.id", orgId);
|
|
||||||
|
|
||||||
const orgUserProjects = await fetchMyOrganizationProjects(orgId);
|
|
||||||
localStorage.setItem("projectData.id", orgUserProjects[0]._id);
|
|
||||||
|
|
||||||
resolve(true);
|
resolve(true);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ export {
|
|||||||
useAddOrgPmtMethod,
|
useAddOrgPmtMethod,
|
||||||
useAddOrgTaxId,
|
useAddOrgTaxId,
|
||||||
useCreateCustomerPortalSession,
|
useCreateCustomerPortalSession,
|
||||||
|
useCreateOrg,
|
||||||
|
useDeleteOrgById,
|
||||||
useDeleteOrgPmtMethod,
|
useDeleteOrgPmtMethod,
|
||||||
useDeleteOrgTaxId,
|
useDeleteOrgTaxId,
|
||||||
useGetOrganizations,
|
useGetOrganizations,
|
||||||
|
|||||||
@@ -41,6 +41,25 @@ export const useGetOrganizations = () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const useCreateOrg = () => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: async ({
|
||||||
|
name
|
||||||
|
}: {
|
||||||
|
name: string;
|
||||||
|
}) => {
|
||||||
|
const { data: { organization } } = await apiRequest.post(
|
||||||
|
"/api/v2/organizations",
|
||||||
|
{
|
||||||
|
name
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return organization;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const useRenameOrg = () => {
|
export const useRenameOrg = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
@@ -333,4 +352,33 @@ export const useGetOrgLicenses = (organizationId: string) => {
|
|||||||
},
|
},
|
||||||
enabled: true
|
enabled: true
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useDeleteOrgById = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: async ({
|
||||||
|
organizationId,
|
||||||
|
}: {
|
||||||
|
organizationId: string;
|
||||||
|
}) => {
|
||||||
|
const { data: { organization } } = await apiRequest.delete<{ organization: Organization }>(
|
||||||
|
`/api/v2/organizations/${organizationId}`
|
||||||
|
);
|
||||||
|
return organization;
|
||||||
|
},
|
||||||
|
onSuccess(_, dto) {
|
||||||
|
queryClient.invalidateQueries(organizationKeys.getUserOrganizations);
|
||||||
|
queryClient.invalidateQueries(organizationKeys.getOrgPlanBillingInfo(dto.organizationId));
|
||||||
|
queryClient.invalidateQueries(organizationKeys.getOrgPlanTable(dto.organizationId));
|
||||||
|
queryClient.invalidateQueries(organizationKeys.getOrgPlansTable(dto.organizationId, "monthly")); // You might need to invalidate for 'yearly' as well.
|
||||||
|
queryClient.invalidateQueries(organizationKeys.getOrgPlansTable(dto.organizationId, "yearly"));
|
||||||
|
queryClient.invalidateQueries(organizationKeys.getOrgBillingDetails(dto.organizationId));
|
||||||
|
queryClient.invalidateQueries(organizationKeys.getOrgPmtMethods(dto.organizationId));
|
||||||
|
queryClient.invalidateQueries(organizationKeys.getOrgTaxIds(dto.organizationId));
|
||||||
|
queryClient.invalidateQueries(organizationKeys.getOrgInvoices(dto.organizationId));
|
||||||
|
queryClient.invalidateQueries(organizationKeys.getOrgLicenses(dto.organizationId));
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
@@ -63,10 +63,12 @@ export const useGetRoles = ({ orgId, workspaceId }: TGetRolesDTO) =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
const getUserOrgPermissions = async ({ orgId }: TGetUserOrgPermissionsDTO) => {
|
const getUserOrgPermissions = async ({ orgId }: TGetUserOrgPermissionsDTO) => {
|
||||||
|
if (orgId === "") return [];
|
||||||
|
|
||||||
const { data } = await apiRequest.get<{
|
const { data } = await apiRequest.get<{
|
||||||
data: { permissions: PackRule<RawRuleOf<MongoAbility<OrgPermissionSet>>>[] };
|
data: { permissions: PackRule<RawRuleOf<MongoAbility<OrgPermissionSet>>>[] };
|
||||||
}>(`/api/v1/roles/organization/${orgId}/permissions`, {});
|
}>(`/api/v1/roles/organization/${orgId}/permissions`, {});
|
||||||
|
|
||||||
return data.data.permissions;
|
return data.data.permissions;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -74,7 +76,7 @@ export const useGetUserOrgPermissions = ({ orgId }: TGetUserOrgPermissionsDTO) =
|
|||||||
useQuery({
|
useQuery({
|
||||||
queryKey: roleQueryKeys.getUserOrgPermissions({ orgId }),
|
queryKey: roleQueryKeys.getUserOrgPermissions({ orgId }),
|
||||||
queryFn: () => getUserOrgPermissions({ orgId }),
|
queryFn: () => getUserOrgPermissions({ orgId }),
|
||||||
enabled: Boolean(orgId),
|
// enabled: Boolean(orgId),
|
||||||
select: (data) => {
|
select: (data) => {
|
||||||
const rule = unpackRules<RawRuleOf<MongoAbility<OrgPermissionSet>>>(data);
|
const rule = unpackRules<RawRuleOf<MongoAbility<OrgPermissionSet>>>(data);
|
||||||
const ability = createMongoAbility<OrgPermissionSet>(rule, { conditionsMatcher });
|
const ability = createMongoAbility<OrgPermissionSet>(rule, { conditionsMatcher });
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export {
|
|||||||
useCreateAPIKey,
|
useCreateAPIKey,
|
||||||
useDeleteAPIKey,
|
useDeleteAPIKey,
|
||||||
useDeleteOrgMembership,
|
useDeleteOrgMembership,
|
||||||
|
useDeleteUser,
|
||||||
useGetMyAPIKeys,
|
useGetMyAPIKeys,
|
||||||
useGetMyIp,
|
useGetMyIp,
|
||||||
useGetMyOrganizationProjects,
|
useGetMyOrganizationProjects,
|
||||||
|
|||||||
@@ -42,6 +42,20 @@ export const fetchUserDetails = async () => {
|
|||||||
|
|
||||||
export const useGetUser = () => useQuery(userKeys.getUser, fetchUserDetails);
|
export const useGetUser = () => useQuery(userKeys.getUser, fetchUserDetails);
|
||||||
|
|
||||||
|
export const useDeleteUser = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: async () => {
|
||||||
|
const { data: { user } } = await apiRequest.delete<{ user: User }>("/api/v2/users/me");
|
||||||
|
return user;
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.clear();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const fetchUserAction = async (action: string) => {
|
export const fetchUserAction = async (action: string) => {
|
||||||
const { data } = await apiRequest.get<{ userAction: string }>("/api/v1/user-action", {
|
const { data } = await apiRequest.get<{ userAction: string }>("/api/v1/user-action", {
|
||||||
params: {
|
params: {
|
||||||
@@ -208,21 +222,31 @@ export const useRegisterUserAction = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useLogoutUser = () =>
|
export const useLogoutUser = () => {
|
||||||
useMutation({
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation({
|
||||||
mutationFn: async () => {
|
mutationFn: async () => {
|
||||||
await apiRequest.post("/api/v1/auth/logout");
|
await apiRequest.post("/api/v1/auth/logout");
|
||||||
},
|
},
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
setAuthToken("");
|
setAuthToken("");
|
||||||
// Delete the cookie by not setting a value; Alternatively clear the local storage
|
// Delete the cookie by not setting a value; Alternatively clear the local storage
|
||||||
localStorage.setItem("publicKey", "");
|
localStorage.removeItem("protectedKey");
|
||||||
localStorage.setItem("encryptedPrivateKey", "");
|
localStorage.removeItem("protectedKeyIV");
|
||||||
localStorage.setItem("iv", "");
|
localStorage.removeItem("protectedKeyTag");
|
||||||
localStorage.setItem("tag", "");
|
localStorage.removeItem("publicKey");
|
||||||
localStorage.setItem("PRIVATE_KEY", "");
|
localStorage.removeItem("encryptedPrivateKey");
|
||||||
|
localStorage.removeItem("iv");
|
||||||
|
localStorage.removeItem("tag");
|
||||||
|
localStorage.removeItem("PRIVATE_KEY");
|
||||||
|
localStorage.removeItem("orgData.id");
|
||||||
|
localStorage.removeItem("projectData.id");
|
||||||
|
|
||||||
|
queryClient.clear();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export const useGetMyIp = () => {
|
export const useGetMyIp = () => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ export const AppLayout = ({ children }: LayoutProps) => {
|
|||||||
// eslint-disable-next-line prefer-const
|
// eslint-disable-next-line prefer-const
|
||||||
const { workspaces, currentWorkspace } = useWorkspace();
|
const { workspaces, currentWorkspace } = useWorkspace();
|
||||||
const { orgs, currentOrg } = useOrganization();
|
const { orgs, currentOrg } = useOrganization();
|
||||||
|
|
||||||
const { user } = useUser();
|
const { user } = useUser();
|
||||||
const { subscription } = useSubscription();
|
const { subscription } = useSubscription();
|
||||||
const workspaceId = currentWorkspace?._id || "";
|
const workspaceId = currentWorkspace?._id || "";
|
||||||
@@ -157,16 +158,6 @@ export const AppLayout = ({ children }: LayoutProps) => {
|
|||||||
try {
|
try {
|
||||||
console.log("Logging out...");
|
console.log("Logging out...");
|
||||||
await logout.mutateAsync();
|
await logout.mutateAsync();
|
||||||
localStorage.removeItem("protectedKey");
|
|
||||||
localStorage.removeItem("protectedKeyIV");
|
|
||||||
localStorage.removeItem("protectedKeyTag");
|
|
||||||
localStorage.removeItem("publicKey");
|
|
||||||
localStorage.removeItem("encryptedPrivateKey");
|
|
||||||
localStorage.removeItem("iv");
|
|
||||||
localStorage.removeItem("tag");
|
|
||||||
localStorage.removeItem("PRIVATE_KEY");
|
|
||||||
localStorage.removeItem("orgData.id");
|
|
||||||
localStorage.removeItem("projectData.id");
|
|
||||||
router.push("/login");
|
router.push("/login");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
import SecurityClient from "@app/components/utilities/SecurityClient";
|
||||||
|
|
||||||
export type GitRisks = {
|
export type IGitRisks = {
|
||||||
_id: string;
|
_id: string;
|
||||||
description: string;
|
description: string;
|
||||||
startLine: string;
|
startLine: string;
|
||||||
@@ -41,7 +41,7 @@ export type GitRisks = {
|
|||||||
* Will create a new integration session and return it for the given org
|
* Will create a new integration session and return it for the given org
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const getRisksByOrganization = (oranizationId: string): Promise<GitRisks[]> =>
|
const getRisksByOrganization = (oranizationId: string): Promise<IGitRisks[]> =>
|
||||||
SecurityClient.fetchCall(`/api/v1/secret-scanning/organization/${oranizationId}/risks`, {
|
SecurityClient.fetchCall(`/api/v1/secret-scanning/organization/${oranizationId}/risks`, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -18,4 +18,4 @@ export default function SettingsOrg() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsOrg.requireAuth = true;
|
SettingsOrg.requireAuth = true;
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import Head from "next/head";
|
||||||
|
|
||||||
|
import { NonePage } from "@app/views/Org/NonePage";
|
||||||
|
|
||||||
|
export default function NoneOrganization() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>{t("common.head-title", { title: t("settings.org.title") })}</title>
|
||||||
|
<link rel="icon" href="/infisical.ico" />
|
||||||
|
</Head>
|
||||||
|
<NonePage />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
NoneOrganization.requireAuth = true;
|
||||||
@@ -2,7 +2,6 @@ import { useEffect, useState } from "react";
|
|||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import axios from "axios"
|
import axios from "axios"
|
||||||
|
|
||||||
import { fetchOrganizations } from "@app/hooks/api/organization/queries";
|
|
||||||
import { fetchUserDetails } from "@app/hooks/api/users/queries";
|
import { fetchUserDetails } from "@app/hooks/api/users/queries";
|
||||||
import { getAuthToken, isLoggedIn } from "@app/reactQuery";
|
import { getAuthToken, isLoggedIn } from "@app/reactQuery";
|
||||||
|
|
||||||
@@ -11,6 +10,8 @@ import {
|
|||||||
MFAStep,
|
MFAStep,
|
||||||
SAMLSSOStep
|
SAMLSSOStep
|
||||||
} from "./components";
|
} from "./components";
|
||||||
|
// import { navigateUserToOrg } from "../../Login.utils";
|
||||||
|
import { navigateUserToOrg } from "./Login.utils";
|
||||||
|
|
||||||
export const Login = () => {
|
export const Login = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -24,10 +25,6 @@ export const Login = () => {
|
|||||||
// TODO(akhilmhdh): workspace will be controlled by a workspace context
|
// TODO(akhilmhdh): workspace will be controlled by a workspace context
|
||||||
const redirectToDashboard = async () => {
|
const redirectToDashboard = async () => {
|
||||||
try {
|
try {
|
||||||
const userOrgs = await fetchOrganizations();
|
|
||||||
// userWorkspace = userWorkspaces[0] && userWorkspaces[0]._id;
|
|
||||||
const userOrg = userOrgs[0] && userOrgs[0]._id;
|
|
||||||
|
|
||||||
// user details
|
// user details
|
||||||
const userDetails = await fetchUserDetails()
|
const userDetails = await fetchUserDetails()
|
||||||
// send details back to client
|
// send details back to client
|
||||||
@@ -40,7 +37,8 @@ export const Login = () => {
|
|||||||
const instance = axios.create()
|
const instance = axios.create()
|
||||||
await instance.post(cliUrl, { email: userDetails.email, privateKey: localStorage.getItem("PRIVATE_KEY"), JTWToken: getAuthToken() })
|
await instance.post(cliUrl, { email: userDetails.email, privateKey: localStorage.getItem("PRIVATE_KEY"), JTWToken: getAuthToken() })
|
||||||
}
|
}
|
||||||
router.push(`/org/${userOrg}/overview`);
|
|
||||||
|
await navigateUserToOrg(router);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("Error - Not logged in yet");
|
console.log("Error - Not logged in yet");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { NextRouter } from "next/router";
|
||||||
|
|
||||||
|
import { fetchOrganizations } from "@app/hooks/api/organization/queries";
|
||||||
|
|
||||||
|
export const navigateUserToOrg = async (router: NextRouter) => {
|
||||||
|
const userOrgs = await fetchOrganizations();
|
||||||
|
|
||||||
|
if (userOrgs.length > 0) {
|
||||||
|
// user is part of at least 1 org
|
||||||
|
const userOrg = userOrgs[0] && userOrgs[0]._id;
|
||||||
|
localStorage.setItem("orgData.id", userOrg);
|
||||||
|
router.push(`/org/${userOrg}/overview`);
|
||||||
|
} else {
|
||||||
|
// user is not part of any org
|
||||||
|
router.push("/org/none");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,9 +12,10 @@ import { useNotificationContext } from "@app/components/context/Notifications/No
|
|||||||
import attemptCliLogin from "@app/components/utilities/attemptCliLogin";
|
import attemptCliLogin from "@app/components/utilities/attemptCliLogin";
|
||||||
import attemptLogin from "@app/components/utilities/attemptLogin";
|
import attemptLogin from "@app/components/utilities/attemptLogin";
|
||||||
import { Button, Input } from "@app/components/v2";
|
import { Button, Input } from "@app/components/v2";
|
||||||
import { fetchOrganizations } from "@app/hooks/api/organization/queries";
|
|
||||||
import { useFetchServerStatus } from "@app/hooks/api/serverDetails";
|
import { useFetchServerStatus } from "@app/hooks/api/serverDetails";
|
||||||
|
|
||||||
|
import { navigateUserToOrg } from "../../Login.utils";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
setStep: (step: number) => void;
|
setStep: (step: number) => void;
|
||||||
email: string;
|
email: string;
|
||||||
@@ -73,6 +74,7 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
|
|||||||
email: email.toLowerCase(),
|
email: email.toLowerCase(),
|
||||||
password
|
password
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isLoginSuccessful && isLoginSuccessful.success) {
|
if (isLoginSuccessful && isLoginSuccessful.success) {
|
||||||
// case: login was successful
|
// case: login was successful
|
||||||
|
|
||||||
@@ -82,15 +84,14 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
|
|||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const userOrgs = await fetchOrganizations();
|
|
||||||
const userOrg = userOrgs[0] && userOrgs[0]._id;
|
await navigateUserToOrg(router);
|
||||||
|
|
||||||
// case: login does not require MFA step
|
// case: login does not require MFA step
|
||||||
createNotification({
|
createNotification({
|
||||||
text: "Successfully logged in",
|
text: "Successfully logged in",
|
||||||
type: "success"
|
type: "success"
|
||||||
});
|
});
|
||||||
router.push(`/org/${userOrg}/overview`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ import attemptLoginMfa from "@app/components/utilities/attemptLoginMfa";
|
|||||||
import { Button } from "@app/components/v2";
|
import { Button } from "@app/components/v2";
|
||||||
import { useUpdateUserAuthMethods } from "@app/hooks/api";
|
import { useUpdateUserAuthMethods } from "@app/hooks/api";
|
||||||
import { useSendMfaToken } from "@app/hooks/api/auth";
|
import { useSendMfaToken } from "@app/hooks/api/auth";
|
||||||
import { fetchOrganizations } from "@app/hooks/api/organization/queries";
|
|
||||||
import { fetchUserDetails } from "@app/hooks/api/users/queries";
|
import { fetchUserDetails } from "@app/hooks/api/users/queries";
|
||||||
import { AuthMethod } from "@app/hooks/api/users/types";
|
import { AuthMethod } from "@app/hooks/api/users/types";
|
||||||
|
|
||||||
|
import { navigateUserToOrg } from "../../Login.utils";
|
||||||
|
|
||||||
// The style for the verification code input
|
// The style for the verification code input
|
||||||
const props = {
|
const props = {
|
||||||
@@ -127,8 +127,6 @@ export const MFAStep = ({
|
|||||||
|
|
||||||
if (isLoginSuccessful) {
|
if (isLoginSuccessful) {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
const userOrgs = await fetchOrganizations();
|
|
||||||
const userOrg = userOrgs[0] && userOrgs[0]._id;
|
|
||||||
|
|
||||||
// case: login does not require MFA step
|
// case: login does not require MFA step
|
||||||
createNotification({
|
createNotification({
|
||||||
@@ -144,7 +142,7 @@ export const MFAStep = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
router.push(`/org/${userOrg}/overview`);
|
await navigateUserToOrg(router);
|
||||||
} else {
|
} else {
|
||||||
createNotification({
|
createNotification({
|
||||||
text: "Failed to log in",
|
text: "Failed to log in",
|
||||||
|
|||||||
@@ -10,9 +10,10 @@ import attemptCliLogin from "@app/components/utilities/attemptCliLogin";
|
|||||||
import attemptLogin from "@app/components/utilities/attemptLogin";
|
import attemptLogin from "@app/components/utilities/attemptLogin";
|
||||||
import { Button, Input } from "@app/components/v2";
|
import { Button, Input } from "@app/components/v2";
|
||||||
import { useUpdateUserAuthMethods } from "@app/hooks/api";
|
import { useUpdateUserAuthMethods } from "@app/hooks/api";
|
||||||
import { fetchOrganizations } from "@app/hooks/api/organization/queries";
|
|
||||||
import { fetchUserDetails } from "@app/hooks/api/users/queries";
|
import { fetchUserDetails } from "@app/hooks/api/users/queries";
|
||||||
|
|
||||||
|
import { navigateUserToOrg } from "../../Login.utils";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
providerAuthToken: string;
|
providerAuthToken: string;
|
||||||
email: string;
|
email: string;
|
||||||
@@ -92,8 +93,6 @@ export const PasswordStep = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// case: login does not require MFA step
|
// case: login does not require MFA step
|
||||||
const userOrgs = await fetchOrganizations();
|
|
||||||
const userOrg = userOrgs[0]._id;
|
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
createNotification({
|
createNotification({
|
||||||
text: "Successfully logged in",
|
text: "Successfully logged in",
|
||||||
@@ -108,7 +107,7 @@ export const PasswordStep = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
router.push(`/org/${userOrg}/overview`);
|
await navigateUserToOrg(router);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -120,8 +119,6 @@ export const PasswordStep = ({
|
|||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
|
|||||||
@@ -0,0 +1,112 @@
|
|||||||
|
import { useEffect } from "react";
|
||||||
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
import { yupResolver } from "@hookform/resolvers/yup";
|
||||||
|
import * as yup from "yup";
|
||||||
|
|
||||||
|
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
FormControl,
|
||||||
|
Input,
|
||||||
|
Modal,
|
||||||
|
ModalContent} from "@app/components/v2";
|
||||||
|
import { useCreateOrg } from "@app/hooks/api";
|
||||||
|
import { usePopUp } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
|
const schema = yup.object({
|
||||||
|
name: yup.string().required("Organization name is required"),
|
||||||
|
}).required();
|
||||||
|
|
||||||
|
export type FormData = yup.InferType<typeof schema>;
|
||||||
|
|
||||||
|
export const NonePage = () => {
|
||||||
|
const { createNotification } = useNotificationContext();
|
||||||
|
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp([
|
||||||
|
"createOrg",
|
||||||
|
] as const);
|
||||||
|
|
||||||
|
const { mutateAsync } = useCreateOrg();
|
||||||
|
|
||||||
|
const {
|
||||||
|
control,
|
||||||
|
handleSubmit,
|
||||||
|
reset,
|
||||||
|
formState: { isSubmitting }
|
||||||
|
} = useForm<FormData>({
|
||||||
|
resolver: yupResolver(schema),
|
||||||
|
defaultValues: {
|
||||||
|
name: ""
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
handlePopUpOpen("createOrg");
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onFormSubmit = async ({ name }: FormData) => {
|
||||||
|
try {
|
||||||
|
|
||||||
|
const organization = await mutateAsync({
|
||||||
|
name
|
||||||
|
});
|
||||||
|
|
||||||
|
createNotification({
|
||||||
|
text: "Successfully created organization",
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
|
||||||
|
window.location.href = `/org/${organization._id}/overview`;
|
||||||
|
|
||||||
|
reset();
|
||||||
|
handlePopUpToggle("createOrg", false);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
createNotification({
|
||||||
|
text: "Failed to created organization",
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex justify-center bg-bunker-800 text-white w-full h-full">
|
||||||
|
<Modal
|
||||||
|
isOpen={popUp?.createOrg?.isOpen}
|
||||||
|
>
|
||||||
|
<ModalContent
|
||||||
|
title="Create Organization"
|
||||||
|
subTitle="Looks like you're not part of any organizations. Create one to start using Infisical"
|
||||||
|
>
|
||||||
|
<form onSubmit={handleSubmit(onFormSubmit)}>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue=""
|
||||||
|
name="name"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Name"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
{...field}
|
||||||
|
placeholder="Acme Corp"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
className=""
|
||||||
|
size="sm"
|
||||||
|
type="submit"
|
||||||
|
isLoading={isSubmitting}
|
||||||
|
isDisabled={isSubmitting}
|
||||||
|
>
|
||||||
|
Create
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { NonePage } from "./NonePage";
|
||||||
@@ -14,14 +14,14 @@ import {
|
|||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import timeSince from "@app/ee/utilities/timeSince";
|
import timeSince from "@app/ee/utilities/timeSince";
|
||||||
import getRisksByOrganization, {
|
import getRisksByOrganization, {
|
||||||
GitRisks
|
IGitRisks
|
||||||
} from "@app/pages/api/secret-scanning/getRisksByOrganization";
|
} from "@app/pages/api/secret-scanning/getRisksByOrganization";
|
||||||
|
|
||||||
import { RiskStatusSelection } from "./RiskStatusSelection";
|
import { RiskStatusSelection } from "./RiskStatusSelection";
|
||||||
|
|
||||||
export const SecretScanningLogsTable = () => {
|
export const SecretScanningLogsTable = () => {
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [gitRisks, setGitRisks] = useState<GitRisks[]>([]);
|
const [gitRisks, setGitRisks] = useState<IGitRisks[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchRisks = async () => {
|
const fetchRisks = async () => {
|
||||||
|
|||||||
+81
@@ -0,0 +1,81 @@
|
|||||||
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
|
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
DeleteActionModal
|
||||||
|
} from "@app/components/v2";
|
||||||
|
import { useOrganization, useUser } from "@app/context";
|
||||||
|
import {
|
||||||
|
useDeleteOrgById,
|
||||||
|
useGetOrgUsers
|
||||||
|
} from "@app/hooks/api";
|
||||||
|
import { usePopUp } from "@app/hooks/usePopUp";
|
||||||
|
import { navigateUserToOrg } from "@app/views/Login/Login.utils";
|
||||||
|
|
||||||
|
export const OrgDeleteSection = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
const { currentOrg } = useOrganization();
|
||||||
|
const { user } = useUser();
|
||||||
|
const { createNotification } = useNotificationContext();
|
||||||
|
const { data: members } = useGetOrgUsers(currentOrg?._id ?? "");
|
||||||
|
|
||||||
|
const membershipOrg = members?.find((member) => member.user._id === user._id);
|
||||||
|
|
||||||
|
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
||||||
|
"deleteOrg"
|
||||||
|
] as const);
|
||||||
|
|
||||||
|
const { mutateAsync, isLoading } = useDeleteOrgById();
|
||||||
|
|
||||||
|
const handleDeleteOrgSubmit = async () => {
|
||||||
|
try {
|
||||||
|
if (!currentOrg?._id) return;
|
||||||
|
|
||||||
|
await mutateAsync({
|
||||||
|
organizationId: currentOrg?._id
|
||||||
|
});
|
||||||
|
|
||||||
|
createNotification({
|
||||||
|
text: "Successfully deleted organization",
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
|
||||||
|
await navigateUserToOrg(router);
|
||||||
|
|
||||||
|
handlePopUpClose("deleteOrg");
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
createNotification({
|
||||||
|
text: "Failed to delete organization",
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-4 bg-mineshaft-900 rounded-lg border border-mineshaft-600 mb-6">
|
||||||
|
<p className="text-xl font-semibold text-mineshaft-100 mb-4">
|
||||||
|
Danger Zone
|
||||||
|
</p>
|
||||||
|
<Button
|
||||||
|
isLoading={isLoading}
|
||||||
|
colorSchema="danger"
|
||||||
|
variant="outline_bg"
|
||||||
|
type="submit"
|
||||||
|
onClick={() => handlePopUpOpen("deleteOrg")}
|
||||||
|
isDisabled={(membershipOrg && membershipOrg.role !== "admin")}
|
||||||
|
>
|
||||||
|
{`Delete ${currentOrg?.name}`}
|
||||||
|
</Button>
|
||||||
|
<DeleteActionModal
|
||||||
|
isOpen={popUp.deleteOrg.isOpen}
|
||||||
|
title="Are you sure want to delete this organization?"
|
||||||
|
subTitle={`Permanently remove ${currentOrg?.name} and all of its data. This action is not reversible, so please be careful.`}
|
||||||
|
onChange={(isOpen) => handlePopUpToggle("deleteOrg", isOpen)}
|
||||||
|
deleteKey="confirm"
|
||||||
|
onDeleteApproved={handleDeleteOrgSubmit}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { OrgDeleteSection } from "./OrgDeleteSection";
|
||||||
+13
@@ -1,11 +1,24 @@
|
|||||||
|
import { useOrganization, useUser } from "@app/context";
|
||||||
|
import { useGetOrgUsers } from "@app/hooks/api";
|
||||||
|
|
||||||
|
import { OrgDeleteSection } from "../OrgDeleteSection";
|
||||||
import { OrgIncidentContactsSection } from "../OrgIncidentContactsSection";
|
import { OrgIncidentContactsSection } from "../OrgIncidentContactsSection";
|
||||||
import { OrgNameChangeSection } from "../OrgNameChangeSection";
|
import { OrgNameChangeSection } from "../OrgNameChangeSection";
|
||||||
|
|
||||||
export const OrgGeneralTab = () => {
|
export const OrgGeneralTab = () => {
|
||||||
|
const { currentOrg } = useOrganization();
|
||||||
|
const { user } = useUser();
|
||||||
|
const { data: members } = useGetOrgUsers(currentOrg?._id ?? "");
|
||||||
|
|
||||||
|
const membershipOrg = members?.find((member) => member.user._id === user._id);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<OrgNameChangeSection />
|
<OrgNameChangeSection />
|
||||||
<OrgIncidentContactsSection />
|
<OrgIncidentContactsSection />
|
||||||
|
{(membershipOrg && membershipOrg.role === "admin") && (
|
||||||
|
<OrgDeleteSection />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ export const OrgIncidentContactsSection = () => {
|
|||||||
const permission = useOrgPermission();
|
const permission = useOrgPermission();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-4 bg-mineshaft-900 rounded-lg border border-mineshaft-600">
|
<div className="p-4 bg-mineshaft-900 mb-6 rounded-lg border border-mineshaft-600">
|
||||||
<div className="flex justify-between mb-4">
|
<div className="flex justify-between mb-4">
|
||||||
<p className="min-w-max text-xl font-semibold">{t("section.incident.incident-contacts")}</p>
|
<p className="min-w-max text-xl font-semibold">{t("section.incident.incident-contacts")}</p>
|
||||||
<OrgPermissionCan I={OrgPermissionActions.Create} a={OrgPermissionSubjects.IncidentAccount}>
|
<OrgPermissionCan I={OrgPermissionActions.Create} a={OrgPermissionSubjects.IncidentAccount}>
|
||||||
|
|||||||
+1
-1
@@ -53,7 +53,7 @@ export const OrgNameChangeSection = (): JSX.Element => {
|
|||||||
onSubmit={handleSubmit(onFormSubmit)}
|
onSubmit={handleSubmit(onFormSubmit)}
|
||||||
className="p-4 bg-mineshaft-900 mb-6 rounded-lg border border-mineshaft-600"
|
className="p-4 bg-mineshaft-900 mb-6 rounded-lg border border-mineshaft-600"
|
||||||
>
|
>
|
||||||
<p className="text-xl font-semibold text-mineshaft-100 mb-4">Organization name</p>
|
<p className="text-xl font-semibold text-mineshaft-100 mb-4">Name</p>
|
||||||
<div className="mb-2 max-w-md">
|
<div className="mb-2 max-w-md">
|
||||||
<Controller
|
<Controller
|
||||||
defaultValue=""
|
defaultValue=""
|
||||||
|
|||||||
+64
@@ -0,0 +1,64 @@
|
|||||||
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
|
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
DeleteActionModal
|
||||||
|
} from "@app/components/v2";
|
||||||
|
import { useDeleteUser } from "@app/hooks/api";
|
||||||
|
import { usePopUp } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
|
export const DeleteAccountSection = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
const { createNotification } = useNotificationContext();
|
||||||
|
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
||||||
|
"deleteAccount"
|
||||||
|
] as const);
|
||||||
|
|
||||||
|
const { mutateAsync: deleteUserMutateAsync, isLoading } = useDeleteUser();
|
||||||
|
|
||||||
|
const handleDeleteAccountSubmit = async () => {
|
||||||
|
try {
|
||||||
|
await deleteUserMutateAsync();
|
||||||
|
|
||||||
|
createNotification({
|
||||||
|
text: "Successfully deleted account",
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
|
||||||
|
router.push("/login");
|
||||||
|
handlePopUpClose("deleteAccount");
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
createNotification({
|
||||||
|
text: "Failed to delete account",
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-4 bg-mineshaft-900 rounded-lg border border-mineshaft-600 mb-6">
|
||||||
|
<p className="text-xl font-semibold text-mineshaft-100 mb-4">
|
||||||
|
Danger Zone
|
||||||
|
</p>
|
||||||
|
<Button
|
||||||
|
isLoading={isLoading}
|
||||||
|
colorSchema="danger"
|
||||||
|
variant="outline_bg"
|
||||||
|
type="submit"
|
||||||
|
onClick={() => handlePopUpOpen("deleteAccount")}
|
||||||
|
>
|
||||||
|
Delete my account
|
||||||
|
</Button>
|
||||||
|
<DeleteActionModal
|
||||||
|
isOpen={popUp.deleteAccount.isOpen}
|
||||||
|
title="Are you sure want to delete your account?"
|
||||||
|
subTitle="Permanently remove this account and all of its data. This action is not reversible, so please be careful."
|
||||||
|
onChange={(isOpen) => handlePopUpToggle("deleteAccount", isOpen)}
|
||||||
|
deleteKey="confirm"
|
||||||
|
onDeleteApproved={handleDeleteAccountSubmit}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { DeleteAccountSection } from "./DeleteAccountSection";
|
||||||
+2
@@ -1,4 +1,5 @@
|
|||||||
import { ChangeLanguageSection } from "../ChangeLanguageSection";
|
import { ChangeLanguageSection } from "../ChangeLanguageSection";
|
||||||
|
import { DeleteAccountSection } from "../DeleteAccountSection";
|
||||||
import { EmergencyKitSection } from "../EmergencyKitSection";
|
import { EmergencyKitSection } from "../EmergencyKitSection";
|
||||||
import { SessionsSection } from "../SessionsSection";
|
import { SessionsSection } from "../SessionsSection";
|
||||||
import { UserNameSection } from "../UserNameSection";
|
import { UserNameSection } from "../UserNameSection";
|
||||||
@@ -10,6 +11,7 @@ export const PersonalGeneralTab = () => {
|
|||||||
<ChangeLanguageSection />
|
<ChangeLanguageSection />
|
||||||
<SessionsSection />
|
<SessionsSection />
|
||||||
<EmergencyKitSection />
|
<EmergencyKitSection />
|
||||||
|
<DeleteAccountSection />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
+50
-53
@@ -1,10 +1,11 @@
|
|||||||
import { useState } from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
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 { Button, FormControl, Input } from "@app/components/v2";
|
import {
|
||||||
|
Button,
|
||||||
|
DeleteActionModal,
|
||||||
|
} from "@app/components/v2";
|
||||||
import {
|
import {
|
||||||
ProjectPermissionActions,
|
ProjectPermissionActions,
|
||||||
ProjectPermissionSub,
|
ProjectPermissionSub,
|
||||||
@@ -13,78 +14,74 @@ import {
|
|||||||
} from "@app/context";
|
} from "@app/context";
|
||||||
import { useToggle } from "@app/hooks";
|
import { useToggle } from "@app/hooks";
|
||||||
import { useDeleteWorkspace } from "@app/hooks/api";
|
import { useDeleteWorkspace } from "@app/hooks/api";
|
||||||
|
import { usePopUp } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
export const DeleteProjectSection = () => {
|
export const DeleteProjectSection = () => {
|
||||||
const { t } = useTranslation();
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { createNotification } = useNotificationContext();
|
const { createNotification } = useNotificationContext();
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
||||||
const { currentOrg } = useOrganization();
|
"deleteWorkspace"
|
||||||
const [isDeleting, setIsDeleting] = useToggle();
|
] as const);
|
||||||
const [deleteProjectInput, setDeleteProjectInput] = useState("");
|
|
||||||
const deleteWorkspace = useDeleteWorkspace();
|
|
||||||
|
|
||||||
const onDeleteWorkspace = async () => {
|
const { currentOrg } = useOrganization();
|
||||||
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
const [isDeleting, setIsDeleting] = useToggle();
|
||||||
|
const deleteWorkspace = useDeleteWorkspace();
|
||||||
|
|
||||||
|
const handleDeleteWorkspaceSubmit = async () => {
|
||||||
setIsDeleting.on();
|
setIsDeleting.on();
|
||||||
try {
|
try {
|
||||||
if (!currentWorkspace?._id) return;
|
if (!currentWorkspace?._id) return;
|
||||||
|
|
||||||
await deleteWorkspace.mutateAsync({
|
await deleteWorkspace.mutateAsync({
|
||||||
workspaceID: currentWorkspace?._id
|
workspaceID: currentWorkspace?._id
|
||||||
});
|
});
|
||||||
// redirect user to the org overview
|
|
||||||
router.push(`/org/${currentOrg?._id}/overview`);
|
|
||||||
|
|
||||||
createNotification({
|
createNotification({
|
||||||
text: "Successfully deleted workspace",
|
text: "Successfully deleted project",
|
||||||
type: "success"
|
type: "success"
|
||||||
});
|
});
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
router.push(`/org/${currentOrg?._id}/overview`);
|
||||||
|
handlePopUpClose("deleteWorkspace");
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
createNotification({
|
createNotification({
|
||||||
text: "Failed to delete workspace",
|
text: "Failed to delete project",
|
||||||
type: "error"
|
type: "error"
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setIsDeleting.off();
|
setIsDeleting.off();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-6 p-4 bg-mineshaft-900 rounded-lg border border-red">
|
<div className="p-4 bg-mineshaft-900 rounded-lg border border-mineshaft-600 mb-6">
|
||||||
<p className="mb-3 text-xl font-semibold text-red">{t("settings.project.danger-zone")}</p>
|
<p className="text-xl font-semibold text-mineshaft-100 mb-4">
|
||||||
<p className="text-gray-400 mb-8">{t("settings.project.danger-zone-note")}</p>
|
Danger Zone
|
||||||
<div className="mr-auto mt-4 max-h-28 w-full max-w-md">
|
</p>
|
||||||
<FormControl
|
<ProjectPermissionCan I={ProjectPermissionActions.Delete} a={ProjectPermissionSub.Workspace}>
|
||||||
label={
|
{(isAllowed) => (
|
||||||
<div className="mb-0.5 text-sm font-normal text-gray-400">
|
<Button
|
||||||
Type <span className="font-bold">{currentWorkspace?.name}</span> to delete the
|
isLoading={isDeleting}
|
||||||
workspace
|
isDisabled={!isAllowed || isDeleting}
|
||||||
</div>
|
colorSchema="danger"
|
||||||
}
|
variant="outline_bg"
|
||||||
>
|
type="submit"
|
||||||
<Input
|
onClick={() => handlePopUpOpen("deleteWorkspace")}
|
||||||
onChange={(e) => setDeleteProjectInput(e.target.value)}
|
>
|
||||||
value={deleteProjectInput}
|
{`Delete ${currentWorkspace?.name}`}
|
||||||
placeholder="Type the project name to delete"
|
</Button>
|
||||||
className="bg-mineshaft-800"
|
)}
|
||||||
/>
|
</ProjectPermissionCan>
|
||||||
</FormControl>
|
<DeleteActionModal
|
||||||
</div>
|
isOpen={popUp.deleteWorkspace.isOpen}
|
||||||
<ProjectPermissionCan I={ProjectPermissionActions.Delete} a={ProjectPermissionSub.Workspace}>
|
title="Are you sure want to delete this project?"
|
||||||
{(isAllowed) => (
|
subTitle={`Permanently remove ${currentWorkspace?.name} and all of its data. This action is not reversible, so please be careful.`}
|
||||||
<Button
|
onChange={(isOpen) => handlePopUpToggle("deleteWorkspace", isOpen)}
|
||||||
colorSchema="danger"
|
deleteKey="confirm"
|
||||||
onClick={onDeleteWorkspace}
|
onDeleteApproved={handleDeleteWorkspaceSubmit}
|
||||||
isDisabled={!isAllowed || deleteProjectInput !== currentWorkspace?.name || isDeleting}
|
/>
|
||||||
isLoading={isDeleting}
|
|
||||||
>
|
|
||||||
{t("settings.project.delete-project")}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</ProjectPermissionCan>
|
|
||||||
<p className="mt-3 ml-0.5 text-xs text-gray-500">
|
|
||||||
{t("settings.project.delete-project-note")}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user