feat(rbac): added new zod validation and permission check for all org level api

This commit is contained in:
Akhil Mohan
2023-09-08 21:20:29 +05:30
parent 96437fd1b7
commit e342e88499
27 changed files with 1870 additions and 1593 deletions
@@ -3,228 +3,503 @@ import { Request, Response } from "express";
import { getLicenseServerUrl } from "../../../config";
import { licenseServerKeyRequest } from "../../../config/request";
import { EELicenseService } from "../../services";
import { validateRequest } from "../../../helpers/validation";
import * as reqValidator from "../../../validation/organization";
import {
GeneralPermissionActions,
OrgPermissionSubjects,
getUserOrgPermissions
} from "../../../services/RoleService";
import { ForbiddenError } from "@casl/ability";
import { Organization } from "../../../models";
import { OrganizationNotFoundError } from "../../../utils/errors";
export const getOrganizationPlansTable = async (req: Request, res: Response) => {
const billingCycle = req.query.billingCycle as string;
const { data } = await licenseServerKeyRequest.get(
`${await getLicenseServerUrl()}/api/license-server/v1/cloud-products?billing-cycle=${billingCycle}`
);
const {
query: { billingCycle },
params: { organizationId }
} = await validateRequest(reqValidator.GetOrgPlansTablev1, req);
return res.status(200).send(data);
}
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
ForbiddenError.from(permission).throwUnlessCan(
GeneralPermissionActions.Read,
OrgPermissionSubjects.Billing
);
const { data } = await licenseServerKeyRequest.get(
`${await getLicenseServerUrl()}/api/license-server/v1/cloud-products?billing-cycle=${billingCycle}`
);
return res.status(200).send(data);
};
/**
* Return the organization current plan's feature set
*/
export const getOrganizationPlan = async (req: Request, res: Response) => {
const { organizationId } = req.params;
const workspaceId = req.query.workspaceId as string;
const {
query: { workspaceId },
params: { organizationId }
} = await validateRequest(reqValidator.GetOrgPlanv1, req);
const plan = await EELicenseService.getPlan(new Types.ObjectId(organizationId), new Types.ObjectId(workspaceId));
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
ForbiddenError.from(permission).throwUnlessCan(
GeneralPermissionActions.Read,
OrgPermissionSubjects.Billing
);
return res.status(200).send({
plan,
});
}
const plan = await EELicenseService.getPlan(
new Types.ObjectId(organizationId),
new Types.ObjectId(workspaceId)
);
return res.status(200).send({
plan
});
};
/**
* Return checkout url for pro trial
* @param req
* @param res
* @returns
* @param req
* @param res
* @returns
*/
export const startOrganizationTrial = async (req: Request, res: Response) => {
const { organizationId } = req.params;
const { success_url } = req.body;
const {
params: { organizationId },
body: { success_url }
} = await validateRequest(reqValidator.StartOrgTrailv1, req);
const { data: { url } } = await licenseServerKeyRequest.post(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/session/trial`,
{
success_url
}
);
EELicenseService.delPlan(new Types.ObjectId(organizationId));
return res.status(200).send({
url
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
ForbiddenError.from(permission).throwUnlessCan(
GeneralPermissionActions.Create,
OrgPermissionSubjects.Billing
);
ForbiddenError.from(permission).throwUnlessCan(
GeneralPermissionActions.Edit,
OrgPermissionSubjects.Billing
);
const organization = await Organization.findById(organizationId);
if (!organization) {
throw OrganizationNotFoundError({
message: "Failed to find organization"
});
}
}
const {
data: { url }
} = await licenseServerKeyRequest.post(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${
organization.customerId
}/session/trial`,
{
success_url
}
);
EELicenseService.delPlan(new Types.ObjectId(organizationId));
return res.status(200).send({
url
});
};
/**
* Return the organization's current plan's billing info
* @param req
* @param res
* @returns
* @param req
* @param res
* @returns
*/
export const getOrganizationPlanBillingInfo = async (req: Request, res: Response) => {
const { data } = await licenseServerKeyRequest.get(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/cloud-plan/billing`
);
return res.status(200).send(data);
}
const {
params: { organizationId }
} = await validateRequest(reqValidator.GetOrgPlanBillingInfov1, req);
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
ForbiddenError.from(permission).throwUnlessCan(
GeneralPermissionActions.Read,
OrgPermissionSubjects.Billing
);
const organization = await Organization.findById(organizationId);
if (!organization) {
throw OrganizationNotFoundError({
message: "Failed to find organization"
});
}
const { data } = await licenseServerKeyRequest.get(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${
organization.customerId
}/cloud-plan/billing`
);
return res.status(200).send(data);
};
/**
* Return the organization's current plan's feature table
* @param req
* @param res
* @returns
* @param req
* @param res
* @returns
*/
export const getOrganizationPlanTable = async (req: Request, res: Response) => {
const { data } = await licenseServerKeyRequest.get(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/cloud-plan/table`
);
const {
params: { organizationId }
} = await validateRequest(reqValidator.GetOrgPlanTablev1, req);
return res.status(200).send(data);
}
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
ForbiddenError.from(permission).throwUnlessCan(
GeneralPermissionActions.Read,
OrgPermissionSubjects.Billing
);
const organization = await Organization.findById(organizationId);
if (!organization) {
throw OrganizationNotFoundError({
message: "Failed to find organization"
});
}
const { data } = await licenseServerKeyRequest.get(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${
organization.customerId
}/cloud-plan/table`
);
return res.status(200).send(data);
};
export const getOrganizationBillingDetails = async (req: Request, res: Response) => {
const { data } = await licenseServerKeyRequest.get(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/billing-details`
);
const {
params: { organizationId }
} = await validateRequest(reqValidator.GetOrgBillingDetailsv1, req);
return res.status(200).send(data);
}
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
ForbiddenError.from(permission).throwUnlessCan(
GeneralPermissionActions.Read,
OrgPermissionSubjects.Billing
);
const organization = await Organization.findById(organizationId);
if (!organization) {
throw OrganizationNotFoundError({
message: "Failed to find organization"
});
}
const { data } = await licenseServerKeyRequest.get(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${
organization.customerId
}/billing-details`
);
return res.status(200).send(data);
};
export const updateOrganizationBillingDetails = async (req: Request, res: Response) => {
const {
name,
email
} = req.body;
const {
params: { organizationId },
body: { name, email }
} = await validateRequest(reqValidator.UpdateOrgBillingDetailsv1, req);
const { data } = await licenseServerKeyRequest.patch(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/billing-details`,
{
...(name ? { name } : {}),
...(email ? { email } : {})
}
);
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
ForbiddenError.from(permission).throwUnlessCan(
GeneralPermissionActions.Edit,
OrgPermissionSubjects.Billing
);
return res.status(200).send(data);
}
const organization = await Organization.findById(organizationId);
if (!organization) {
throw OrganizationNotFoundError({
message: "Failed to find organization"
});
}
const { data } = await licenseServerKeyRequest.patch(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${
organization.customerId
}/billing-details`,
{
...(name ? { name } : {}),
...(email ? { email } : {})
}
);
return res.status(200).send(data);
};
/**
* Return the organization's payment methods on file
*/
export const getOrganizationPmtMethods = async (req: Request, res: Response) => {
const { data: { pmtMethods } } = await licenseServerKeyRequest.get(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/billing-details/payment-methods`
);
const {
params: { organizationId }
} = await validateRequest(reqValidator.GetOrgPmtMethodsv1, req);
return res.status(200).send(pmtMethods);
}
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
ForbiddenError.from(permission).throwUnlessCan(
GeneralPermissionActions.Read,
OrgPermissionSubjects.Billing
);
const organization = await Organization.findById(organizationId);
if (!organization) {
throw OrganizationNotFoundError({
message: "Failed to find organization"
});
}
const {
data: { pmtMethods }
} = await licenseServerKeyRequest.get(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${
organization.customerId
}/billing-details/payment-methods`
);
return res.status(200).send(pmtMethods);
};
/**
* Return URL to add payment method for organization
*/
export const addOrganizationPmtMethod = async (req: Request, res: Response) => {
const {
success_url,
cancel_url,
} = req.body;
const { data: { url } } = await licenseServerKeyRequest.post(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/billing-details/payment-methods`,
{
success_url,
cancel_url,
}
);
return res.status(200).send({
url,
});
}
const {
params: { organizationId },
body: { success_url, cancel_url }
} = await validateRequest(reqValidator.CreateOrgPmtMethodv1, req);
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
ForbiddenError.from(permission).throwUnlessCan(
GeneralPermissionActions.Create,
OrgPermissionSubjects.Billing
);
const organization = await Organization.findById(organizationId);
if (!organization) {
throw OrganizationNotFoundError({
message: "Failed to find organization"
});
}
const {
data: { url }
} = await licenseServerKeyRequest.post(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${
organization.customerId
}/billing-details/payment-methods`,
{
success_url,
cancel_url
}
);
return res.status(200).send({
url
});
};
/**
* Delete payment method with id [pmtMethodId] for organization
* @param req
* @param res
* @returns
* @param req
* @param res
* @returns
*/
export const deleteOrganizationPmtMethod = async (req: Request, res: Response) => {
const { pmtMethodId } = req.params;
const {
params: { organizationId, pmtMethodId }
} = await validateRequest(reqValidator.DelOrgPmtMethodv1, req);
const { data } = await licenseServerKeyRequest.delete(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/billing-details/payment-methods/${pmtMethodId}`,
);
return res.status(200).send(data);
}
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
ForbiddenError.from(permission).throwUnlessCan(
GeneralPermissionActions.Delete,
OrgPermissionSubjects.Billing
);
const organization = await Organization.findById(organizationId);
if (!organization) {
throw OrganizationNotFoundError({
message: "Failed to find organization"
});
}
const { data } = await licenseServerKeyRequest.delete(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${
organization.customerId
}/billing-details/payment-methods/${pmtMethodId}`
);
return res.status(200).send(data);
};
/**
* Return the organization's tax ids on file
*/
export const getOrganizationTaxIds = async (req: Request, res: Response) => {
const { data: { tax_ids } } = await licenseServerKeyRequest.get(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/billing-details/tax-ids`
);
const {
params: { organizationId }
} = await validateRequest(reqValidator.GetOrgTaxIdsv1, req);
return res.status(200).send(tax_ids);
}
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
ForbiddenError.from(permission).throwUnlessCan(
GeneralPermissionActions.Read,
OrgPermissionSubjects.Billing
);
const organization = await Organization.findById(organizationId);
if (!organization) {
throw OrganizationNotFoundError({
message: "Failed to find organization"
});
}
const {
data: { tax_ids }
} = await licenseServerKeyRequest.get(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${
organization.customerId
}/billing-details/tax-ids`
);
return res.status(200).send(tax_ids);
};
/**
* Add tax id to organization
*/
export const addOrganizationTaxId = async (req: Request, res: Response) => {
const {
type,
value
} = req.body;
const {
params: { organizationId },
body: { type, value }
} = await validateRequest(reqValidator.CreateOrgTaxId, req);
const { data } = await licenseServerKeyRequest.post(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/billing-details/tax-ids`,
{
type,
value
}
);
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
ForbiddenError.from(permission).throwUnlessCan(
GeneralPermissionActions.Create,
OrgPermissionSubjects.Billing
);
return res.status(200).send(data);
}
const organization = await Organization.findById(organizationId);
if (!organization) {
throw OrganizationNotFoundError({
message: "Failed to find organization"
});
}
const { data } = await licenseServerKeyRequest.post(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${
organization.customerId
}/billing-details/tax-ids`,
{
type,
value
}
);
return res.status(200).send(data);
};
/**
* Delete tax id with id [taxId] from organization tax ids on file
* @param req
* @param res
* @returns
* @param req
* @param res
* @returns
*/
export const deleteOrganizationTaxId = async (req: Request, res: Response) => {
const { taxId } = req.params;
const {
params: { organizationId, taxId }
} = await validateRequest(reqValidator.DelOrgTaxIdv1, req);
const { data } = await licenseServerKeyRequest.delete(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/billing-details/tax-ids/${taxId}`,
);
return res.status(200).send(data);
}
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
ForbiddenError.from(permission).throwUnlessCan(
GeneralPermissionActions.Delete,
OrgPermissionSubjects.Billing
);
const organization = await Organization.findById(organizationId);
if (!organization) {
throw OrganizationNotFoundError({
message: "Failed to find organization"
});
}
const { data } = await licenseServerKeyRequest.delete(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${
organization.customerId
}/billing-details/tax-ids/${taxId}`
);
return res.status(200).send(data);
};
/**
* Return organization's invoices on file
* @param req
* @param res
* @returns
* @param req
* @param res
* @returns
*/
export const getOrganizationInvoices = async (req: Request, res: Response) => {
const { data: { invoices } } = await licenseServerKeyRequest.get(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/invoices`
);
const {
params: { organizationId }
} = await validateRequest(reqValidator.GetOrgInvoicesv1, req);
return res.status(200).send(invoices);
}
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
ForbiddenError.from(permission).throwUnlessCan(
GeneralPermissionActions.Read,
OrgPermissionSubjects.Billing
);
const organization = await Organization.findById(organizationId);
if (!organization) {
throw OrganizationNotFoundError({
message: "Failed to find organization"
});
}
const {
data: { invoices }
} = await licenseServerKeyRequest.get(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${
organization.customerId
}/invoices`
);
return res.status(200).send(invoices);
};
/**
* Return organization's licenses on file
* @param req
* @param res
* @returns
* @param req
* @param res
* @returns
*/
export const getOrganizationLicenses = async (req: Request, res: Response) => {
const { data: { licenses } } = await licenseServerKeyRequest.get(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/licenses`
);
const {
params: { organizationId }
} = await validateRequest(reqValidator.GetOrgLicencesv1, req);
return res.status(200).send(licenses);
}
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
ForbiddenError.from(permission).throwUnlessCan(
GeneralPermissionActions.Read,
OrgPermissionSubjects.Billing
);
const organization = await Organization.findById(organizationId);
if (!organization) {
throw OrganizationNotFoundError({
message: "Failed to find organization"
});
}
const {
data: { licenses }
} = await licenseServerKeyRequest.get(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${
organization.customerId
}/licenses`
);
return res.status(200).send(licenses);
};
+211 -192
View File
@@ -2,239 +2,258 @@ import { Request, Response } from "express";
import { Types } from "mongoose";
import { BotOrgService } from "../../../services";
import { SSOConfig } from "../../models";
import {
AuthMethod,
MembershipOrg,
User
} from "../../../models";
import { AuthMethod, MembershipOrg, User } from "../../../models";
import { getSSOConfigHelper } from "../../helpers/organizations";
import { client } from "../../../config";
import { ResourceNotFoundError } from "../../../utils/errors";
import { getSiteURL } from "../../../config";
import { EELicenseService } from "../../services";
import * as reqValidator from "../../../validation/sso";
import { validateRequest } from "../../../helpers/validation";
import {
GeneralPermissionActions,
OrgPermissionSubjects,
getUserOrgPermissions
} from "../../../services/RoleService";
import { ForbiddenError } from "@casl/ability";
/**
* Redirect user to appropriate SSO endpoint after successful authentication
* to finish inputting their master key for logging in or signing up
* @param req
* @param res
* @returns
* @param req
* @param res
* @returns
*/
export const redirectSSO = async (req: Request, res: Response) => {
if (req.isUserCompleted) {
return res.redirect(`${await getSiteURL()}/login/sso?token=${encodeURIComponent(req.providerAuthToken)}`);
}
return res.redirect(`${await getSiteURL()}/signup/sso?token=${encodeURIComponent(req.providerAuthToken)}`);
}
if (req.isUserCompleted) {
return res.redirect(
`${await getSiteURL()}/login/sso?token=${encodeURIComponent(req.providerAuthToken)}`
);
}
return res.redirect(
`${await getSiteURL()}/signup/sso?token=${encodeURIComponent(req.providerAuthToken)}`
);
};
/**
* Return organization SAML SSO configuration
* @param req
* @param res
* @returns
* @param req
* @param res
* @returns
*/
export const getSSOConfig = async (req: Request, res: Response) => {
const organizationId = req.query.organizationId as string;
const data = await getSSOConfigHelper({
organizationId: new Types.ObjectId(organizationId)
});
const {
query: { organizationId }
} = await validateRequest(reqValidator.GetSsoConfigv1, req);
return res.status(200).send(data);
}
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
ForbiddenError.from(permission).throwUnlessCan(
GeneralPermissionActions.Read,
OrgPermissionSubjects.Sso
);
const data = await getSSOConfigHelper({
organizationId: new Types.ObjectId(organizationId)
});
return res.status(200).send(data);
};
/**
* Update organization SAML SSO configuration
* @param req
* @param res
* @returns
* @param req
* @param res
* @returns
*/
export const updateSSOConfig = async (req: Request, res: Response) => {
const {
body: { organizationId, authProvider, isActive, entryPoint, issuer, cert }
} = await validateRequest(reqValidator.UpdateSsoConfigv1, req);
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
ForbiddenError.from(permission).throwUnlessCan(
GeneralPermissionActions.Edit,
OrgPermissionSubjects.Sso
);
const plan = await EELicenseService.getPlan(new Types.ObjectId(organizationId));
if (!plan.samlSSO)
return res.status(400).send({
message:
"Failed to update SAML SSO configuration due to plan restriction. Upgrade plan to update SSO configuration."
});
interface PatchUpdate {
authProvider?: string;
isActive?: boolean;
encryptedEntryPoint?: string;
entryPointIV?: string;
entryPointTag?: string;
encryptedIssuer?: string;
issuerIV?: string;
issuerTag?: string;
encryptedCert?: string;
certIV?: string;
certTag?: string;
}
const update: PatchUpdate = {};
if (authProvider) {
update.authProvider = authProvider;
}
if (isActive !== undefined) {
update.isActive = isActive;
}
const key = await BotOrgService.getSymmetricKey(new Types.ObjectId(organizationId));
if (entryPoint) {
const {
organizationId,
authProvider,
isActive,
entryPoint,
issuer,
cert,
} = req.body;
ciphertext: encryptedEntryPoint,
iv: entryPointIV,
tag: entryPointTag
} = client.encryptSymmetric(entryPoint, key);
const plan = await EELicenseService.getPlan(new Types.ObjectId(organizationId));
if (!plan.samlSSO) return res.status(400).send({
message: "Failed to update SAML SSO configuration due to plan restriction. Upgrade plan to update SSO configuration."
update.encryptedEntryPoint = encryptedEntryPoint;
update.entryPointIV = entryPointIV;
update.entryPointTag = entryPointTag;
}
if (issuer) {
const {
ciphertext: encryptedIssuer,
iv: issuerIV,
tag: issuerTag
} = client.encryptSymmetric(issuer, key);
update.encryptedIssuer = encryptedIssuer;
update.issuerIV = issuerIV;
update.issuerTag = issuerTag;
}
if (cert) {
const {
ciphertext: encryptedCert,
iv: certIV,
tag: certTag
} = client.encryptSymmetric(cert, key);
update.encryptedCert = encryptedCert;
update.certIV = certIV;
update.certTag = certTag;
}
const ssoConfig = await SSOConfig.findOneAndUpdate(
{
organization: new Types.ObjectId(organizationId)
},
update,
{
new: true
}
);
if (!ssoConfig)
throw ResourceNotFoundError({
message: "Failed to find SSO config to update"
});
interface PatchUpdate {
authProvider?: string;
isActive?: boolean;
encryptedEntryPoint?: string;
entryPointIV?: string;
entryPointTag?: string;
encryptedIssuer?: string;
issuerIV?: string;
issuerTag?: string;
encryptedCert?: string;
certIV?: string;
certTag?: string;
}
const update: PatchUpdate = {};
if (authProvider) {
update.authProvider = authProvider;
}
if (isActive !== undefined) {
update.isActive = isActive;
}
const key = await BotOrgService.getSymmetricKey(
new Types.ObjectId(organizationId)
);
if (entryPoint) {
const {
ciphertext: encryptedEntryPoint,
iv: entryPointIV,
tag: entryPointTag
} = client.encryptSymmetric(entryPoint, key);
update.encryptedEntryPoint = encryptedEntryPoint;
update.entryPointIV = entryPointIV;
update.entryPointTag = entryPointTag;
}
if (issuer) {
const {
ciphertext: encryptedIssuer,
iv: issuerIV,
tag: issuerTag
} = client.encryptSymmetric(issuer, key);
update.encryptedIssuer = encryptedIssuer;
update.issuerIV = issuerIV;
update.issuerTag = issuerTag;
}
if (update.isActive !== undefined) {
const membershipOrgs = await MembershipOrg.find({
organization: new Types.ObjectId(organizationId)
}).select("user");
if (cert) {
const {
ciphertext: encryptedCert,
iv: certIV,
tag: certTag
} = client.encryptSymmetric(cert, key);
update.encryptedCert = encryptedCert;
update.certIV = certIV;
update.certTag = certTag;
}
const ssoConfig = await SSOConfig.findOneAndUpdate(
if (update.isActive) {
await User.updateMany(
{
organization: new Types.ObjectId(organizationId)
_id: {
$in: membershipOrgs.map((membershipOrg) => membershipOrg.user)
}
},
update,
{
new: true
authMethods: [ssoConfig.authProvider]
}
);
if (!ssoConfig) throw ResourceNotFoundError({
message: "Failed to find SSO config to update"
});
if (update.isActive !== undefined) {
const membershipOrgs = await MembershipOrg.find({
organization: new Types.ObjectId(organizationId)
}).select("user");
if (update.isActive) {
await User.updateMany(
{
_id: {
$in: membershipOrgs.map((membershipOrg) => membershipOrg.user)
}
},
{
authMethods: [ssoConfig.authProvider],
}
);
} else {
await User.updateMany(
{
_id: {
$in: membershipOrgs.map((membershipOrg) => membershipOrg.user)
}
},
{
authMethods: [AuthMethod.EMAIL],
}
);
);
} else {
await User.updateMany(
{
_id: {
$in: membershipOrgs.map((membershipOrg) => membershipOrg.user)
}
},
{
authMethods: [AuthMethod.EMAIL]
}
);
}
return res.status(200).send(ssoConfig);
}
}
return res.status(200).send(ssoConfig);
};
/**
* Create organization SAML SSO configuration
* @param req
* @param res
* @returns
* @param req
* @param res
* @returns
*/
export const createSSOConfig = async (req: Request, res: Response) => {
const {
organizationId,
authProvider,
isActive,
entryPoint,
issuer,
cert
} = req.body;
const {
body: { organizationId, authProvider, isActive, entryPoint, issuer, cert }
} = await validateRequest(reqValidator.CreateSsoConfigv1, req);
const plan = await EELicenseService.getPlan(new Types.ObjectId(organizationId));
if (!plan.samlSSO) return res.status(400).send({
message: "Failed to create SAML SSO configuration due to plan restriction. Upgrade plan to add SSO configuration."
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
ForbiddenError.from(permission).throwUnlessCan(
GeneralPermissionActions.Create,
OrgPermissionSubjects.Sso
);
const plan = await EELicenseService.getPlan(new Types.ObjectId(organizationId));
if (!plan.samlSSO)
return res.status(400).send({
message:
"Failed to create SAML SSO configuration due to plan restriction. Upgrade plan to add SSO configuration."
});
const key = await BotOrgService.getSymmetricKey(
new Types.ObjectId(organizationId)
);
const {
ciphertext: encryptedEntryPoint,
iv: entryPointIV,
tag: entryPointTag
} = client.encryptSymmetric(entryPoint, key);
const key = await BotOrgService.getSymmetricKey(new Types.ObjectId(organizationId));
const {
ciphertext: encryptedIssuer,
iv: issuerIV,
tag: issuerTag
} = client.encryptSymmetric(issuer, key);
const {
ciphertext: encryptedEntryPoint,
iv: entryPointIV,
tag: entryPointTag
} = client.encryptSymmetric(entryPoint, key);
const {
ciphertext: encryptedCert,
iv: certIV,
tag: certTag
} = client.encryptSymmetric(cert, key);
const ssoConfig = await new SSOConfig({
organization: new Types.ObjectId(organizationId),
authProvider,
isActive,
encryptedEntryPoint,
entryPointIV,
entryPointTag,
encryptedIssuer,
issuerIV,
issuerTag,
encryptedCert,
certIV,
certTag
}).save();
const {
ciphertext: encryptedIssuer,
iv: issuerIV,
tag: issuerTag
} = client.encryptSymmetric(issuer, key);
return res.status(200).send(ssoConfig);
}
const {
ciphertext: encryptedCert,
iv: certIV,
tag: certTag
} = client.encryptSymmetric(cert, key);
const ssoConfig = await new SSOConfig({
organization: new Types.ObjectId(organizationId),
authProvider,
isActive,
encryptedEntryPoint,
entryPointIV,
entryPointTag,
encryptedIssuer,
issuerIV,
issuerTag,
encryptedCert,
certIV,
certTag
}).save();
return res.status(200).send(ssoConfig);
};
+78 -188
View File
@@ -1,237 +1,127 @@
import express from "express";
const router = express.Router();
import {
requireAuth,
requireOrganizationAuth,
validateRequest,
} from "../../../middleware";
import { body, param, query } from "express-validator";
import { requireAuth } from "../../../middleware";
import { organizationsController } from "../../controllers/v1";
import {
ACCEPTED, ADMIN, AuthMode, MEMBER, OWNER
} from "../../../variables";
import { AuthMode } from "../../../variables";
router.get(
"/:organizationId/plans/table",
requireAuth({
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED],
}),
param("organizationId").exists().trim(),
query("billingCycle").exists().isString().isIn(["monthly", "yearly"]),
validateRequest,
organizationsController.getOrganizationPlansTable
"/:organizationId/plans/table",
requireAuth({
acceptedAuthModes: [AuthMode.JWT]
}),
organizationsController.getOrganizationPlansTable
);
router.get(
"/:organizationId/plan",
requireAuth({
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED],
}),
param("organizationId").exists().trim(),
query("workspaceId").optional().isString(),
validateRequest,
organizationsController.getOrganizationPlan
"/:organizationId/plan",
requireAuth({
acceptedAuthModes: [AuthMode.JWT]
}),
organizationsController.getOrganizationPlan
);
router.post(
"/:organizationId/session/trial",
requireAuth({
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED],
}),
param("organizationId").exists().trim(),
body("success_url").exists().trim(),
validateRequest,
organizationsController.startOrganizationTrial
"/:organizationId/session/trial",
requireAuth({
acceptedAuthModes: [AuthMode.JWT]
}),
organizationsController.startOrganizationTrial
);
router.get(
"/:organizationId/plan/billing",
requireAuth({
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED],
}),
param("organizationId").exists().trim(),
query("workspaceId").optional().isString(),
validateRequest,
organizationsController.getOrganizationPlanBillingInfo
"/:organizationId/plan/billing",
requireAuth({
acceptedAuthModes: [AuthMode.JWT]
}),
organizationsController.getOrganizationPlanBillingInfo
);
router.get(
"/:organizationId/plan/table",
requireAuth({
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED],
}),
param("organizationId").exists().trim(),
query("workspaceId").optional().isString(),
validateRequest,
organizationsController.getOrganizationPlanTable
"/:organizationId/plan/table",
requireAuth({
acceptedAuthModes: [AuthMode.JWT]
}),
organizationsController.getOrganizationPlanTable
);
router.get(
"/:organizationId/billing-details",
requireAuth({
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED],
}),
param("organizationId").exists().trim(),
validateRequest,
organizationsController.getOrganizationBillingDetails
"/:organizationId/billing-details",
requireAuth({
acceptedAuthModes: [AuthMode.JWT]
}),
organizationsController.getOrganizationBillingDetails
);
router.patch(
"/:organizationId/billing-details",
requireAuth({
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED],
}),
param("organizationId").exists().trim(),
body("email").optional().isString().trim(),
body("name").optional().isString().trim(),
validateRequest,
organizationsController.updateOrganizationBillingDetails
"/:organizationId/billing-details",
requireAuth({
acceptedAuthModes: [AuthMode.JWT]
}),
organizationsController.updateOrganizationBillingDetails
);
router.get(
"/:organizationId/billing-details/payment-methods",
requireAuth({
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED],
}),
param("organizationId").exists().trim(),
validateRequest,
organizationsController.getOrganizationPmtMethods
"/:organizationId/billing-details/payment-methods",
requireAuth({
acceptedAuthModes: [AuthMode.JWT]
}),
organizationsController.getOrganizationPmtMethods
);
router.post(
"/:organizationId/billing-details/payment-methods",
requireAuth({
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED],
}),
param("organizationId").exists().trim(),
body("success_url").exists().isString(),
body("cancel_url").exists().isString(),
validateRequest,
organizationsController.addOrganizationPmtMethod
"/:organizationId/billing-details/payment-methods",
requireAuth({
acceptedAuthModes: [AuthMode.JWT]
}),
organizationsController.addOrganizationPmtMethod
);
router.delete(
"/:organizationId/billing-details/payment-methods/:pmtMethodId",
requireAuth({
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED],
}),
param("organizationId").exists().trim(),
param("pmtMethodId").exists().trim(),
validateRequest,
organizationsController.deleteOrganizationPmtMethod
"/:organizationId/billing-details/payment-methods/:pmtMethodId",
requireAuth({
acceptedAuthModes: [AuthMode.JWT]
}),
organizationsController.deleteOrganizationPmtMethod
);
router.get(
"/:organizationId/billing-details/tax-ids",
requireAuth({
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED],
}),
param("organizationId").exists().trim(),
validateRequest,
organizationsController.getOrganizationTaxIds
"/:organizationId/billing-details/tax-ids",
requireAuth({
acceptedAuthModes: [AuthMode.JWT]
}),
organizationsController.getOrganizationTaxIds
);
router.post(
"/:organizationId/billing-details/tax-ids",
requireAuth({
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED],
}),
param("organizationId").exists().trim(),
body("type").exists().isString(),
body("value").exists().isString(),
validateRequest,
organizationsController.addOrganizationTaxId
"/:organizationId/billing-details/tax-ids",
requireAuth({
acceptedAuthModes: [AuthMode.JWT]
}),
organizationsController.addOrganizationTaxId
);
router.delete(
"/:organizationId/billing-details/tax-ids/:taxId",
requireAuth({
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED],
}),
param("organizationId").exists().trim(),
param("taxId").exists().trim(),
validateRequest,
organizationsController.deleteOrganizationTaxId
"/:organizationId/billing-details/tax-ids/:taxId",
requireAuth({
acceptedAuthModes: [AuthMode.JWT]
}),
organizationsController.deleteOrganizationTaxId
);
router.get(
"/:organizationId/invoices",
requireAuth({
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED],
}),
param("organizationId").exists().trim(),
validateRequest,
organizationsController.getOrganizationInvoices
"/:organizationId/invoices",
requireAuth({
acceptedAuthModes: [AuthMode.JWT]
}),
organizationsController.getOrganizationInvoices
);
router.get(
"/:organizationId/licenses",
requireAuth({
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN],
acceptedStatuses: [ACCEPTED],
}),
param("organizationId").exists().trim(),
validateRequest,
organizationsController.getOrganizationLicenses
"/:organizationId/licenses",
requireAuth({
acceptedAuthModes: [AuthMode.JWT]
}),
organizationsController.getOrganizationLicenses
);
export default router;
export default router;
+14 -42
View File
@@ -1,81 +1,53 @@
import express from "express";
const router = express.Router();
import { requireAuth } from "../../../middleware";
import {
requireAuth,
requireOrganizationAuth,
validateRequest,
} from "../../../middleware";
import { body, param } from "express-validator";
import { createInstallationSession, getCurrentOrganizationInstallationStatus, getRisksForOrganization, linkInstallationToOrganization, updateRisksStatus } from "../../../controllers/v1/secretScanningController";
import { ACCEPTED, ADMIN, AuthMode, MEMBER, OWNER } from "../../../variables";
createInstallationSession,
getCurrentOrganizationInstallationStatus,
getRisksForOrganization,
linkInstallationToOrganization,
updateRisksStatus
} from "../../../controllers/v1/secretScanningController";
import { AuthMode } from "../../../variables";
router.post(
"/create-installation-session/organization/:organizationId",
requireAuth({
acceptedAuthModes: [AuthMode.JWT],
acceptedAuthModes: [AuthMode.JWT]
}),
param("organizationId").exists().trim(),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED],
}),
validateRequest,
createInstallationSession
);
router.post(
"/link-installation",
requireAuth({
acceptedAuthModes: [AuthMode.JWT],
acceptedAuthModes: [AuthMode.JWT]
}),
body("installationId").exists().trim(),
body("sessionId").exists().trim(),
validateRequest,
linkInstallationToOrganization
);
router.get(
"/installation-status/organization/:organizationId",
requireAuth({
acceptedAuthModes: [AuthMode.JWT],
acceptedAuthModes: [AuthMode.JWT]
}),
param("organizationId").exists().trim(),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED],
}),
validateRequest,
getCurrentOrganizationInstallationStatus
);
router.get(
"/organization/:organizationId/risks",
requireAuth({
acceptedAuthModes: [AuthMode.JWT],
acceptedAuthModes: [AuthMode.JWT]
}),
param("organizationId").exists().trim(),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED],
}),
validateRequest,
getRisksForOrganization
);
router.post(
"/organization/:organizationId/risks/:riskId/status",
requireAuth({
acceptedAuthModes: [AuthMode.JWT],
acceptedAuthModes: [AuthMode.JWT]
}),
param("organizationId").exists().trim(),
param("riskId").exists().trim(),
body("status").exists(),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED],
}),
validateRequest,
updateRisksStatus
);
export default router;
export default router;
+59 -110
View File
@@ -1,146 +1,95 @@
import express from "express";
const router = express.Router();
import passport from "passport";
import {
AuthProvider
} from "../../models";
import {
requireAuth,
requireOrganizationAuth,
validateRequest,
} from "../../../middleware";
import { body, query } from "express-validator";
import { requireAuth } from "../../../middleware";
import { ssoController } from "../../controllers/v1";
import { authLimiter } from "../../../helpers/rateLimiter";
import {
ACCEPTED,
ADMIN,
AuthMode,
OWNER
} from "../../../variables";
import { AuthMode } from "../../../variables";
router.get(
"/redirect/google",
authLimiter,
(req, res, next) => {
passport.authenticate("google", {
scope: ["profile", "email"],
session: false,
...(req.query.callback_port ? {
state: req.query.callback_port as string
} : {})
})(req, res, next);
}
);
router.get("/redirect/google", authLimiter, (req, res, next) => {
passport.authenticate("google", {
scope: ["profile", "email"],
session: false,
...(req.query.callback_port
? {
state: req.query.callback_port as string
}
: {})
})(req, res, next);
});
router.get(
"/google",
passport.authenticate("google", {
failureRedirect: "/login/provider/error",
session: false
passport.authenticate("google", {
failureRedirect: "/login/provider/error",
session: false
}),
ssoController.redirectSSO
);
router.get(
"/redirect/github",
authLimiter,
(req, res, next) => {
passport.authenticate("github", {
session: false,
...(req.query.callback_port ? {
state: req.query.callback_port as string
} : {})
})(req, res, next);
}
);
router.get("/redirect/github", authLimiter, (req, res, next) => {
passport.authenticate("github", {
session: false,
...(req.query.callback_port
? {
state: req.query.callback_port as string
}
: {})
})(req, res, next);
});
router.get(
"/github",
authLimiter,
passport.authenticate("github", {
failureRedirect: "/login/provider/error",
session: false
passport.authenticate("github", {
failureRedirect: "/login/provider/error",
session: false
}),
ssoController.redirectSSO
);
router.get(
"/redirect/saml2/:ssoIdentifier",
authLimiter,
(req, res, next) => {
const options = {
failureRedirect: "/",
additionalParams: {
RelayState: req.query.callback_port ?? ""
},
};
passport.authenticate("saml", options)(req, res, next);
}
);
router.get("/redirect/saml2/:ssoIdentifier", authLimiter, (req, res, next) => {
const options = {
failureRedirect: "/",
additionalParams: {
RelayState: req.query.callback_port ?? ""
}
};
passport.authenticate("saml", options)(req, res, next);
});
router.post("/saml2/:ssoIdentifier",
passport.authenticate("saml", {
failureRedirect: "/login/provider/error",
failureFlash: true,
router.post(
"/saml2/:ssoIdentifier",
passport.authenticate("saml", {
failureRedirect: "/login/provider/error",
failureFlash: true,
session: false
}),
ssoController.redirectSSO
);
router.get(
"/config",
requireAuth({
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN],
acceptedStatuses: [ACCEPTED],
locationOrganizationId: "query"
}),
query("organizationId").exists().trim(),
validateRequest,
ssoController.getSSOConfig
"/config",
requireAuth({
acceptedAuthModes: [AuthMode.JWT]
}),
ssoController.getSSOConfig
);
router.post(
"/config",
requireAuth({
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN],
acceptedStatuses: [ACCEPTED],
locationOrganizationId: "body"
}),
body("organizationId").exists().trim(),
body("authProvider").exists().isString().isIn([AuthProvider.OKTA_SAML]),
body("isActive").exists().isBoolean(),
body("entryPoint").exists().isString(),
body("issuer").exists().isString(),
body("cert").exists().isString(),
validateRequest,
ssoController.createSSOConfig
"/config",
requireAuth({
acceptedAuthModes: [AuthMode.JWT]
}),
ssoController.createSSOConfig
);
router.patch(
"/config",
requireAuth({
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN],
acceptedStatuses: [ACCEPTED],
locationOrganizationId: "body"
}),
body("organizationId").exists().trim(),
body("authProvider").optional().isString(),
body("isActive").optional().isBoolean(),
body("entryPoint").optional().isString(),
body("issuer").optional().isString(),
body("cert").optional().isString(),
validateRequest,
ssoController.updateSSOConfig
"/config",
requireAuth({
acceptedAuthModes: [AuthMode.JWT]
}),
ssoController.updateSSOConfig
);
export default router;
export default router;