mirror of
https://github.com/Infisical/infisical.git
synced 2025-03-22 15:06:12 +00:00
Compare commits
1 Commits
ldapauth
...
snyk-fix-f
Author | SHA1 | Date | |
---|---|---|---|
9ce8692710 |
.goreleaser.yamlREADME.mdspec.yaml
backend
package-lock.jsonpackage.jsonspec.json
src
controllers
v1
authController.tsindex.tsintegrationAuthController.tsintegrationController.tsldapController.tsmembershipController.tsmembershipOrgController.tsorganizationController.tssecretImpsController.tssecretScanningController.tsuniversalAuthController.tsworkspaceController.ts
v2
v3
ee
controllers
v1
identitiesController.tsindex.tsorganizationsController.tsroleController.tssecretApprovalRequestsController.tsssoController.tsworkspaceController.ts
v3
helpers
models
routes
services
helpers
index.tsinterfaces
middleware
models
identity.tsidentityAccessToken.tsidentityMembership.tsidentityMembershipOrg.tsidentityUniversalAuth.tsidentityUniversalAuthClientSecret.tsindex.tsmembership.tsmembershipOrg.tsserviceTokenDataV3.tsserviceTokenDataV3Key.tsuser.ts
routes
v1
v2
v3
services
types/express
utils
validation
auth.tsidentities.tsindex.tsintegrationAuth.tsldap.tsorganization.tssecrets.tsserviceTokenData.tsserviceTokenDataV3.tsworkspace.ts
variables
swagger
cli
docker-compose.dev.ymldocs
api-reference
endpoints
environments
identities
organizations
delete-membership.mdxlist-identity-memberships.mdxmemberships.mdxupdate-membership.mdxworkspaces.mdx
service-tokens
universal-auth
attach.mdxcreate-client-secret.mdxlist-client-secrets.mdxlogin.mdxrenew-access-token.mdxretrieve.mdxrevoke-client-secret.mdxupdate.mdx
users
workspaces
overview
changelog
cli/commands
contributing
documentation
getting-started
guides
platform
images
getting-started/api
org-create-project-1.pngorg-create-project-2.pngproject-create-secret.pngproject-dashboard.pngproject-explore-env.png
platform/identities
identities-org-client-secret-create-1.pngidentities-org-client-secret-create-2.pngidentities-org-client-secret.pngidentities-org-create-auth-method.pngidentities-org-create.pngidentities-org.pngidentities-project-create.pngidentities-project.png
sdk-flow.pngself-hosting/deployment-options
aws-lightsail
awsl-container-service-overview.pngawsl-create-container-service-capacity.pngawsl-create-container-service-deployment.pngawsl-create-container-service-envars.pngawsl-create-container-service-public-endpoint.pngawsl-create-container-service-summary.pngawsl-create-container-service.pngawsl-select-lightsail.png
azure-app-services
aas-app-service-configuration.pngaas-app-service-deployment-complete.pngaas-app-service-overview.pngaas-create-app-service-basics.pngaas-create-app-service-docker.pngaas-create-app-service-review.pngaas-create-app-service.pngaas-select-app-services.png
azure-container-instances
aci-container-instance-overview.pngaci-create-container-instance-advanced.pngaci-create-container-instance-basics-1.pngaci-create-container-instance-basics-2.pngaci-create-container-instance-networking.pngaci-create-container-instance-review.pngaci-create-container-instance.pngaci-select-container-instances.png
flyio
gcp-cloud-run
gcp-cloud-run-create-project-2.pnggcp-cloud-run-create-project.pnggcp-cloud-run-create-service-docker-image.pnggcp-cloud-run-create-service-envars.pnggcp-cloud-run-create-service.pnggcp-cloud-run-select-cloud-run.pnggcp-cloud-run-service-details.png
railway
infisical-agent
integrations
cicd
cloud
aws-parameter-store.mdxaws-secret-manager.mdxazure-key-vault.mdxcheckly.mdxcloudflare-pages.mdxcloudflare-workers.mdxflyio.mdxgcp-secret-manager.mdxhasura-cloud.mdxheroku.mdxlaravel-forge.mdxnetlify.mdxnorthflank.mdxqovery.mdxrailway.mdxrender.mdxsupabase.mdxteamcity.mdxterraform-cloud.mdxvercel.mdxwindmill.mdx
platforms
internals
mint.jsonsdks
self-hosting
configuration
deployment-options
aws-lightsail.mdxazure-app-services.mdxazure-container-instances.mdxdocker-compose.mdxfly.io.mdxgcp-cloud-run.mdxkubernetes-helm.mdxrailway.mdxstandalone-infisical.mdx
overview.mdxfrontend
package-lock.jsonpackage.json
src
components/signup
context
hooks/api
auditLogs
auth
identities
index.tsxintegrationAuth
integrations
ldapConfig
organization
serviceTokens
ssoConfig
users
workspace
layouts/AppLayout
pages
views
IntegrationsPage
Login
Org/MembersPage
MembersPage.tsx
components
OrgIdentityTab
OrgIdentityTab.tsxindex.tsx
components
IdentitySection
IdentityAuthMethodModal.tsxIdentityModal.tsxIdentitySection.tsxIdentityTable.tsxIdentityUniversalAuthClientSecretModal.tsxIdentityUniversalAuthForm.tsxindex.tsx
index.tsxOrgMembersTab
OrgMembersTable
OrgRoleTabSection
index.tsxProject
AuditLogsPage/components
MembersPage
MembersPage.tsx
components
IdentityTab
MemberListTab
ProjectRoleListTab/components
ProjectRoleList
ProjectRoleModifySection
ServiceTokenTab
index.tsxSecretApprovalPage
SecretApprovalPage.tsx
components/SecretApprovalRequest
SecretMainPage/components
ActionBar
FolderListView
SecretDropzone
SecretListView
SecretOverviewPage
SecretRotationPage
Settings/OrgSettingsPage/components/OrgAuthTab
Signup
admin/SignUpPage
helm-charts/secrets-operator
k8-operator
@ -108,7 +108,7 @@ brews:
|
||||
zsh_completion.install "completions/infisical.zsh" => "_infisical"
|
||||
fish_completion.install "completions/infisical.fish"
|
||||
man1.install "manpages/infisical.1.gz"
|
||||
- name: "infisical@{{.Version}}"
|
||||
- name: 'infisical@{{.Version}}'
|
||||
tap:
|
||||
owner: Infisical
|
||||
name: homebrew-get-cli
|
||||
@ -186,14 +186,12 @@ aurs:
|
||||
# man pages
|
||||
install -Dm644 "./manpages/infisical.1.gz" "${pkgdir}/usr/share/man/man1/infisical.1.gz"
|
||||
|
||||
dockers:
|
||||
- dockerfile: docker/alpine
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
ids:
|
||||
- all-other-builds
|
||||
image_templates:
|
||||
- "infisical/cli:{{ .Version }}"
|
||||
- "infisical/cli:{{ .Major }}.{{ .Minor }}"
|
||||
- "infisical/cli:{{ .Major }}"
|
||||
- "infisical/cli:latest"
|
||||
# dockers:
|
||||
# - dockerfile: cli/docker/Dockerfile
|
||||
# goos: linux
|
||||
# goarch: amd64
|
||||
# ids:
|
||||
# - infisical
|
||||
# image_templates:
|
||||
# - "infisical/cli:{{ .Version }}"
|
||||
# - "infisical/cli:latest"
|
||||
|
@ -129,7 +129,7 @@ Note that this security address should be used only for undisclosed vulnerabilit
|
||||
|
||||
## Contributing
|
||||
|
||||
Whether it's big or small, we love contributions. Check out our guide to see how to [get started](https://infisical.com/docs/contributing/getting-started).
|
||||
Whether it's big or small, we love contributions. Check out our guide to see how to [get started](https://infisical.com/docs/contributing/overview).
|
||||
|
||||
Not sure where to get started? You can:
|
||||
|
||||
|
353
backend/package-lock.json
generated
353
backend/package-lock.json
generated
@ -24,7 +24,7 @@
|
||||
"ajv": "^8.12.0",
|
||||
"argon2": "^0.30.3",
|
||||
"aws-sdk": "^2.1364.0",
|
||||
"axios": "^1.6.0",
|
||||
"axios": "^1.3.5",
|
||||
"axios-retry": "^3.4.0",
|
||||
"bcrypt": "^5.1.0",
|
||||
"bigint-conversion": "^2.4.0",
|
||||
@ -56,15 +56,14 @@
|
||||
"passport-github": "^1.1.0",
|
||||
"passport-gitlab2": "^5.0.0",
|
||||
"passport-google-oauth20": "^2.0.0",
|
||||
"passport-ldapauth": "^3.0.1",
|
||||
"pg": "^8.11.3",
|
||||
"pino": "^8.16.1",
|
||||
"pino-http": "^8.5.1",
|
||||
"posthog-node": "^2.6.0",
|
||||
"probot": "^12.3.3",
|
||||
"probot": "^12.3.1",
|
||||
"query-string": "^7.1.3",
|
||||
"rate-limit-mongo": "^2.3.2",
|
||||
"rimraf": "^3.0.2",
|
||||
"rimraf": "^4.0.0",
|
||||
"swagger-ui-express": "^4.6.2",
|
||||
"tweetnacl": "^1.0.3",
|
||||
"tweetnacl-util": "^0.15.1",
|
||||
@ -5293,6 +5292,20 @@
|
||||
"node-pre-gyp": "bin/node-pre-gyp"
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/node-pre-gyp/node_modules/rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||
"dependencies": {
|
||||
"glob": "^7.1.3"
|
||||
},
|
||||
"bin": {
|
||||
"rimraf": "bin.js"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/@maxmind/geoip2-node": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@maxmind/geoip2-node/-/geoip2-node-3.5.0.tgz",
|
||||
@ -5992,9 +6005,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/webhooks": {
|
||||
"version": "9.26.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-9.26.3.tgz",
|
||||
"integrity": "sha512-DLGk+gzeVq5oK89Bo601txYmyrelMQ7Fi5EnjHE0Xs8CWicy2xkmnJMKptKJrBJpstqbd/9oeDFi/Zj2pudBDQ==",
|
||||
"version": "9.26.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-9.26.0.tgz",
|
||||
"integrity": "sha512-foZlsgrTDwAmD5j2Czn6ji10lbWjGDVsUxTIydjG9KTkAWKJrFapXJgO5SbGxRwfPd3OJdhK3nA2YPqVhxLXqA==",
|
||||
"dependencies": {
|
||||
"@octokit/request-error": "^2.0.2",
|
||||
"@octokit/webhooks-methods": "^2.0.0",
|
||||
@ -7414,14 +7427,6 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/ldapjs": {
|
||||
"version": "2.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/ldapjs/-/ldapjs-2.2.5.tgz",
|
||||
"integrity": "sha512-Lv/nD6QDCmcT+V1vaTRnEKE8UgOilVv5pHcQuzkU1LcRe4mbHHuUo/KHi0LKrpdHhQY8FJzryF38fcVdeUIrzg==",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/libsodium-wrappers": {
|
||||
"version": "0.7.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/libsodium-wrappers/-/libsodium-wrappers-0.7.10.tgz",
|
||||
@ -7974,11 +7979,6 @@
|
||||
"node": ">=6.5"
|
||||
}
|
||||
},
|
||||
"node_modules/abstract-logging": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz",
|
||||
"integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA=="
|
||||
},
|
||||
"node_modules/accepts": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||
@ -8277,18 +8277,11 @@
|
||||
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/asn1": {
|
||||
"version": "0.2.6",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
||||
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
|
||||
"dependencies": {
|
||||
"safer-buffer": "~2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/assert-plus": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
|
||||
"integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
@ -8346,9 +8339,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz",
|
||||
"integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==",
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz",
|
||||
"integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.0",
|
||||
"form-data": "^4.0.0",
|
||||
@ -8455,17 +8448,6 @@
|
||||
"@babel/core": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/backoff": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz",
|
||||
"integrity": "sha512-wC5ihrnUXmR2douXmXLCe5O3zg3GKIyvRi/hi58a/XyRxVI+3/yM0PYueQOZXPXQ9pxBislYkw+sF9b7C/RuMA==",
|
||||
"dependencies": {
|
||||
"precond": "0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
@ -8529,11 +8511,6 @@
|
||||
"node": ">= 10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/bcryptjs": {
|
||||
"version": "2.4.3",
|
||||
"resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
|
||||
"integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ=="
|
||||
},
|
||||
"node_modules/before-after-hook": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
|
||||
@ -9157,7 +9134,8 @@
|
||||
"node_modules/core-util-is": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="
|
||||
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/cors": {
|
||||
"version": "2.8.5",
|
||||
@ -10012,6 +9990,7 @@
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
|
||||
"integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==",
|
||||
"dev": true,
|
||||
"engines": [
|
||||
"node >=0.6.0"
|
||||
]
|
||||
@ -10217,6 +10196,21 @@
|
||||
"node": "^10.12.0 || >=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/flat-cache/node_modules/rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"glob": "^7.1.3"
|
||||
},
|
||||
"bin": {
|
||||
"rimraf": "bin.js"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/flatstr": {
|
||||
"version": "1.0.12",
|
||||
"resolved": "https://registry.npmjs.org/flatstr/-/flatstr-1.0.12.tgz",
|
||||
@ -11923,57 +11917,6 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/ldap-filter": {
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/ldap-filter/-/ldap-filter-0.3.3.tgz",
|
||||
"integrity": "sha512-/tFkx5WIn4HuO+6w9lsfxq4FN3O+fDZeO9Mek8dCD8rTUpqzRa766BOBO7BcGkn3X86m5+cBm1/2S/Shzz7gMg==",
|
||||
"dependencies": {
|
||||
"assert-plus": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/ldapauth-fork": {
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/ldapauth-fork/-/ldapauth-fork-5.0.5.tgz",
|
||||
"integrity": "sha512-LWUk76+V4AOZbny/3HIPQtGPWZyA3SW2tRhsWIBi9imP22WJktKLHV1ofd8Jo/wY7Ve6vAT7FCI5mEn3blZTjw==",
|
||||
"dependencies": {
|
||||
"@types/ldapjs": "^2.2.2",
|
||||
"bcryptjs": "^2.4.0",
|
||||
"ldapjs": "^2.2.1",
|
||||
"lru-cache": "^7.10.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ldapauth-fork/node_modules/lru-cache": {
|
||||
"version": "7.18.3",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
|
||||
"integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/ldapjs": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-2.3.3.tgz",
|
||||
"integrity": "sha512-75QiiLJV/PQqtpH+HGls44dXweviFwQ6SiIK27EqzKQ5jU/7UFrl2E5nLdQ3IYRBzJ/AVFJI66u0MZ0uofKYwg==",
|
||||
"dependencies": {
|
||||
"abstract-logging": "^2.0.0",
|
||||
"asn1": "^0.2.4",
|
||||
"assert-plus": "^1.0.0",
|
||||
"backoff": "^2.5.0",
|
||||
"ldap-filter": "^0.3.3",
|
||||
"once": "^1.4.0",
|
||||
"vasync": "^2.2.0",
|
||||
"verror": "^1.8.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/leven": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
|
||||
@ -15743,18 +15686,6 @@
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/passport-ldapauth": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/passport-ldapauth/-/passport-ldapauth-3.0.1.tgz",
|
||||
"integrity": "sha512-TRRx3BHi8GC8MfCT9wmghjde/EGeKjll7zqHRRfGRxXbLcaDce2OftbQrFG7/AWaeFhR6zpZHtBQ/IkINdLVjQ==",
|
||||
"dependencies": {
|
||||
"ldapauth-fork": "^5.0.1",
|
||||
"passport-strategy": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/passport-oauth2": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.7.0.tgz",
|
||||
@ -16368,14 +16299,6 @@
|
||||
"form-data": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/precond": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz",
|
||||
"integrity": "sha512-QCYG84SgGyGzqJ/vlMsxeXd/pgL/I94ixdNFyh1PusWmTCyVfPJjZ1K1jvHtsbfnXQs2TSkEP2fR7QiMZAnKFQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/prelude-ls": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||
@ -16412,9 +16335,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/probot": {
|
||||
"version": "12.3.3",
|
||||
"resolved": "https://registry.npmjs.org/probot/-/probot-12.3.3.tgz",
|
||||
"integrity": "sha512-cdtKd+xISzi8sw6++BYBXleRknCA6hqUMoHj/sJqQBrjbNxQLhfeFCq9O2d0Z4eShsy5YFRR3MWwDKJ9uAE0CA==",
|
||||
"version": "12.3.1",
|
||||
"resolved": "https://registry.npmjs.org/probot/-/probot-12.3.1.tgz",
|
||||
"integrity": "sha512-ECSgycmAC0ILEK6cOa+x3QPufP5JybsuohOFCYr3glQU5SkbmypZJE/Sfio9mxAFHK5LCXveIDsfZCxf6ck4JA==",
|
||||
"dependencies": {
|
||||
"@octokit/core": "^3.2.4",
|
||||
"@octokit/plugin-enterprise-compatibility": "^1.2.8",
|
||||
@ -16423,7 +16346,7 @@
|
||||
"@octokit/plugin-retry": "^3.0.6",
|
||||
"@octokit/plugin-throttling": "^3.3.4",
|
||||
"@octokit/types": "^8.0.0",
|
||||
"@octokit/webhooks": "^9.26.3",
|
||||
"@octokit/webhooks": "^9.8.4",
|
||||
"@probot/get-private-key": "^1.1.0",
|
||||
"@probot/octokit-plugin-config": "^1.0.0",
|
||||
"@probot/pino": "^2.2.0",
|
||||
@ -17231,14 +17154,15 @@
|
||||
"integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA=="
|
||||
},
|
||||
"node_modules/rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||
"dependencies": {
|
||||
"glob": "^7.1.3"
|
||||
},
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.0.0.tgz",
|
||||
"integrity": "sha512-1jUYEffrxKEAYJLJvqYLvKQW5oExH8QhBVYKUO1YXPrKTpobYuZ89KbgmpGbeTj+6kUXqGrIT1w+3BI4T6hR1w==",
|
||||
"deprecated": "Please upgrade to 4.3.1 or higher to fix a potentially damaging issue regarding symbolic link following. See https://github.com/isaacs/rimraf/issues/259 for details.",
|
||||
"bin": {
|
||||
"rimraf": "bin.js"
|
||||
"rimraf": "dist/cjs/src/bin.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
@ -18460,21 +18384,11 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/vasync": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/vasync/-/vasync-2.2.1.tgz",
|
||||
"integrity": "sha512-Hq72JaTpcTFdWiNA4Y22Amej2GH3BFmBaKPPlDZ4/oC8HNn2ISHLkFrJU4Ds8R3jcUi7oo5Y9jcMHKjES+N9wQ==",
|
||||
"engines": [
|
||||
"node >=0.6.0"
|
||||
],
|
||||
"dependencies": {
|
||||
"verror": "1.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/verror": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
|
||||
"integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
|
||||
"dev": true,
|
||||
"engines": [
|
||||
"node >=0.6.0"
|
||||
],
|
||||
@ -23026,6 +22940,16 @@
|
||||
"rimraf": "^3.0.2",
|
||||
"semver": "^7.3.5",
|
||||
"tar": "^6.1.11"
|
||||
},
|
||||
"dependencies": {
|
||||
"rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||
"requires": {
|
||||
"glob": "^7.1.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@maxmind/geoip2-node": {
|
||||
@ -23508,9 +23432,9 @@
|
||||
}
|
||||
},
|
||||
"@octokit/webhooks": {
|
||||
"version": "9.26.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-9.26.3.tgz",
|
||||
"integrity": "sha512-DLGk+gzeVq5oK89Bo601txYmyrelMQ7Fi5EnjHE0Xs8CWicy2xkmnJMKptKJrBJpstqbd/9oeDFi/Zj2pudBDQ==",
|
||||
"version": "9.26.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-9.26.0.tgz",
|
||||
"integrity": "sha512-foZlsgrTDwAmD5j2Czn6ji10lbWjGDVsUxTIydjG9KTkAWKJrFapXJgO5SbGxRwfPd3OJdhK3nA2YPqVhxLXqA==",
|
||||
"requires": {
|
||||
"@octokit/request-error": "^2.0.2",
|
||||
"@octokit/webhooks-methods": "^2.0.0",
|
||||
@ -24657,14 +24581,6 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/ldapjs": {
|
||||
"version": "2.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/ldapjs/-/ldapjs-2.2.5.tgz",
|
||||
"integrity": "sha512-Lv/nD6QDCmcT+V1vaTRnEKE8UgOilVv5pHcQuzkU1LcRe4mbHHuUo/KHi0LKrpdHhQY8FJzryF38fcVdeUIrzg==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/libsodium-wrappers": {
|
||||
"version": "0.7.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/libsodium-wrappers/-/libsodium-wrappers-0.7.10.tgz",
|
||||
@ -25108,11 +25024,6 @@
|
||||
"event-target-shim": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"abstract-logging": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz",
|
||||
"integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA=="
|
||||
},
|
||||
"accepts": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||
@ -25333,18 +25244,11 @@
|
||||
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
|
||||
"dev": true
|
||||
},
|
||||
"asn1": {
|
||||
"version": "0.2.6",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
||||
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
|
||||
"requires": {
|
||||
"safer-buffer": "~2.1.0"
|
||||
}
|
||||
},
|
||||
"assert-plus": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
|
||||
"integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw=="
|
||||
"integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
|
||||
"dev": true
|
||||
},
|
||||
"asynckit": {
|
||||
"version": "0.4.0",
|
||||
@ -25386,9 +25290,9 @@
|
||||
}
|
||||
},
|
||||
"axios": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz",
|
||||
"integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==",
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz",
|
||||
"integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.15.0",
|
||||
"form-data": "^4.0.0",
|
||||
@ -25474,14 +25378,6 @@
|
||||
"babel-preset-current-node-syntax": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"backoff": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz",
|
||||
"integrity": "sha512-wC5ihrnUXmR2douXmXLCe5O3zg3GKIyvRi/hi58a/XyRxVI+3/yM0PYueQOZXPXQ9pxBislYkw+sF9b7C/RuMA==",
|
||||
"requires": {
|
||||
"precond": "0.2"
|
||||
}
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
@ -25523,11 +25419,6 @@
|
||||
"node-addon-api": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"bcryptjs": {
|
||||
"version": "2.4.3",
|
||||
"resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
|
||||
"integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ=="
|
||||
},
|
||||
"before-after-hook": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
|
||||
@ -25981,7 +25872,8 @@
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="
|
||||
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
|
||||
"dev": true
|
||||
},
|
||||
"cors": {
|
||||
"version": "2.8.5",
|
||||
@ -26618,7 +26510,8 @@
|
||||
"extsprintf": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
|
||||
"integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g=="
|
||||
"integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==",
|
||||
"dev": true
|
||||
},
|
||||
"fast-copy": {
|
||||
"version": "3.0.1",
|
||||
@ -26780,6 +26673,17 @@
|
||||
"requires": {
|
||||
"flatted": "^3.1.0",
|
||||
"rimraf": "^3.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "^7.1.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"flatstr": {
|
||||
@ -28044,47 +27948,6 @@
|
||||
"integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
|
||||
"dev": true
|
||||
},
|
||||
"ldap-filter": {
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/ldap-filter/-/ldap-filter-0.3.3.tgz",
|
||||
"integrity": "sha512-/tFkx5WIn4HuO+6w9lsfxq4FN3O+fDZeO9Mek8dCD8rTUpqzRa766BOBO7BcGkn3X86m5+cBm1/2S/Shzz7gMg==",
|
||||
"requires": {
|
||||
"assert-plus": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"ldapauth-fork": {
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/ldapauth-fork/-/ldapauth-fork-5.0.5.tgz",
|
||||
"integrity": "sha512-LWUk76+V4AOZbny/3HIPQtGPWZyA3SW2tRhsWIBi9imP22WJktKLHV1ofd8Jo/wY7Ve6vAT7FCI5mEn3blZTjw==",
|
||||
"requires": {
|
||||
"@types/ldapjs": "^2.2.2",
|
||||
"bcryptjs": "^2.4.0",
|
||||
"ldapjs": "^2.2.1",
|
||||
"lru-cache": "^7.10.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"lru-cache": {
|
||||
"version": "7.18.3",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
|
||||
"integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"ldapjs": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-2.3.3.tgz",
|
||||
"integrity": "sha512-75QiiLJV/PQqtpH+HGls44dXweviFwQ6SiIK27EqzKQ5jU/7UFrl2E5nLdQ3IYRBzJ/AVFJI66u0MZ0uofKYwg==",
|
||||
"requires": {
|
||||
"abstract-logging": "^2.0.0",
|
||||
"asn1": "^0.2.4",
|
||||
"assert-plus": "^1.0.0",
|
||||
"backoff": "^2.5.0",
|
||||
"ldap-filter": "^0.3.3",
|
||||
"once": "^1.4.0",
|
||||
"vasync": "^2.2.0",
|
||||
"verror": "^1.8.1"
|
||||
}
|
||||
},
|
||||
"leven": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
|
||||
@ -30762,15 +30625,6 @@
|
||||
"passport-oauth2": "1.x.x"
|
||||
}
|
||||
},
|
||||
"passport-ldapauth": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/passport-ldapauth/-/passport-ldapauth-3.0.1.tgz",
|
||||
"integrity": "sha512-TRRx3BHi8GC8MfCT9wmghjde/EGeKjll7zqHRRfGRxXbLcaDce2OftbQrFG7/AWaeFhR6zpZHtBQ/IkINdLVjQ==",
|
||||
"requires": {
|
||||
"ldapauth-fork": "^5.0.1",
|
||||
"passport-strategy": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"passport-oauth2": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.7.0.tgz",
|
||||
@ -31210,11 +31064,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"precond": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz",
|
||||
"integrity": "sha512-QCYG84SgGyGzqJ/vlMsxeXd/pgL/I94ixdNFyh1PusWmTCyVfPJjZ1K1jvHtsbfnXQs2TSkEP2fR7QiMZAnKFQ=="
|
||||
},
|
||||
"prelude-ls": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||
@ -31241,9 +31090,9 @@
|
||||
}
|
||||
},
|
||||
"probot": {
|
||||
"version": "12.3.3",
|
||||
"resolved": "https://registry.npmjs.org/probot/-/probot-12.3.3.tgz",
|
||||
"integrity": "sha512-cdtKd+xISzi8sw6++BYBXleRknCA6hqUMoHj/sJqQBrjbNxQLhfeFCq9O2d0Z4eShsy5YFRR3MWwDKJ9uAE0CA==",
|
||||
"version": "12.3.1",
|
||||
"resolved": "https://registry.npmjs.org/probot/-/probot-12.3.1.tgz",
|
||||
"integrity": "sha512-ECSgycmAC0ILEK6cOa+x3QPufP5JybsuohOFCYr3glQU5SkbmypZJE/Sfio9mxAFHK5LCXveIDsfZCxf6ck4JA==",
|
||||
"requires": {
|
||||
"@octokit/core": "^3.2.4",
|
||||
"@octokit/plugin-enterprise-compatibility": "^1.2.8",
|
||||
@ -31252,7 +31101,7 @@
|
||||
"@octokit/plugin-retry": "^3.0.6",
|
||||
"@octokit/plugin-throttling": "^3.3.4",
|
||||
"@octokit/types": "^8.0.0",
|
||||
"@octokit/webhooks": "^9.26.3",
|
||||
"@octokit/webhooks": "^9.8.4",
|
||||
"@probot/get-private-key": "^1.1.0",
|
||||
"@probot/octokit-plugin-config": "^1.0.0",
|
||||
"@probot/pino": "^2.2.0",
|
||||
@ -31918,12 +31767,9 @@
|
||||
"integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA=="
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||
"requires": {
|
||||
"glob": "^7.1.3"
|
||||
}
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.0.0.tgz",
|
||||
"integrity": "sha512-1jUYEffrxKEAYJLJvqYLvKQW5oExH8QhBVYKUO1YXPrKTpobYuZ89KbgmpGbeTj+6kUXqGrIT1w+3BI4T6hR1w=="
|
||||
},
|
||||
"ripemd160": {
|
||||
"version": "2.0.2",
|
||||
@ -32837,18 +32683,11 @@
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="
|
||||
},
|
||||
"vasync": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/vasync/-/vasync-2.2.1.tgz",
|
||||
"integrity": "sha512-Hq72JaTpcTFdWiNA4Y22Amej2GH3BFmBaKPPlDZ4/oC8HNn2ISHLkFrJU4Ds8R3jcUi7oo5Y9jcMHKjES+N9wQ==",
|
||||
"requires": {
|
||||
"verror": "1.10.0"
|
||||
}
|
||||
},
|
||||
"verror": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
|
||||
"integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"assert-plus": "^1.0.0",
|
||||
"core-util-is": "1.0.2",
|
||||
|
@ -15,7 +15,7 @@
|
||||
"ajv": "^8.12.0",
|
||||
"argon2": "^0.30.3",
|
||||
"aws-sdk": "^2.1364.0",
|
||||
"axios": "^1.6.0",
|
||||
"axios": "^1.3.5",
|
||||
"axios-retry": "^3.4.0",
|
||||
"bcrypt": "^5.1.0",
|
||||
"bigint-conversion": "^2.4.0",
|
||||
@ -47,15 +47,14 @@
|
||||
"passport-github": "^1.1.0",
|
||||
"passport-gitlab2": "^5.0.0",
|
||||
"passport-google-oauth20": "^2.0.0",
|
||||
"passport-ldapauth": "^3.0.1",
|
||||
"pg": "^8.11.3",
|
||||
"pino": "^8.16.1",
|
||||
"pino-http": "^8.5.1",
|
||||
"posthog-node": "^2.6.0",
|
||||
"probot": "^12.3.3",
|
||||
"probot": "^12.3.1",
|
||||
"query-string": "^7.1.3",
|
||||
"rate-limit-mongo": "^2.3.2",
|
||||
"rimraf": "^3.0.2",
|
||||
"rimraf": "^4.0.0",
|
||||
"swagger-ui-express": "^4.6.2",
|
||||
"tweetnacl": "^1.0.3",
|
||||
"tweetnacl-util": "^0.15.1",
|
||||
|
1282
backend/spec.json
1282
backend/spec.json
File diff suppressed because it is too large
Load Diff
@ -3,22 +3,15 @@ import jwt from "jsonwebtoken";
|
||||
import * as bigintConversion from "bigint-conversion";
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const jsrp = require("jsrp");
|
||||
import {
|
||||
LoginSRPDetail,
|
||||
TokenVersion,
|
||||
User
|
||||
} from "../../models";
|
||||
import { LoginSRPDetail, TokenVersion, User } from "../../models";
|
||||
import { clearTokens, createToken, issueAuthTokens } from "../../helpers/auth";
|
||||
import { checkUserDevice } from "../../helpers/user";
|
||||
import { AuthTokenType } from "../../variables";
|
||||
import {
|
||||
BadRequestError,
|
||||
UnauthorizedRequestError
|
||||
} from "../../utils/errors";
|
||||
import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors";
|
||||
import {
|
||||
getAuthSecret,
|
||||
getHttpsEnabled,
|
||||
getJwtAuthLifetime,
|
||||
getJwtAuthLifetime
|
||||
} from "../../config";
|
||||
import { ActorType } from "../../ee/models";
|
||||
import { validateRequest } from "../../helpers/validation";
|
||||
@ -32,11 +25,10 @@ declare module "jsonwebtoken" {
|
||||
userId: string;
|
||||
refreshVersion?: number;
|
||||
}
|
||||
export interface IdentityAccessTokenJwtPayload extends jwt.JwtPayload {
|
||||
_id: string;
|
||||
clientSecretId: string;
|
||||
identityAccessTokenId: string;
|
||||
export interface ServiceRefreshTokenJwtPayload extends jwt.JwtPayload {
|
||||
serviceTokenDataId: string;
|
||||
authTokenType: string;
|
||||
tokenVersion: number;
|
||||
}
|
||||
}
|
||||
|
||||
@ -274,4 +266,4 @@ export const getNewToken = async (req: Request, res: Response) => {
|
||||
|
||||
export const handleAuthProviderCallback = (req: Request, res: Response) => {
|
||||
res.redirect(`/login/provider/success?token=${encodeURIComponent(req.providerAuthToken)}`);
|
||||
};
|
||||
};
|
||||
|
@ -1,6 +1,4 @@
|
||||
import * as authController from "./authController";
|
||||
import * as universalAuthController from "./universalAuthController";
|
||||
import * as ldapController from "./ldapController";
|
||||
import * as botController from "./botController";
|
||||
import * as integrationAuthController from "./integrationAuthController";
|
||||
import * as integrationController from "./integrationController";
|
||||
@ -22,8 +20,6 @@ import * as adminController from "./adminController";
|
||||
|
||||
export {
|
||||
authController,
|
||||
universalAuthController,
|
||||
ldapController,
|
||||
botController,
|
||||
integrationAuthController,
|
||||
integrationController,
|
||||
|
@ -2,7 +2,7 @@ import { Request, Response } from "express";
|
||||
import { Types } from "mongoose";
|
||||
import { standardRequest } from "../../config/request";
|
||||
import { getApps, getTeams, revokeAccess } from "../../integrations";
|
||||
import { Bot, IIntegrationAuth, Integration, IntegrationAuth, Workspace } from "../../models";
|
||||
import { Bot, IntegrationAuth, Workspace } from "../../models";
|
||||
import { EventType } from "../../ee/models";
|
||||
import { IntegrationService } from "../../services";
|
||||
import { EEAuditLogService } from "../../ee/services";
|
||||
@ -130,6 +130,7 @@ export const oAuthExchange = async (req: Request, res: Response) => {
|
||||
export const saveIntegrationToken = async (req: Request, res: Response) => {
|
||||
// TODO: refactor
|
||||
// TODO: check if access token is valid for each integration
|
||||
let integrationAuth;
|
||||
const {
|
||||
body: { workspaceId, integration, url, accessId, namespace, accessToken, refreshToken }
|
||||
} = await validateRequest(reqValidator.SaveIntegrationAccessTokenV1, req);
|
||||
@ -151,21 +152,31 @@ export const saveIntegrationToken = async (req: Request, res: Response) => {
|
||||
|
||||
if (!bot) throw new Error("Bot must be enabled to save integration access token");
|
||||
|
||||
let integrationAuth = await new IntegrationAuth({
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
integration,
|
||||
url,
|
||||
namespace,
|
||||
algorithm: ALGORITHM_AES_256_GCM,
|
||||
keyEncoding: ENCODING_SCHEME_UTF8,
|
||||
...(integration === INTEGRATION_GCP_SECRET_MANAGER
|
||||
? {
|
||||
metadata: {
|
||||
authMethod: "serviceAccount"
|
||||
integrationAuth = await IntegrationAuth.findOneAndUpdate(
|
||||
{
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
integration
|
||||
},
|
||||
{
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
integration,
|
||||
url,
|
||||
namespace,
|
||||
algorithm: ALGORITHM_AES_256_GCM,
|
||||
keyEncoding: ENCODING_SCHEME_UTF8,
|
||||
...(integration === INTEGRATION_GCP_SECRET_MANAGER
|
||||
? {
|
||||
metadata: {
|
||||
authMethod: "serviceAccount"
|
||||
}
|
||||
}
|
||||
}
|
||||
: {})
|
||||
}).save();
|
||||
: {})
|
||||
},
|
||||
{
|
||||
new: true,
|
||||
upsert: true
|
||||
}
|
||||
);
|
||||
|
||||
// encrypt and save integration access details
|
||||
if (refreshToken) {
|
||||
@ -177,12 +188,12 @@ export const saveIntegrationToken = async (req: Request, res: Response) => {
|
||||
|
||||
// encrypt and save integration access details
|
||||
if (accessId || accessToken) {
|
||||
integrationAuth = (await IntegrationService.setIntegrationAuthAccess({
|
||||
integrationAuth = await IntegrationService.setIntegrationAuthAccess({
|
||||
integrationAuthId: integrationAuth._id.toString(),
|
||||
accessId,
|
||||
accessToken,
|
||||
accessExpiresAt: undefined
|
||||
})) as IIntegrationAuth;
|
||||
});
|
||||
}
|
||||
|
||||
if (!integrationAuth) throw new Error("Failed to save integration access token");
|
||||
@ -1197,64 +1208,13 @@ export const getIntegrationAuthTeamCityBuildConfigs = async (req: Request, res:
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete all integration authorizations and integrations for workspace with id [workspaceId]
|
||||
* with integration name [integration]
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const deleteIntegrationAuths = async (req: Request, res: Response) => {
|
||||
const {
|
||||
query: { integration, workspaceId }
|
||||
} = await validateRequest(reqValidator.DeleteIntegrationAuthsV1, req);
|
||||
|
||||
const { permission } = await getAuthDataProjectPermissions({
|
||||
authData: req.authData,
|
||||
workspaceId: new Types.ObjectId(workspaceId)
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Delete,
|
||||
ProjectPermissionSub.Integrations
|
||||
);
|
||||
|
||||
const integrationAuths = await IntegrationAuth.deleteMany({
|
||||
integration,
|
||||
workspace: new Types.ObjectId(workspaceId)
|
||||
});
|
||||
|
||||
const integrations = await Integration.deleteMany({
|
||||
integration,
|
||||
workspace: new Types.ObjectId(workspaceId)
|
||||
});
|
||||
|
||||
await EEAuditLogService.createAuditLog(
|
||||
req.authData,
|
||||
{
|
||||
type: EventType.UNAUTHORIZE_INTEGRATION,
|
||||
metadata: {
|
||||
integration
|
||||
}
|
||||
},
|
||||
{
|
||||
workspaceId: new Types.ObjectId(workspaceId)
|
||||
}
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
integrationAuths,
|
||||
integrations
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete integration authorization with id [integrationAuthId]
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const deleteIntegrationAuthById = async (req: Request, res: Response) => {
|
||||
export const deleteIntegrationAuth = async (req: Request, res: Response) => {
|
||||
const {
|
||||
params: { integrationAuthId }
|
||||
} = await validateRequest(reqValidator.DeleteIntegrationAuthV1, req);
|
||||
|
@ -251,21 +251,6 @@ export const deleteIntegration = async (req: Request, res: Response) => {
|
||||
});
|
||||
|
||||
if (!deletedIntegration) throw new Error("Failed to find integration");
|
||||
|
||||
const numOtherIntegrationsUsingSameAuth = await Integration.countDocuments({
|
||||
integrationAuth: deletedIntegration.integrationAuth,
|
||||
_id: {
|
||||
$nin: [deletedIntegration._id]
|
||||
}
|
||||
});
|
||||
|
||||
if (numOtherIntegrationsUsingSameAuth === 0) {
|
||||
// no other integrations are using the same integration auth
|
||||
// -> delete integration auth associated with the integration being deleted
|
||||
await IntegrationAuth.deleteOne({
|
||||
_id: deletedIntegration.integrationAuth
|
||||
});
|
||||
}
|
||||
|
||||
await EEAuditLogService.createAuditLog(
|
||||
req.authData,
|
||||
|
@ -1,233 +0,0 @@
|
||||
import { Request, Response } from "express";
|
||||
import { Types } from "mongoose";
|
||||
import { client, getSiteURL } from "../../config";
|
||||
import * as reqValidator from "../../validation/ldap";
|
||||
import { validateRequest } from "../../helpers/validation";
|
||||
import { getLdapConfigHelper } from "../../ee/helpers/organizations";
|
||||
import {
|
||||
OrgPermissionActions,
|
||||
OrgPermissionSubjects,
|
||||
getAuthDataOrgPermissions
|
||||
} from "../../ee/services/RoleService";
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { LDAPConfig } from "../../ee/models";
|
||||
import { BotOrgService } from "../../services";
|
||||
|
||||
/**
|
||||
* Return appropriate SSO endpoint after successful authentication with LDAP
|
||||
* to finish inputting their master key for logging in or signing up
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const redirectLDAP = async (req: Request, res: Response) => {
|
||||
let nextUrl;
|
||||
if (req.isUserCompleted) {
|
||||
nextUrl = `${await getSiteURL()}/login/sso?token=${encodeURIComponent(req.providerAuthToken)}`;
|
||||
} else {
|
||||
nextUrl = `${await getSiteURL()}/signup/sso?token=${encodeURIComponent(req.providerAuthToken)}`
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
nextUrl
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return organization LDAP configuration
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const getLDAPConfig = async (req: Request, res: Response) => {
|
||||
const {
|
||||
query: { organizationId }
|
||||
} = await validateRequest(reqValidator.GetLdapConfigv1, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Read,
|
||||
OrgPermissionSubjects.Sso
|
||||
);
|
||||
|
||||
const data = await getLdapConfigHelper({
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
|
||||
return res.status(200).send(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update organization LDAP configuration
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const updateLDAPConfig = async (req: Request, res: Response) => {
|
||||
const {
|
||||
body: {
|
||||
organizationId,
|
||||
isActive,
|
||||
url,
|
||||
bindDN,
|
||||
bindPass,
|
||||
searchBase,
|
||||
caCert
|
||||
}
|
||||
} = await validateRequest(reqValidator.UpdateLdapConfigv1, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Edit,
|
||||
OrgPermissionSubjects.Sso
|
||||
);
|
||||
|
||||
interface PatchUpdate {
|
||||
isActive?: boolean;
|
||||
url?: string;
|
||||
encryptedBindDN?: string;
|
||||
bindDNIV?: string;
|
||||
bindDNTag?: string;
|
||||
encryptedBindPass?: string;
|
||||
bindPassIV?: string;
|
||||
bindPassTag?: string;
|
||||
searchBase?: string;
|
||||
encryptedCACert?: string;
|
||||
caCertIV?: string;
|
||||
caCertTag?: string;
|
||||
}
|
||||
|
||||
const update: PatchUpdate = {};
|
||||
|
||||
if (url) {
|
||||
update.url = url;
|
||||
}
|
||||
|
||||
if (searchBase) {
|
||||
update.searchBase = searchBase;
|
||||
}
|
||||
|
||||
if (isActive !== undefined) {
|
||||
update.isActive = isActive;
|
||||
}
|
||||
|
||||
const key = await BotOrgService.getSymmetricKey(new Types.ObjectId(organizationId));
|
||||
|
||||
if (bindDN) {
|
||||
const {
|
||||
ciphertext: encryptedBindDN,
|
||||
iv: bindDNIV,
|
||||
tag: bindDNTag
|
||||
} = client.encryptSymmetric(bindDN, key);
|
||||
|
||||
update.encryptedBindDN = encryptedBindDN;
|
||||
update.bindDNIV = bindDNIV;
|
||||
update.bindDNTag = bindDNTag;
|
||||
}
|
||||
|
||||
if (bindPass) {
|
||||
const {
|
||||
ciphertext: encryptedBindPass,
|
||||
iv: bindPassIV,
|
||||
tag: bindPassTag
|
||||
} = client.encryptSymmetric(bindPass, key);
|
||||
|
||||
update.encryptedBindPass = encryptedBindPass;
|
||||
update.bindPassIV = bindPassIV;
|
||||
update.bindPassTag = bindPassTag;
|
||||
}
|
||||
|
||||
if (caCert) {
|
||||
const {
|
||||
ciphertext: encryptedCACert,
|
||||
iv: caCertIV,
|
||||
tag: caCertTag
|
||||
} = client.encryptSymmetric(caCert, key);
|
||||
|
||||
update.encryptedCACert = encryptedCACert;
|
||||
update.caCertIV = caCertIV;
|
||||
update.caCertTag = caCertTag;
|
||||
}
|
||||
|
||||
const ldapConfig = await LDAPConfig.findOneAndUpdate(
|
||||
{ organization: new Types.ObjectId(organizationId) },
|
||||
update,
|
||||
{ new: true }
|
||||
);
|
||||
|
||||
return res.status(200).send(ldapConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create organization LDAP configuration
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const createLDAPConfig = async (req: Request, res: Response) => {
|
||||
const {
|
||||
body: {
|
||||
organizationId,
|
||||
isActive,
|
||||
url,
|
||||
bindDN,
|
||||
bindPass,
|
||||
searchBase,
|
||||
caCert
|
||||
}
|
||||
} = await validateRequest(reqValidator.CreateLdapConfigv1, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Create,
|
||||
OrgPermissionSubjects.Sso
|
||||
);
|
||||
|
||||
const key = await BotOrgService.getSymmetricKey(new Types.ObjectId(organizationId));
|
||||
|
||||
const {
|
||||
ciphertext: encryptedBindDN,
|
||||
iv: bindDNIV,
|
||||
tag: bindDNTag
|
||||
} = client.encryptSymmetric(bindDN, key);
|
||||
|
||||
const {
|
||||
ciphertext: encryptedBindPass,
|
||||
iv: bindPassIV,
|
||||
tag: bindPassTag
|
||||
} = client.encryptSymmetric(bindPass, key);
|
||||
|
||||
const {
|
||||
ciphertext: encryptedCACert,
|
||||
iv: caCertIV,
|
||||
tag: caCertTag
|
||||
} = client.encryptSymmetric(caCert, key);
|
||||
|
||||
const ldapConfig = await new LDAPConfig({
|
||||
organization: new Types.ObjectId(organizationId),
|
||||
isActive,
|
||||
url,
|
||||
encryptedBindDN,
|
||||
bindDNIV,
|
||||
bindDNTag,
|
||||
encryptedBindPass,
|
||||
bindPassIV,
|
||||
bindPassTag,
|
||||
searchBase,
|
||||
encryptedCACert,
|
||||
caCertIV,
|
||||
caCertTag
|
||||
}).save();
|
||||
|
||||
return res.status(200).send(ldapConfig);
|
||||
}
|
@ -4,9 +4,9 @@ import { IUser, Key, Membership, MembershipOrg, User, Workspace } from "../../mo
|
||||
import { EventType, Role } from "../../ee/models";
|
||||
import { deleteMembership as deleteMember, findMembership } from "../../helpers/membership";
|
||||
import { sendMail } from "../../helpers/nodemailer";
|
||||
import { ACCEPTED, ADMIN, CUSTOM, MEMBER, NO_ACCESS, VIEWER } from "../../variables";
|
||||
import { ACCEPTED, ADMIN, CUSTOM, MEMBER, VIEWER } from "../../variables";
|
||||
import { getSiteURL } from "../../config";
|
||||
import { EEAuditLogService, EELicenseService } from "../../ee/services";
|
||||
import { EEAuditLogService } from "../../ee/services";
|
||||
import { validateRequest } from "../../helpers/validation";
|
||||
import * as reqValidator from "../../validation/membership";
|
||||
import {
|
||||
@ -129,7 +129,7 @@ export const changeMembershipRole = async (req: Request, res: Response) => {
|
||||
ProjectPermissionSub.Member
|
||||
);
|
||||
|
||||
const isCustomRole = ![ADMIN, MEMBER, VIEWER, NO_ACCESS].includes(role);
|
||||
const isCustomRole = ![ADMIN, MEMBER, VIEWER].includes(role);
|
||||
if (isCustomRole) {
|
||||
const wsRole = await Role.findOne({
|
||||
slug: role,
|
||||
@ -137,13 +137,6 @@ export const changeMembershipRole = async (req: Request, res: Response) => {
|
||||
workspace: membershipToChangeRole.workspace
|
||||
});
|
||||
if (!wsRole) throw BadRequestError({ message: "Role not found" });
|
||||
|
||||
const plan = await EELicenseService.getPlan(wsRole.organization);
|
||||
|
||||
if (!plan.rbac) return res.status(400).send({
|
||||
message: "Failed to assign custom role due to RBAC restriction. Upgrade plan to assign custom role to member."
|
||||
});
|
||||
|
||||
const membership = await Membership.findByIdAndUpdate(membershipId, {
|
||||
role: CUSTOM,
|
||||
customRole: wsRole
|
||||
|
@ -21,7 +21,7 @@ import { validateRequest } from "../../helpers/validation";
|
||||
import {
|
||||
OrgPermissionActions,
|
||||
OrgPermissionSubjects,
|
||||
getAuthDataOrgPermissions
|
||||
getUserOrgPermissions
|
||||
} from "../../ee/services/RoleService";
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
@ -44,12 +44,11 @@ export const deleteMembershipOrg = async (req: Request, _res: Response) => {
|
||||
if (!membershipOrgToDelete) {
|
||||
throw new Error("Failed to delete organization membership that doesn't exist");
|
||||
}
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: membershipOrgToDelete.organization
|
||||
});
|
||||
|
||||
const { permission, membership: membershipOrg } = await getUserOrgPermissions(
|
||||
req.user._id,
|
||||
membershipOrgToDelete.organization.toString()
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Delete,
|
||||
OrgPermissionSubjects.Member
|
||||
@ -61,7 +60,7 @@ export const deleteMembershipOrg = async (req: Request, _res: Response) => {
|
||||
});
|
||||
|
||||
await updateSubscriptionOrgQuantity({
|
||||
organizationId: membershipOrgToDelete.organization.toString()
|
||||
organizationId: membershipOrg.organization.toString()
|
||||
});
|
||||
|
||||
return membershipOrgToDelete;
|
||||
@ -97,11 +96,7 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
|
||||
body: { inviteeEmail, organizationId }
|
||||
} = await validateRequest(reqValidator.InviteUserToOrgv1, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Create,
|
||||
OrgPermissionSubjects.Member
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { Request, Response } from "express";
|
||||
import { Types } from "mongoose";
|
||||
import {
|
||||
IncidentContactOrg,
|
||||
Membership,
|
||||
@ -15,7 +14,7 @@ import { ACCEPTED } from "../../variables";
|
||||
import {
|
||||
OrgPermissionActions,
|
||||
OrgPermissionSubjects,
|
||||
getAuthDataOrgPermissions
|
||||
getUserOrgPermissions
|
||||
} from "../../ee/services/RoleService";
|
||||
import { OrganizationNotFoundError } from "../../utils/errors";
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
@ -45,10 +44,7 @@ export const getOrganization = async (req: Request, res: Response) => {
|
||||
} = await validateRequest(reqValidator.GetOrgv1, req);
|
||||
|
||||
// ensure user has membership
|
||||
await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
})
|
||||
await getUserOrgPermissions(req.user._id, organizationId);
|
||||
|
||||
const organization = await Organization.findById(organizationId);
|
||||
if (!organization) {
|
||||
@ -72,12 +68,8 @@ export const getOrganizationMembers = async (req: Request, res: Response) => {
|
||||
const {
|
||||
params: { organizationId }
|
||||
} = await validateRequest(reqValidator.GetOrgMembersv1, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
|
||||
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Read,
|
||||
OrgPermissionSubjects.Member
|
||||
@ -103,10 +95,7 @@ export const getOrganizationWorkspaces = async (req: Request, res: Response) =>
|
||||
params: { organizationId }
|
||||
} = await validateRequest(reqValidator.GetOrgWorkspacesv1, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
})
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Read,
|
||||
OrgPermissionSubjects.Workspace
|
||||
@ -148,10 +137,7 @@ export const changeOrganizationName = async (req: Request, res: Response) => {
|
||||
body: { name }
|
||||
} = await validateRequest(reqValidator.ChangeOrgNamev1, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Edit,
|
||||
OrgPermissionSubjects.Settings
|
||||
@ -186,10 +172,7 @@ export const getOrganizationIncidentContacts = async (req: Request, res: Respons
|
||||
params: { organizationId }
|
||||
} = await validateRequest(reqValidator.GetOrgIncidentContactv1, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Read,
|
||||
OrgPermissionSubjects.IncidentAccount
|
||||
@ -216,10 +199,7 @@ export const addOrganizationIncidentContact = async (req: Request, res: Response
|
||||
body: { email }
|
||||
} = await validateRequest(reqValidator.CreateOrgIncideContact, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Create,
|
||||
OrgPermissionSubjects.IncidentAccount
|
||||
@ -248,10 +228,7 @@ export const deleteOrganizationIncidentContact = async (req: Request, res: Respo
|
||||
body: { email }
|
||||
} = await validateRequest(reqValidator.DelOrgIncideContact, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Delete,
|
||||
OrgPermissionSubjects.IncidentAccount
|
||||
@ -280,10 +257,7 @@ export const createOrganizationPortalSession = async (req: Request, res: Respons
|
||||
params: { organizationId }
|
||||
} = await validateRequest(reqValidator.GetOrgPlanBillingInfov1, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Edit,
|
||||
OrgPermissionSubjects.Billing
|
||||
@ -347,10 +321,7 @@ export const getOrganizationMembersAndTheirWorkspaces = async (req: Request, res
|
||||
params: { organizationId }
|
||||
} = await validateRequest(reqValidator.GetOrgMembersv1, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Read,
|
||||
OrgPermissionSubjects.Member
|
||||
|
@ -111,17 +111,11 @@ export const createSecretImp = async (req: Request, res: Response) => {
|
||||
authData: req.authData,
|
||||
workspaceId: new Types.ObjectId(workspaceId)
|
||||
});
|
||||
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
subject(ProjectPermissionSub.Secrets, { environment, secretPath: directory })
|
||||
);
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
subject(ProjectPermissionSub.Secrets, { environment: secretImport.environment, secretPath: secretImport.secretPath })
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
const folders = await Folder.findOne({
|
||||
@ -329,7 +323,7 @@ export const updateSecretImport = async (req: Request, res: Response) => {
|
||||
authData: req.authData,
|
||||
workspaceId: importSecDoc.workspace
|
||||
});
|
||||
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Edit,
|
||||
subject(ProjectPermissionSub.Secrets, {
|
||||
@ -337,13 +331,6 @@ export const updateSecretImport = async (req: Request, res: Response) => {
|
||||
secretPath
|
||||
})
|
||||
);
|
||||
|
||||
secretImports.forEach(({ environment, secretPath }) => {
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
subject(ProjectPermissionSub.Secrets, { environment, secretPath })
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
const orderBefore = importSecDoc.imports;
|
||||
@ -466,7 +453,7 @@ export const deleteSecretImport = async (req: Request, res: Response) => {
|
||||
authData: req.authData,
|
||||
workspaceId: importSecDoc.workspace
|
||||
});
|
||||
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Delete,
|
||||
subject(ProjectPermissionSub.Secrets, {
|
||||
@ -633,7 +620,7 @@ export const getAllSecretsFromImport = async (req: Request, res: Response) => {
|
||||
authData: req.authData,
|
||||
workspaceId: new Types.ObjectId(workspaceId)
|
||||
});
|
||||
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
subject(ProjectPermissionSub.Secrets, {
|
||||
@ -690,7 +677,7 @@ export const getAllSecretsFromImport = async (req: Request, res: Response) => {
|
||||
authData: req.authData,
|
||||
workspaceId: importSecDoc.workspace
|
||||
});
|
||||
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
subject(ProjectPermissionSub.Secrets, {
|
||||
|
@ -21,7 +21,7 @@ import * as reqValidator from "../../validation/secretScanning";
|
||||
import {
|
||||
OrgPermissionActions,
|
||||
OrgPermissionSubjects,
|
||||
getAuthDataOrgPermissions
|
||||
getUserOrgPermissions
|
||||
} from "../../ee/services/RoleService";
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
@ -37,11 +37,8 @@ export const createInstallationSession = async (req: Request, res: Response) =>
|
||||
message: "Failed to find organization"
|
||||
});
|
||||
}
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Create,
|
||||
OrgPermissionSubjects.SecretScanning
|
||||
@ -73,12 +70,11 @@ export const linkInstallationToOrganization = async (req: Request, res: Response
|
||||
if (!installationSession) {
|
||||
throw UnauthorizedRequestError();
|
||||
}
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: installationSession.organization
|
||||
});
|
||||
|
||||
const { permission } = await getUserOrgPermissions(
|
||||
req.user._id,
|
||||
installationSession.organization.toString()
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Edit,
|
||||
OrgPermissionSubjects.SecretScanning
|
||||
@ -146,10 +142,7 @@ export const getRisksForOrganization = async (req: Request, res: Response) => {
|
||||
params: { organizationId }
|
||||
} = await validateRequest(reqValidator.GetOrgRisksv1, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Read,
|
||||
OrgPermissionSubjects.SecretScanning
|
||||
@ -169,10 +162,7 @@ export const updateRisksStatus = async (req: Request, res: Response) => {
|
||||
body: { status }
|
||||
} = await validateRequest(reqValidator.UpdateRiskStatusv1, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Edit,
|
||||
OrgPermissionSubjects.SecretScanning
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -17,7 +17,7 @@ import { OrganizationNotFoundError } from "../../utils/errors";
|
||||
import {
|
||||
OrgPermissionActions,
|
||||
OrgPermissionSubjects,
|
||||
getAuthDataOrgPermissions
|
||||
getUserOrgPermissions
|
||||
} from "../../ee/services/RoleService";
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { validateRequest } from "../../helpers/validation";
|
||||
@ -152,10 +152,7 @@ export const createWorkspace = async (req: Request, res: Response) => {
|
||||
});
|
||||
}
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Create,
|
||||
OrgPermissionSubjects.Workspace
|
||||
|
@ -204,16 +204,20 @@ export const login2 = async (req: Request, res: Response) => {
|
||||
* @param res
|
||||
*/
|
||||
export const sendMfaToken = async (req: Request, res: Response) => {
|
||||
const {
|
||||
body: { email }
|
||||
} = await validateRequest(reqValidator.SendMfaTokenV2, req);
|
||||
|
||||
const code = await TokenService.createToken({
|
||||
type: TOKEN_EMAIL_MFA,
|
||||
email: req.user.email
|
||||
email
|
||||
});
|
||||
|
||||
// send MFA code [code] to [email]
|
||||
await sendMail({
|
||||
template: "emailMfa.handlebars",
|
||||
subjectLine: "Infisical MFA code",
|
||||
recipients: [req.user.email],
|
||||
recipients: [email],
|
||||
substitutions: {
|
||||
code
|
||||
}
|
||||
@ -232,17 +236,17 @@ export const sendMfaToken = async (req: Request, res: Response) => {
|
||||
*/
|
||||
export const verifyMfaToken = async (req: Request, res: Response) => {
|
||||
const {
|
||||
body: { mfaToken }
|
||||
body: { email, mfaToken }
|
||||
} = await validateRequest(reqValidator.VerifyMfaTokenV2, req);
|
||||
|
||||
await TokenService.validateToken({
|
||||
type: TOKEN_EMAIL_MFA,
|
||||
email: req.user.email,
|
||||
email,
|
||||
token: mfaToken
|
||||
});
|
||||
|
||||
const user = await User.findOne({
|
||||
email: req.user.email
|
||||
email
|
||||
}).select(
|
||||
"+salt +verifier +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag +publicKey +encryptedPrivateKey +iv +tag +devices"
|
||||
);
|
||||
|
@ -1,15 +1,6 @@
|
||||
import { Request, Response } from "express";
|
||||
import { Types } from "mongoose";
|
||||
import {
|
||||
IWorkspace,
|
||||
Identity,
|
||||
IdentityMembership,
|
||||
IdentityMembershipOrg,
|
||||
Membership,
|
||||
MembershipOrg,
|
||||
User,
|
||||
Workspace
|
||||
} from "../../models";
|
||||
import { Membership, MembershipOrg, Workspace } from "../../models";
|
||||
import { Role } from "../../ee/models";
|
||||
import { deleteMembershipOrg } from "../../helpers/membershipOrg";
|
||||
import {
|
||||
@ -18,16 +9,15 @@ import {
|
||||
updateSubscriptionOrgQuantity
|
||||
} from "../../helpers/organization";
|
||||
import { addMembershipsOrg } from "../../helpers/membershipOrg";
|
||||
import { BadRequestError, ResourceNotFoundError, UnauthorizedRequestError } from "../../utils/errors";
|
||||
import { ACCEPTED, ADMIN, CUSTOM, MEMBER, NO_ACCESS } from "../../variables";
|
||||
import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors";
|
||||
import { ACCEPTED, ADMIN, CUSTOM } from "../../variables";
|
||||
import * as reqValidator from "../../validation/organization";
|
||||
import { validateRequest } from "../../helpers/validation";
|
||||
import {
|
||||
OrgPermissionActions,
|
||||
OrgPermissionSubjects,
|
||||
getAuthDataOrgPermissions
|
||||
getUserOrgPermissions
|
||||
} from "../../ee/services/RoleService";
|
||||
import { EELicenseService } from "../../ee/services";
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
/**
|
||||
@ -37,12 +27,11 @@ import { ForbiddenError } from "@casl/ability";
|
||||
*/
|
||||
export const getOrganizationMemberships = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Return organization user memberships'
|
||||
#swagger.description = 'Return organization user memberships'
|
||||
#swagger.summary = 'Return organization memberships'
|
||||
#swagger.description = 'Return organization memberships'
|
||||
|
||||
#swagger.security = [{
|
||||
"apiKeyAuth": [],
|
||||
"bearerAuth": []
|
||||
"apiKeyAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['organizationId'] = {
|
||||
@ -74,10 +63,7 @@ export const getOrganizationMemberships = async (req: Request, res: Response) =>
|
||||
params: { organizationId }
|
||||
} = await validateRequest(reqValidator.GetOrgMembersv2, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Read,
|
||||
OrgPermissionSubjects.Member
|
||||
@ -99,12 +85,11 @@ export const getOrganizationMemberships = async (req: Request, res: Response) =>
|
||||
*/
|
||||
export const updateOrganizationMembership = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Update organization user membership'
|
||||
#swagger.description = 'Update organization user membership'
|
||||
#swagger.summary = 'Update organization membership'
|
||||
#swagger.description = 'Update organization membership'
|
||||
|
||||
#swagger.security = [{
|
||||
"apiKeyAuth": [],
|
||||
"bearerAuth": []
|
||||
"apiKeyAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['organizationId'] = {
|
||||
@ -156,32 +141,16 @@ export const updateOrganizationMembership = async (req: Request, res: Response)
|
||||
params: { organizationId, membershipId },
|
||||
body: { role }
|
||||
} = await validateRequest(reqValidator.UpdateOrgMemberv2, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Edit,
|
||||
OrgPermissionSubjects.Member
|
||||
);
|
||||
|
||||
const isCustomRole = ![ADMIN, MEMBER, NO_ACCESS].includes(role);
|
||||
const isCustomRole = !["admin", "member"].includes(role);
|
||||
if (isCustomRole) {
|
||||
const orgRole = await Role.findOne({
|
||||
slug: role,
|
||||
isOrgRole: true,
|
||||
organization: new Types.ObjectId(organizationId)
|
||||
});
|
||||
|
||||
const orgRole = await Role.findOne({ slug: role, isOrgRole: true });
|
||||
if (!orgRole) throw BadRequestError({ message: "Role not found" });
|
||||
|
||||
const plan = await EELicenseService.getPlan(new Types.ObjectId(organizationId));
|
||||
|
||||
if (!plan.rbac) return res.status(400).send({
|
||||
message:
|
||||
"Failed to assign custom role due to RBAC restriction. Upgrade plan to assign custom role to member."
|
||||
});
|
||||
|
||||
const membership = await MembershipOrg.findByIdAndUpdate(membershipId, {
|
||||
role: CUSTOM,
|
||||
@ -220,12 +189,11 @@ export const updateOrganizationMembership = async (req: Request, res: Response)
|
||||
*/
|
||||
export const deleteOrganizationMembership = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Delete organization user membership'
|
||||
#swagger.description = 'Delete organization user membership'
|
||||
#swagger.summary = 'Delete organization membership'
|
||||
#swagger.description = 'Delete organization membership'
|
||||
|
||||
#swagger.security = [{
|
||||
"apiKeyAuth": [],
|
||||
"bearerAuth": []
|
||||
"apiKeyAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['organizationId'] = {
|
||||
@ -259,18 +227,7 @@ export const deleteOrganizationMembership = async (req: Request, res: Response)
|
||||
const {
|
||||
params: { organizationId, membershipId }
|
||||
} = await validateRequest(reqValidator.DeleteOrgMemberv2, req);
|
||||
|
||||
const membershipOrg = await MembershipOrg.findOne({
|
||||
_id: new Types.ObjectId(membershipId),
|
||||
organization: new Types.ObjectId(organizationId)
|
||||
});
|
||||
|
||||
if (!membershipOrg) throw ResourceNotFoundError();
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: membershipOrg.organization
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Delete,
|
||||
OrgPermissionSubjects.Member
|
||||
@ -302,8 +259,7 @@ export const getOrganizationWorkspaces = async (req: Request, res: Response) =>
|
||||
#swagger.description = 'Return projects in organization that user is part of'
|
||||
|
||||
#swagger.security = [{
|
||||
"apiKeyAuth": [],
|
||||
"bearerAuth": []
|
||||
"apiKeyAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['organizationId'] = {
|
||||
@ -331,16 +287,11 @@ export const getOrganizationWorkspaces = async (req: Request, res: Response) =>
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
const {
|
||||
params: { organizationId }
|
||||
} = await validateRequest(reqValidator.GetOrgWorkspacesv2, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Read,
|
||||
OrgPermissionSubjects.Workspace
|
||||
@ -357,27 +308,13 @@ export const getOrganizationWorkspaces = async (req: Request, res: Response) =>
|
||||
).map((w) => w._id.toString())
|
||||
);
|
||||
|
||||
let workspaces: IWorkspace[] = [];
|
||||
|
||||
if (req.authData.authPayload instanceof Identity) {
|
||||
workspaces = (
|
||||
await IdentityMembership.find({
|
||||
identity: req.authData.authPayload._id
|
||||
}).populate<{ workspace: IWorkspace }>("workspace")
|
||||
)
|
||||
.filter((m) => workspacesSet.has(m.workspace._id.toString()))
|
||||
.map((m) => m.workspace);
|
||||
}
|
||||
|
||||
if (req.authData.authPayload instanceof User) {
|
||||
workspaces = (
|
||||
await Membership.find({
|
||||
user: req.authData.authPayload._id
|
||||
}).populate<{ workspace: IWorkspace }>("workspace")
|
||||
)
|
||||
.filter((m) => workspacesSet.has(m.workspace._id.toString()))
|
||||
.map((m) => m.workspace);
|
||||
}
|
||||
const workspaces = (
|
||||
await Membership.find({
|
||||
user: req.user._id
|
||||
}).populate("workspace")
|
||||
)
|
||||
.filter((m) => workspacesSet.has(m.workspace._id.toString()))
|
||||
.map((m) => m.workspace);
|
||||
|
||||
return res.status(200).send({
|
||||
workspaces
|
||||
@ -440,66 +377,3 @@ export const deleteOrganizationById = async (req: Request, res: Response) => {
|
||||
organization
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Return list of identity memberships for organization with id [organizationId]
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const getOrganizationIdentityMemberships = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Return organization identity memberships'
|
||||
#swagger.description = 'Return organization identity memberships'
|
||||
|
||||
#swagger.security = [{
|
||||
"bearerAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['organizationId'] = {
|
||||
"description": "ID of organization",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"in": "path"
|
||||
}
|
||||
|
||||
#swagger.responses[200] = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"identityMemberships": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
$ref: "#/components/schemas/IdentityMembershipOrg"
|
||||
},
|
||||
"description": "Identity memberships of organization"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
const {
|
||||
params: { organizationId }
|
||||
} = await validateRequest(reqValidator.GetOrgIdentityMembershipsV2, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Read,
|
||||
OrgPermissionSubjects.Identity
|
||||
);
|
||||
|
||||
const identityMemberships = await IdentityMembershipOrg.find({
|
||||
organization: new Types.ObjectId(organizationId)
|
||||
}).populate("identity customRole");
|
||||
|
||||
return res.status(200).send({
|
||||
identityMemberships
|
||||
});
|
||||
}
|
@ -13,7 +13,7 @@ import {
|
||||
ProjectPermissionSub,
|
||||
getAuthDataProjectPermissions
|
||||
} from "../../ee/services/ProjectRoleService";
|
||||
import { ForbiddenError, subject } from "@casl/ability";
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { Types } from "mongoose";
|
||||
|
||||
/**
|
||||
@ -86,14 +86,6 @@ export const createServiceTokenData = async (req: Request, res: Response) => {
|
||||
ProjectPermissionSub.ServiceTokens
|
||||
);
|
||||
|
||||
scopes.forEach(({ environment, secretPath }) => {
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
subject(ProjectPermissionSub.Secrets, { environment, secretPath: secretPath })
|
||||
);
|
||||
})
|
||||
|
||||
|
||||
const secret = crypto.randomBytes(16).toString("hex");
|
||||
const secretHash = await bcrypt.hash(secret, await getSaltRounds());
|
||||
|
||||
|
@ -1,15 +1,6 @@
|
||||
import { Request, Response } from "express";
|
||||
import { Types } from "mongoose";
|
||||
import {
|
||||
IIdentity,
|
||||
IdentityMembership,
|
||||
IdentityMembershipOrg,
|
||||
Key,
|
||||
Membership,
|
||||
ServiceTokenData,
|
||||
Workspace
|
||||
} from "../../models";
|
||||
import { IRole, Role } from "../../ee/models";
|
||||
import { Key, Membership, ServiceTokenData, Workspace } from "../../models";
|
||||
import {
|
||||
pullSecrets as pull,
|
||||
v2PushSecrets as push,
|
||||
@ -25,13 +16,9 @@ import * as reqValidator from "../../validation";
|
||||
import {
|
||||
ProjectPermissionActions,
|
||||
ProjectPermissionSub,
|
||||
getAuthDataProjectPermissions,
|
||||
getWorkspaceRolePermissions,
|
||||
isAtLeastAsPrivilegedWorkspace
|
||||
getAuthDataProjectPermissions
|
||||
} from "../../ee/services/ProjectRoleService";
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { BadRequestError, ForbiddenRequestError, ResourceNotFoundError } from "../../utils/errors";
|
||||
import { ADMIN, CUSTOM, MEMBER, NO_ACCESS, VIEWER } from "../../variables";
|
||||
|
||||
interface V2PushSecret {
|
||||
type: string; // personal or shared
|
||||
@ -182,11 +169,11 @@ export const getWorkspaceKey = async (req: Request, res: Response) => {
|
||||
"apiKeyAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['workspaceId'] = {
|
||||
"description": "ID of project",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
#swagger.parameters['workspaceId'] = {
|
||||
"description": "ID of project",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
#swagger.responses[200] = {
|
||||
content: {
|
||||
@ -211,7 +198,7 @@ export const getWorkspaceKey = async (req: Request, res: Response) => {
|
||||
receiver: req.user._id
|
||||
}).populate("sender", "+publicKey");
|
||||
|
||||
if (!key) throw new Error(`getWorkspaceKey: Failed to find workspace key [workspaceId=${workspaceId}] [receiver=${req.user._id}]`);
|
||||
if (!key) throw new Error("Failed to find workspace key");
|
||||
|
||||
await EEAuditLogService.createAuditLog(
|
||||
req.authData,
|
||||
@ -249,34 +236,33 @@ export const getWorkspaceServiceTokenData = async (req: Request, res: Response)
|
||||
*/
|
||||
export const getWorkspaceMemberships = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Return project user memberships'
|
||||
#swagger.description = 'Return project user memberships'
|
||||
#swagger.summary = 'Return project memberships'
|
||||
#swagger.description = 'Return project memberships'
|
||||
|
||||
#swagger.security = [{
|
||||
"apiKeyAuth": [],
|
||||
"bearerAuth": []
|
||||
"apiKeyAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['workspaceId'] = {
|
||||
"description": "ID of project",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
#swagger.parameters['workspaceId'] = {
|
||||
"description": "ID of project",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
#swagger.responses[200] = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"memberships": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
$ref: "#/components/schemas/Membership"
|
||||
},
|
||||
"description": "Memberships of project"
|
||||
}
|
||||
}
|
||||
"properties": {
|
||||
"memberships": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
$ref: "#/components/schemas/Membership"
|
||||
},
|
||||
"description": "Memberships of project"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -313,27 +299,26 @@ export const getWorkspaceMemberships = async (req: Request, res: Response) => {
|
||||
*/
|
||||
export const updateWorkspaceMembership = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Update project user membership'
|
||||
#swagger.description = 'Update project user membership'
|
||||
#swagger.summary = 'Update project membership'
|
||||
#swagger.description = 'Update project membership'
|
||||
|
||||
#swagger.security = [{
|
||||
"apiKeyAuth": [],
|
||||
"bearerAuth": []
|
||||
"apiKeyAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['workspaceId'] = {
|
||||
"description": "ID of project",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
#swagger.parameters['workspaceId'] = {
|
||||
"description": "ID of project",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
#swagger.parameters['membershipId'] = {
|
||||
"description": "ID of project membership to update",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
#swagger.parameters['membershipId'] = {
|
||||
"description": "ID of project membership to update",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
#swagger.requestBody = {
|
||||
#swagger.requestBody = {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
@ -342,7 +327,7 @@ export const updateWorkspaceMembership = async (req: Request, res: Response) =>
|
||||
"properties": {
|
||||
"role": {
|
||||
"type": "string",
|
||||
"description": "Role to update to for project membership",
|
||||
"description": "Role of membership - either admin or member",
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -354,13 +339,13 @@ export const updateWorkspaceMembership = async (req: Request, res: Response) =>
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"membership": {
|
||||
$ref: "#/components/schemas/Membership",
|
||||
"description": "Updated membership"
|
||||
}
|
||||
}
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"membership": {
|
||||
$ref: "#/components/schemas/Membership",
|
||||
"description": "Updated membership"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -404,37 +389,36 @@ export const updateWorkspaceMembership = async (req: Request, res: Response) =>
|
||||
*/
|
||||
export const deleteWorkspaceMembership = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Delete project user membership'
|
||||
#swagger.description = 'Delete project user membership'
|
||||
#swagger.summary = 'Delete project membership'
|
||||
#swagger.description = 'Delete project membership'
|
||||
|
||||
#swagger.security = [{
|
||||
"apiKeyAuth": [],
|
||||
"bearerAuth": []
|
||||
"apiKeyAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['workspaceId'] = {
|
||||
"description": "ID of project",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
#swagger.parameters['workspaceId'] = {
|
||||
"description": "ID of project",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
#swagger.parameters['membershipId'] = {
|
||||
"description": "ID of project membership to delete",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
#swagger.parameters['membershipId'] = {
|
||||
"description": "ID of project membership to delete",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
#swagger.responses[200] = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"membership": {
|
||||
$ref: "#/components/schemas/Membership",
|
||||
"description": "Deleted membership"
|
||||
}
|
||||
}
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"membership": {
|
||||
$ref: "#/components/schemas/Membership",
|
||||
"description": "Deleted membership"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -507,377 +491,3 @@ export const toggleAutoCapitalization = async (req: Request, res: Response) => {
|
||||
workspace
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Add identity with id [identityId] to workspace
|
||||
* with id [workspaceId]
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const addIdentityToWorkspace = async (req: Request, res: Response) => {
|
||||
const {
|
||||
params: { workspaceId, identityId },
|
||||
body: {
|
||||
role
|
||||
}
|
||||
} = await validateRequest(reqValidator.AddIdentityToWorkspaceV2, req);
|
||||
|
||||
const { permission } = await getAuthDataProjectPermissions({
|
||||
authData: req.authData,
|
||||
workspaceId: new Types.ObjectId(workspaceId)
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
ProjectPermissionSub.Identity
|
||||
);
|
||||
|
||||
let identityMembership = await IdentityMembership.findOne({
|
||||
identity: new Types.ObjectId(identityId),
|
||||
workspace: new Types.ObjectId(workspaceId)
|
||||
});
|
||||
|
||||
if (identityMembership) throw BadRequestError({
|
||||
message: `Identity with id ${identityId} already exists in project with id ${workspaceId}`
|
||||
});
|
||||
|
||||
|
||||
const workspace = await Workspace.findById(workspaceId);
|
||||
if (!workspace) throw ResourceNotFoundError();
|
||||
|
||||
const identityMembershipOrg = await IdentityMembershipOrg.findOne({
|
||||
identity: new Types.ObjectId(identityId),
|
||||
organization: workspace.organization
|
||||
});
|
||||
|
||||
if (!identityMembershipOrg) throw ResourceNotFoundError({
|
||||
message: `Failed to find identity with id ${identityId}`
|
||||
});
|
||||
|
||||
if (!identityMembershipOrg.organization.equals(workspace.organization)) throw BadRequestError({
|
||||
message: "Failed to add identity to project in another organization"
|
||||
});
|
||||
|
||||
const rolePermission = await getWorkspaceRolePermissions(role, workspaceId);
|
||||
const isAsPrivilegedAsIntendedRole = isAtLeastAsPrivilegedWorkspace(permission, rolePermission);
|
||||
|
||||
if (!isAsPrivilegedAsIntendedRole) throw ForbiddenRequestError({
|
||||
message: "Failed to add identity to project with more privileged role"
|
||||
});
|
||||
|
||||
let customRole;
|
||||
if (role) {
|
||||
const isCustomRole = ![ADMIN, MEMBER, VIEWER, NO_ACCESS].includes(role);
|
||||
if (isCustomRole) {
|
||||
customRole = await Role.findOne({
|
||||
slug: role,
|
||||
isOrgRole: false,
|
||||
workspace: new Types.ObjectId(workspaceId)
|
||||
});
|
||||
|
||||
if (!customRole) throw BadRequestError({ message: "Role not found" });
|
||||
}
|
||||
}
|
||||
|
||||
identityMembership = await new IdentityMembership({
|
||||
identity: identityMembershipOrg.identity,
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
role: customRole ? CUSTOM : role,
|
||||
customRole
|
||||
}).save();
|
||||
|
||||
return res.status(200).send({
|
||||
identityMembership
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update role of identity with id [identityId] in workspace
|
||||
* with id [workspaceId] to [role]
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const updateIdentityWorkspaceRole = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Update project identity membership'
|
||||
#swagger.description = 'Update project identity membership'
|
||||
|
||||
#swagger.security = [{
|
||||
"bearerAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['workspaceId'] = {
|
||||
"description": "ID of project",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
#swagger.parameters['identityId'] = {
|
||||
"description": "ID of identity whose membership to update in project",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
#swagger.requestBody = {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"role": {
|
||||
"type": "string",
|
||||
"description": "Role to update to for identity project membership",
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#swagger.responses[200] = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"identityMembership": {
|
||||
$ref: "#/components/schemas/IdentityMembership",
|
||||
"description": "Updated identity membership"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
const {
|
||||
params: { workspaceId, identityId },
|
||||
body: {
|
||||
role
|
||||
}
|
||||
} = await validateRequest(reqValidator.UpdateIdentityWorkspaceRoleV2, req);
|
||||
|
||||
const { permission } = await getAuthDataProjectPermissions({
|
||||
authData: req.authData,
|
||||
workspaceId: new Types.ObjectId(workspaceId)
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Edit,
|
||||
ProjectPermissionSub.Identity
|
||||
);
|
||||
|
||||
let identityMembership = await IdentityMembership
|
||||
.findOne({
|
||||
identity: new Types.ObjectId(identityId),
|
||||
workspace: new Types.ObjectId(workspaceId)
|
||||
})
|
||||
.populate<{
|
||||
identity: IIdentity,
|
||||
customRole: IRole
|
||||
}>("identity customRole");
|
||||
|
||||
if (!identityMembership) throw BadRequestError({
|
||||
message: `Identity with id ${identityId} does not exist in project with id ${workspaceId}`
|
||||
});
|
||||
|
||||
const identityRolePermission = await getWorkspaceRolePermissions(
|
||||
identityMembership?.customRole?.slug ?? identityMembership.role,
|
||||
identityMembership.workspace.toString()
|
||||
);
|
||||
const isAsPrivilegedAsIdentity = isAtLeastAsPrivilegedWorkspace(permission, identityRolePermission);
|
||||
if (!isAsPrivilegedAsIdentity) throw ForbiddenRequestError({
|
||||
message: "Failed to update role of more privileged identity"
|
||||
});
|
||||
|
||||
const rolePermission = await getWorkspaceRolePermissions(role, workspaceId);
|
||||
const isAsPrivilegedAsIntendedRole = isAtLeastAsPrivilegedWorkspace(permission, rolePermission);
|
||||
|
||||
if (!isAsPrivilegedAsIntendedRole) throw ForbiddenRequestError({
|
||||
message: "Failed to update identity to a more privileged role"
|
||||
});
|
||||
|
||||
let customRole;
|
||||
if (role) {
|
||||
const isCustomRole = ![ADMIN, MEMBER, VIEWER, NO_ACCESS].includes(role);
|
||||
if (isCustomRole) {
|
||||
customRole = await Role.findOne({
|
||||
slug: role,
|
||||
isOrgRole: false,
|
||||
workspace: new Types.ObjectId(workspaceId)
|
||||
});
|
||||
|
||||
if (!customRole) throw BadRequestError({ message: "Role not found" });
|
||||
}
|
||||
}
|
||||
|
||||
identityMembership = await IdentityMembership.findOneAndUpdate(
|
||||
{
|
||||
identity: identityMembership.identity._id,
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
},
|
||||
{
|
||||
role: customRole ? CUSTOM : role,
|
||||
customRole
|
||||
},
|
||||
{
|
||||
new: true
|
||||
}
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
identityMembership
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete identity with id [identityId] from workspace
|
||||
* with id [workspaceId]
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const deleteIdentityFromWorkspace = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Delete project identity membership'
|
||||
#swagger.description = 'Delete project identity membership'
|
||||
|
||||
#swagger.security = [{
|
||||
"bearerAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['workspaceId'] = {
|
||||
"description": "ID of project",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
#swagger.parameters['identityId'] = {
|
||||
"description": "ID of identity whose membership to delete in project",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
#swagger.responses[200] = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"identityMembership": {
|
||||
$ref: "#/components/schemas/IdentityMembership",
|
||||
"description": "Deleted identity membership"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
const {
|
||||
params: { workspaceId, identityId }
|
||||
} = await validateRequest(reqValidator.DeleteIdentityFromWorkspaceV2, req);
|
||||
|
||||
const { permission } = await getAuthDataProjectPermissions({
|
||||
authData: req.authData,
|
||||
workspaceId: new Types.ObjectId(workspaceId)
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Delete,
|
||||
ProjectPermissionSub.Identity
|
||||
);
|
||||
|
||||
const identityMembership = await IdentityMembership
|
||||
.findOne({
|
||||
identity: new Types.ObjectId(identityId),
|
||||
workspace: new Types.ObjectId(workspaceId)
|
||||
})
|
||||
.populate<{
|
||||
identity: IIdentity,
|
||||
customRole: IRole
|
||||
}>("identity customRole");
|
||||
|
||||
if (!identityMembership) throw ResourceNotFoundError({
|
||||
message: `Identity with id ${identityId} does not exist in project with id ${workspaceId}`
|
||||
});
|
||||
|
||||
const identityRolePermission = await getWorkspaceRolePermissions(
|
||||
identityMembership?.customRole?.slug ?? identityMembership.role,
|
||||
identityMembership.workspace.toString()
|
||||
);
|
||||
const isAsPrivilegedAsIdentity = isAtLeastAsPrivilegedWorkspace(permission, identityRolePermission);
|
||||
if (!isAsPrivilegedAsIdentity) throw ForbiddenRequestError({
|
||||
message: "Failed to remove more privileged identity from project"
|
||||
});
|
||||
|
||||
await IdentityMembership.findByIdAndDelete(identityMembership._id);
|
||||
|
||||
return res.status(200).send({
|
||||
identityMembership
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return list of identity memberships for workspace with id [workspaceId]
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const getWorkspaceIdentityMemberships = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Return project identity memberships'
|
||||
#swagger.description = 'Return project identity memberships'
|
||||
|
||||
#swagger.security = [{
|
||||
"bearerAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['workspaceId'] = {
|
||||
"description": "ID of project",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"in": "path"
|
||||
}
|
||||
|
||||
#swagger.responses[200] = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"identityMemberships": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
$ref: "#/components/schemas/IdentityMembership"
|
||||
},
|
||||
"description": "Identity memberships of project"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
const {
|
||||
params: { workspaceId }
|
||||
} = await validateRequest(reqValidator.GetWorkspaceIdentityMembersV2, req);
|
||||
|
||||
const { permission } = await getAuthDataProjectPermissions({
|
||||
authData: req.authData,
|
||||
workspaceId: new Types.ObjectId(workspaceId)
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.Identity
|
||||
);
|
||||
|
||||
const identityMemberships = await IdentityMembership.find({
|
||||
workspace: new Types.ObjectId(workspaceId)
|
||||
}).populate("identity customRole");
|
||||
|
||||
return res.status(200).send({
|
||||
identityMemberships
|
||||
});
|
||||
}
|
@ -94,7 +94,7 @@ const checkSecretsPermission = async ({
|
||||
});
|
||||
return { authVerifier: () => true };
|
||||
}
|
||||
case ActorType.IDENTITY: {
|
||||
case ActorType.SERVICE_V3: {
|
||||
const { permission } = await getAuthDataProjectPermissions({
|
||||
authData,
|
||||
workspaceId: new Types.ObjectId(workspaceId)
|
||||
@ -348,7 +348,7 @@ export const getSecretByNameRaw = async (req: Request, res: Response) => {
|
||||
}
|
||||
*/
|
||||
const {
|
||||
query: { secretPath, environment, workspaceId, type, include_imports, version },
|
||||
query: { secretPath, environment, workspaceId, type, include_imports },
|
||||
params: { secretName }
|
||||
} = await validateRequest(reqValidator.GetSecretByNameRawV3, req);
|
||||
|
||||
@ -371,8 +371,7 @@ export const getSecretByNameRaw = async (req: Request, res: Response) => {
|
||||
type,
|
||||
secretPath,
|
||||
authData: req.authData,
|
||||
include_imports,
|
||||
version
|
||||
include_imports
|
||||
});
|
||||
|
||||
const key = await BotService.getWorkspaceKeyWithBot({
|
||||
@ -866,7 +865,7 @@ export const getSecrets = async (req: Request, res: Response) => {
|
||||
*/
|
||||
export const getSecretByName = async (req: Request, res: Response) => {
|
||||
const {
|
||||
query: { secretPath, environment, workspaceId, type, include_imports, version },
|
||||
query: { secretPath, environment, workspaceId, type, include_imports },
|
||||
params: { secretName }
|
||||
} = await validateRequest(reqValidator.GetSecretByNameV3, req);
|
||||
|
||||
@ -889,8 +888,7 @@ export const getSecretByName = async (req: Request, res: Response) => {
|
||||
type,
|
||||
secretPath,
|
||||
authData: req.authData,
|
||||
include_imports,
|
||||
version
|
||||
include_imports
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
|
@ -101,15 +101,14 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
||||
salt,
|
||||
verifier
|
||||
});
|
||||
|
||||
|
||||
if (!user) throw new Error("Failed to complete account for non-existent user"); // ensure user is non-null
|
||||
|
||||
// this might need to consider LDAP
|
||||
const hasOrgAuthMethodEnabled = user.authMethods.some((authMethod: AuthMethod) =>
|
||||
[AuthMethod.OKTA_SAML, AuthMethod.AZURE_SAML, AuthMethod.JUMPCLOUD_SAML, AuthMethod.LDAP].includes(authMethod)
|
||||
const hasSamlEnabled = user.authMethods.some((authMethod: AuthMethod) =>
|
||||
[AuthMethod.OKTA_SAML, AuthMethod.AZURE_SAML, AuthMethod.JUMPCLOUD_SAML].includes(authMethod)
|
||||
);
|
||||
|
||||
if (!hasOrgAuthMethodEnabled) {
|
||||
if (!hasSamlEnabled) {
|
||||
// TODO: modify this part
|
||||
// initialize default organization and workspace
|
||||
await initializeDefaultOrg({
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Request, Response } from "express";
|
||||
import { Types } from "mongoose";
|
||||
import { validateRequest } from "../../helpers/validation";
|
||||
import { Membership, Secret, User } from "../../models";
|
||||
import { Membership, Secret, ServiceTokenDataV3, User } from "../../models";
|
||||
import { SecretService } from "../../services";
|
||||
import { getAuthDataProjectPermissions } from "../../ee/services/ProjectRoleService";
|
||||
import { UnauthorizedRequestError } from "../../utils/errors";
|
||||
@ -140,3 +140,17 @@ export const nameWorkspaceSecrets = async (req: Request, res: Response) => {
|
||||
message: "Successfully named workspace secrets"
|
||||
});
|
||||
};
|
||||
|
||||
export const getWorkspaceServiceTokenData = async (req: Request, res: Response) => {
|
||||
const {
|
||||
params: { workspaceId }
|
||||
} = await validateRequest(reqValidator.GetWorkspaceServiceTokenDataV3, req);
|
||||
|
||||
const serviceTokenData = await ServiceTokenDataV3.find({
|
||||
workspace: new Types.ObjectId(workspaceId)
|
||||
}).populate("customRole");
|
||||
|
||||
return res.status(200).send({
|
||||
serviceTokenData
|
||||
});
|
||||
}
|
@ -1,460 +0,0 @@
|
||||
import { Request, Response } from "express";
|
||||
import { Types } from "mongoose";
|
||||
import {
|
||||
IIdentity,
|
||||
Identity,
|
||||
IdentityAccessToken,
|
||||
IdentityMembership,
|
||||
IdentityMembershipOrg,
|
||||
IdentityUniversalAuth,
|
||||
IdentityUniversalAuthClientSecret,
|
||||
Organization
|
||||
} from "../../../models";
|
||||
import {
|
||||
EventType,
|
||||
IRole,
|
||||
Role
|
||||
} from "../../models";
|
||||
import { validateRequest } from "../../../helpers/validation";
|
||||
import * as reqValidator from "../../../validation/identities";
|
||||
import {
|
||||
getAuthDataOrgPermissions,
|
||||
getOrgRolePermissions,
|
||||
isAtLeastAsPrivilegedOrg
|
||||
} from "../../services/RoleService";
|
||||
import {
|
||||
BadRequestError,
|
||||
ForbiddenRequestError,
|
||||
ResourceNotFoundError,
|
||||
} from "../../../utils/errors";
|
||||
import { ADMIN, CUSTOM, MEMBER, NO_ACCESS } from "../../../variables";
|
||||
import {
|
||||
OrgPermissionActions,
|
||||
OrgPermissionSubjects
|
||||
} from "../../services/RoleService";
|
||||
import { EEAuditLogService } from "../../services";
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
/**
|
||||
* Create identity
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const createIdentity = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Create identity'
|
||||
#swagger.description = 'Create identity'
|
||||
|
||||
#swagger.security = [{
|
||||
"bearerAuth": []
|
||||
}]
|
||||
|
||||
#swagger.requestBody = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Name of entity to create",
|
||||
"example": "development"
|
||||
},
|
||||
"organizationId": {
|
||||
"type": "string",
|
||||
"description": "ID of organization where to create identity",
|
||||
"example": "dev-environment"
|
||||
},
|
||||
"role": {
|
||||
"type": "string",
|
||||
"description": "Role to assume for organization membership",
|
||||
"example": "no-access"
|
||||
}
|
||||
},
|
||||
"required": ["name", "organizationId", "role"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#swagger.responses[200] = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"identity": {
|
||||
$ref: '#/definitions/Identity'
|
||||
}
|
||||
},
|
||||
"description": "Details of the created identity"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
const {
|
||||
body: {
|
||||
name,
|
||||
organizationId,
|
||||
role
|
||||
}
|
||||
} = await validateRequest(reqValidator.CreateIdentityV1, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Create,
|
||||
OrgPermissionSubjects.Identity
|
||||
);
|
||||
|
||||
const rolePermission = await getOrgRolePermissions(role, organizationId);
|
||||
const hasRequiredPrivileges = isAtLeastAsPrivilegedOrg(permission, rolePermission);
|
||||
|
||||
if (!hasRequiredPrivileges) throw ForbiddenRequestError({
|
||||
message: "Failed to create a more privileged identity"
|
||||
});
|
||||
|
||||
const organization = await Organization.findById(organizationId);
|
||||
if (!organization) throw BadRequestError({ message: `Organization with id ${organizationId} not found` });
|
||||
|
||||
const isCustomRole = ![ADMIN, MEMBER, NO_ACCESS].includes(role);
|
||||
|
||||
let customRole;
|
||||
if (isCustomRole) {
|
||||
customRole = await Role.findOne({
|
||||
slug: role,
|
||||
isOrgRole: true,
|
||||
organization: new Types.ObjectId(organizationId)
|
||||
});
|
||||
|
||||
if (!customRole) throw BadRequestError({ message: "Role not found" });
|
||||
}
|
||||
|
||||
const identity = await new Identity({
|
||||
name
|
||||
}).save();
|
||||
|
||||
await new IdentityMembershipOrg({
|
||||
identity: identity._id,
|
||||
organization: new Types.ObjectId(organizationId),
|
||||
role: isCustomRole ? CUSTOM : role,
|
||||
customRole
|
||||
}).save();
|
||||
|
||||
await EEAuditLogService.createAuditLog(
|
||||
req.authData,
|
||||
{
|
||||
type: EventType.CREATE_IDENTITY,
|
||||
metadata: {
|
||||
identityId: identity._id.toString(),
|
||||
name
|
||||
}
|
||||
},
|
||||
{
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
}
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
identity
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update identity with id [identityId]
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const updateIdentity = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Update identity'
|
||||
#swagger.description = 'Update identity'
|
||||
|
||||
#swagger.security = [{
|
||||
"bearerAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['identityId'] = {
|
||||
"description": "ID of identity to update",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"in": "path"
|
||||
}
|
||||
|
||||
#swagger.requestBody = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Name of entity to update to",
|
||||
"example": "development"
|
||||
},
|
||||
"role": {
|
||||
"type": "string",
|
||||
"description": "Role to update to for organization membership",
|
||||
"example": "no-access"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#swagger.responses[200] = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"identity": {
|
||||
$ref: '#/definitions/Identity'
|
||||
}
|
||||
},
|
||||
"description": "Details of the updated identity"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
const {
|
||||
params: { identityId },
|
||||
body: {
|
||||
name,
|
||||
role
|
||||
}
|
||||
} = await validateRequest(reqValidator.UpdateIdentityV1, req);
|
||||
|
||||
const identityMembershipOrg = await IdentityMembershipOrg
|
||||
.findOne({
|
||||
identity: new Types.ObjectId(identityId)
|
||||
})
|
||||
.populate<{
|
||||
identity: IIdentity,
|
||||
customRole: IRole
|
||||
}>("identity customRole");
|
||||
|
||||
if (!identityMembershipOrg) throw ResourceNotFoundError({
|
||||
message: `Failed to find identity with id ${identityId}`
|
||||
});
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: identityMembershipOrg.organization
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Edit,
|
||||
OrgPermissionSubjects.Identity
|
||||
);
|
||||
|
||||
const identityRolePermission = await getOrgRolePermissions(
|
||||
identityMembershipOrg?.customRole?.slug ?? identityMembershipOrg.role,
|
||||
identityMembershipOrg.organization.toString()
|
||||
);
|
||||
const hasRequiredPrivileges = isAtLeastAsPrivilegedOrg(permission, identityRolePermission);
|
||||
if (!hasRequiredPrivileges) throw ForbiddenRequestError({
|
||||
message: "Failed to update more privileged identity"
|
||||
});
|
||||
|
||||
if (role) {
|
||||
const rolePermission = await getOrgRolePermissions(role, identityMembershipOrg.organization.toString());
|
||||
const hasRequiredPrivileges = isAtLeastAsPrivilegedOrg(permission, rolePermission);
|
||||
|
||||
if (!hasRequiredPrivileges) throw ForbiddenRequestError({
|
||||
message: "Failed to update identity to a more privileged role"
|
||||
});
|
||||
}
|
||||
|
||||
let customRole;
|
||||
if (role) {
|
||||
const isCustomRole = ![ADMIN, MEMBER, NO_ACCESS].includes(role);
|
||||
if (isCustomRole) {
|
||||
customRole = await Role.findOne({
|
||||
slug: role,
|
||||
isOrgRole: true,
|
||||
organization: identityMembershipOrg.organization
|
||||
});
|
||||
|
||||
if (!customRole) throw BadRequestError({ message: "Role not found" });
|
||||
}
|
||||
}
|
||||
|
||||
const identity = await Identity.findByIdAndUpdate(
|
||||
identityId,
|
||||
{
|
||||
name,
|
||||
},
|
||||
{
|
||||
new: true
|
||||
}
|
||||
);
|
||||
|
||||
if (!identity) throw BadRequestError({
|
||||
message: `Failed to update identity with id ${identityId}`
|
||||
});
|
||||
|
||||
await IdentityMembershipOrg.findOneAndUpdate(
|
||||
{
|
||||
identity: identity._id
|
||||
},
|
||||
{
|
||||
role: customRole ? CUSTOM : role,
|
||||
...(customRole ? {
|
||||
customRole
|
||||
} : {}),
|
||||
...(role && !customRole ? { // non-custom role
|
||||
$unset: {
|
||||
customRole: 1
|
||||
}
|
||||
} : {})
|
||||
},
|
||||
{
|
||||
new: true
|
||||
}
|
||||
);
|
||||
|
||||
await EEAuditLogService.createAuditLog(
|
||||
req.authData,
|
||||
{
|
||||
type: EventType.UPDATE_IDENTITY,
|
||||
metadata: {
|
||||
identityId: identity._id.toString(),
|
||||
name: identity.name,
|
||||
}
|
||||
},
|
||||
{
|
||||
organizationId: identityMembershipOrg.organization
|
||||
}
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
identity
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete identity with id [identityId]
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const deleteIdentity = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Delete identity'
|
||||
#swagger.description = 'Delete identity'
|
||||
|
||||
#swagger.security = [{
|
||||
"bearerAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['identityId'] = {
|
||||
"description": "ID of identity",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"in": "path"
|
||||
}
|
||||
|
||||
#swagger.responses[200] = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"identity": {
|
||||
$ref: '#/definitions/Identity'
|
||||
}
|
||||
},
|
||||
"description": "Details of the deleted identity"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
const {
|
||||
params: { identityId }
|
||||
} = await validateRequest(reqValidator.DeleteIdentityV1, req);
|
||||
|
||||
const identityMembershipOrg = await IdentityMembershipOrg
|
||||
.findOne({
|
||||
identity: new Types.ObjectId(identityId)
|
||||
})
|
||||
.populate<{
|
||||
identity: IIdentity,
|
||||
customRole: IRole
|
||||
}>("identity customRole");
|
||||
|
||||
if (!identityMembershipOrg) throw ResourceNotFoundError({
|
||||
message: `Failed to find identity with id ${identityId}`
|
||||
});
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: identityMembershipOrg.organization
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Delete,
|
||||
OrgPermissionSubjects.Identity
|
||||
);
|
||||
|
||||
const identityRolePermission = await getOrgRolePermissions(
|
||||
identityMembershipOrg?.customRole?.slug ?? identityMembershipOrg.role,
|
||||
identityMembershipOrg.organization.toString()
|
||||
);
|
||||
const hasRequiredPrivileges = isAtLeastAsPrivilegedOrg(permission, identityRolePermission);
|
||||
if (!hasRequiredPrivileges) throw ForbiddenRequestError({
|
||||
message: "Failed to delete more privileged identity"
|
||||
});
|
||||
|
||||
const identity = await Identity.findByIdAndDelete(identityMembershipOrg.identity);
|
||||
if (!identity) throw ResourceNotFoundError({
|
||||
message: `Identity with id ${identityId} not found`
|
||||
});
|
||||
|
||||
await IdentityMembershipOrg.findByIdAndDelete(identityMembershipOrg._id);
|
||||
|
||||
await IdentityMembership.deleteMany({
|
||||
identity: identityMembershipOrg.identity
|
||||
});
|
||||
|
||||
await IdentityUniversalAuth.deleteMany({
|
||||
identity: identityMembershipOrg.identity
|
||||
});
|
||||
|
||||
await IdentityUniversalAuthClientSecret.deleteMany({
|
||||
identity: identityMembershipOrg.identity
|
||||
});
|
||||
|
||||
await IdentityAccessToken.deleteMany({
|
||||
identity: identityMembershipOrg.identity
|
||||
});
|
||||
|
||||
await EEAuditLogService.createAuditLog(
|
||||
req.authData,
|
||||
{
|
||||
type: EventType.DELETE_IDENTITY,
|
||||
metadata: {
|
||||
identityId: identity._id.toString()
|
||||
}
|
||||
},
|
||||
{
|
||||
organizationId: identityMembershipOrg.organization
|
||||
}
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
identity
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
import * as identitiesController from "./identitiesController";
|
||||
import * as secretController from "./secretController";
|
||||
import * as secretSnapshotController from "./secretSnapshotController";
|
||||
import * as organizationsController from "./organizationsController";
|
||||
@ -14,7 +13,6 @@ import * as secretRotationProviderController from "./secretRotationProviderContr
|
||||
import * as secretRotationController from "./secretRotationController";
|
||||
|
||||
export {
|
||||
identitiesController,
|
||||
secretController,
|
||||
secretSnapshotController,
|
||||
organizationsController,
|
||||
|
@ -8,7 +8,7 @@ import * as reqValidator from "../../../validation/organization";
|
||||
import {
|
||||
OrgPermissionActions,
|
||||
OrgPermissionSubjects,
|
||||
getAuthDataOrgPermissions,
|
||||
getUserOrgPermissions
|
||||
} from "../../services/RoleService";
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { Organization } from "../../../models";
|
||||
@ -20,10 +20,7 @@ export const getOrganizationPlansTable = async (req: Request, res: Response) =>
|
||||
params: { organizationId }
|
||||
} = await validateRequest(reqValidator.GetOrgPlansTablev1, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Read,
|
||||
OrgPermissionSubjects.Billing
|
||||
@ -45,10 +42,7 @@ export const getOrganizationPlan = async (req: Request, res: Response) => {
|
||||
params: { organizationId }
|
||||
} = await validateRequest(reqValidator.GetOrgPlanv1, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Read,
|
||||
OrgPermissionSubjects.Billing
|
||||
@ -76,10 +70,7 @@ export const startOrganizationTrial = async (req: Request, res: Response) => {
|
||||
body: { success_url }
|
||||
} = await validateRequest(reqValidator.StartOrgTrailv1, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Create,
|
||||
OrgPermissionSubjects.Billing
|
||||
@ -125,10 +116,7 @@ export const getOrganizationPlanBillingInfo = async (req: Request, res: Response
|
||||
params: { organizationId }
|
||||
} = await validateRequest(reqValidator.GetOrgPlanBillingInfov1, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Read,
|
||||
OrgPermissionSubjects.Billing
|
||||
@ -161,10 +149,7 @@ export const getOrganizationPlanTable = async (req: Request, res: Response) => {
|
||||
params: { organizationId }
|
||||
} = await validateRequest(reqValidator.GetOrgPlanTablev1, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Read,
|
||||
OrgPermissionSubjects.Billing
|
||||
@ -191,10 +176,7 @@ export const getOrganizationBillingDetails = async (req: Request, res: Response)
|
||||
params: { organizationId }
|
||||
} = await validateRequest(reqValidator.GetOrgBillingDetailsv1, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Read,
|
||||
OrgPermissionSubjects.Billing
|
||||
@ -222,10 +204,7 @@ export const updateOrganizationBillingDetails = async (req: Request, res: Respon
|
||||
body: { name, email }
|
||||
} = await validateRequest(reqValidator.UpdateOrgBillingDetailsv1, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Edit,
|
||||
OrgPermissionSubjects.Billing
|
||||
@ -259,10 +238,7 @@ export const getOrganizationPmtMethods = async (req: Request, res: Response) =>
|
||||
params: { organizationId }
|
||||
} = await validateRequest(reqValidator.GetOrgPmtMethodsv1, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Read,
|
||||
OrgPermissionSubjects.Billing
|
||||
@ -295,10 +271,7 @@ export const addOrganizationPmtMethod = async (req: Request, res: Response) => {
|
||||
body: { success_url, cancel_url }
|
||||
} = await validateRequest(reqValidator.CreateOrgPmtMethodv1, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Create,
|
||||
OrgPermissionSubjects.Billing
|
||||
@ -339,10 +312,7 @@ export const deleteOrganizationPmtMethod = async (req: Request, res: Response) =
|
||||
params: { organizationId, pmtMethodId }
|
||||
} = await validateRequest(reqValidator.DelOrgPmtMethodv1, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Delete,
|
||||
OrgPermissionSubjects.Billing
|
||||
@ -372,10 +342,7 @@ export const getOrganizationTaxIds = async (req: Request, res: Response) => {
|
||||
params: { organizationId }
|
||||
} = await validateRequest(reqValidator.GetOrgTaxIdsv1, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Read,
|
||||
OrgPermissionSubjects.Billing
|
||||
@ -408,10 +375,7 @@ export const addOrganizationTaxId = async (req: Request, res: Response) => {
|
||||
body: { type, value }
|
||||
} = await validateRequest(reqValidator.CreateOrgTaxId, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Create,
|
||||
OrgPermissionSubjects.Billing
|
||||
@ -448,10 +412,7 @@ export const deleteOrganizationTaxId = async (req: Request, res: Response) => {
|
||||
params: { organizationId, taxId }
|
||||
} = await validateRequest(reqValidator.DelOrgTaxIdv1, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Delete,
|
||||
OrgPermissionSubjects.Billing
|
||||
@ -484,10 +445,7 @@ export const getOrganizationInvoices = async (req: Request, res: Response) => {
|
||||
params: { organizationId }
|
||||
} = await validateRequest(reqValidator.GetOrgInvoicesv1, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Read,
|
||||
OrgPermissionSubjects.Billing
|
||||
@ -522,10 +480,7 @@ export const getOrganizationLicenses = async (req: Request, res: Response) => {
|
||||
params: { organizationId }
|
||||
} = await validateRequest(reqValidator.GetOrgLicencesv1, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Read,
|
||||
OrgPermissionSubjects.Billing
|
||||
|
@ -15,17 +15,14 @@ import {
|
||||
adminProjectPermissions,
|
||||
getAuthDataProjectPermissions,
|
||||
memberProjectPermissions,
|
||||
noAccessProjectPermissions,
|
||||
viewerProjectPermission
|
||||
} from "../../services/ProjectRoleService";
|
||||
import {
|
||||
OrgPermissionActions,
|
||||
OrgPermissionSubjects,
|
||||
adminPermissions,
|
||||
getAuthDataOrgPermissions,
|
||||
getUserOrgPermissions,
|
||||
memberPermissions,
|
||||
noAccessPermissions
|
||||
memberPermissions
|
||||
} from "../../services/RoleService";
|
||||
import { BadRequestError } from "../../../utils/errors";
|
||||
import { Role } from "../../models";
|
||||
@ -39,11 +36,7 @@ export const createRole = async (req: Request, res: Response) => {
|
||||
|
||||
const isOrgRole = !workspaceId; // if workspaceid is provided then its a workspace rule
|
||||
if (isOrgRole) {
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(orgId)
|
||||
});
|
||||
|
||||
const { permission } = await getUserOrgPermissions(req.user.id, orgId);
|
||||
if (permission.cannot(OrgPermissionActions.Create, OrgPermissionSubjects.Role)) {
|
||||
throw BadRequestError({ message: "user doesn't have the permission." });
|
||||
}
|
||||
@ -87,12 +80,9 @@ export const updateRole = async (req: Request, res: Response) => {
|
||||
body: { name, description, slug, permissions, workspaceId, orgId }
|
||||
} = await validateRequest(UpdateRoleSchema, req);
|
||||
const isOrgRole = !workspaceId; // if workspaceid is provided then its a workspace rule
|
||||
|
||||
|
||||
if (isOrgRole) {
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(orgId)
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user.id, orgId);
|
||||
if (permission.cannot(OrgPermissionActions.Edit, OrgPermissionSubjects.Role)) {
|
||||
throw BadRequestError({ message: "User doesn't have the org permission." });
|
||||
}
|
||||
@ -148,10 +138,7 @@ export const deleteRole = async (req: Request, res: Response) => {
|
||||
|
||||
const isOrgRole = !role.workspace;
|
||||
if (isOrgRole) {
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: role.organization
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user.id, role.organization.toString());
|
||||
if (permission.cannot(OrgPermissionActions.Delete, OrgPermissionSubjects.Role)) {
|
||||
throw BadRequestError({ message: "User doesn't have the org permission." });
|
||||
}
|
||||
@ -183,10 +170,7 @@ export const getRoles = async (req: Request, res: Response) => {
|
||||
|
||||
const isOrgRole = !workspaceId;
|
||||
if (isOrgRole) {
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(orgId)
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user.id, orgId);
|
||||
if (permission.cannot(OrgPermissionActions.Read, OrgPermissionSubjects.Role)) {
|
||||
throw BadRequestError({ message: "User doesn't have the org permission." });
|
||||
}
|
||||
@ -211,13 +195,6 @@ export const getRoles = async (req: Request, res: Response) => {
|
||||
description: "Complete administration access over the organization",
|
||||
permissions: isOrgRole ? adminPermissions.rules : adminProjectPermissions.rules
|
||||
},
|
||||
{
|
||||
_id: "no-access",
|
||||
name: "No Access",
|
||||
slug: "no-access",
|
||||
description: "No access to any resources in the organization",
|
||||
permissions: isOrgRole ? noAccessPermissions.rules : noAccessProjectPermissions.rules
|
||||
},
|
||||
{
|
||||
_id: "member",
|
||||
name: isOrgRole ? "Member" : "Developer",
|
||||
@ -252,7 +229,7 @@ export const getUserPermissions = async (req: Request, res: Response) => {
|
||||
const {
|
||||
params: { orgId }
|
||||
} = await validateRequest(GetUserPermission, req);
|
||||
|
||||
|
||||
const { permission, membership } = await getUserOrgPermissions(req.user._id, orgId);
|
||||
|
||||
res.status(200).json({
|
||||
|
@ -17,12 +17,12 @@ export const getSecretApprovalRequestCount = async (req: Request, res: Response)
|
||||
} = await validateRequest(reqValidator.getSecretApprovalRequestCount, req);
|
||||
|
||||
if (!(req.authData.authPayload instanceof User)) return;
|
||||
|
||||
|
||||
const membership = await Membership.findOne({
|
||||
user: req.authData.authPayload._id,
|
||||
workspace: new Types.ObjectId(workspaceId)
|
||||
});
|
||||
|
||||
|
||||
if (!membership) throw UnauthorizedRequestError();
|
||||
|
||||
const approvalRequestCount = await SecretApprovalRequest.aggregate([
|
||||
@ -73,12 +73,12 @@ export const getSecretApprovalRequests = async (req: Request, res: Response) =>
|
||||
} = await validateRequest(reqValidator.getSecretApprovalRequests, req);
|
||||
|
||||
if (!(req.authData.authPayload instanceof User)) return;
|
||||
|
||||
|
||||
const membership = await Membership.findOne({
|
||||
user: req.authData.authPayload._id,
|
||||
workspace: new Types.ObjectId(workspaceId)
|
||||
});
|
||||
|
||||
|
||||
if (!membership) throw UnauthorizedRequestError();
|
||||
|
||||
const query = {
|
||||
@ -168,13 +168,13 @@ export const getSecretApprovalRequestDetails = async (req: Request, res: Respons
|
||||
user: req.authData.authPayload._id,
|
||||
workspace: secretApprovalRequest.workspace
|
||||
});
|
||||
|
||||
|
||||
if (!membership) throw UnauthorizedRequestError();
|
||||
|
||||
// allow to fetch only if its admin or is the committer or approver
|
||||
if (
|
||||
membership.role !== "admin" &&
|
||||
!secretApprovalRequest.committer.equals(membership.id) &&
|
||||
secretApprovalRequest.committer !== membership.id &&
|
||||
!secretApprovalRequest.policy.approvers.find(
|
||||
(approverId) => approverId.toString() === membership._id.toString()
|
||||
)
|
||||
@ -215,7 +215,7 @@ export const updateSecretApprovalReviewStatus = async (req: Request, res: Respon
|
||||
user: req.authData.authPayload._id,
|
||||
workspace: secretApprovalRequest.workspace
|
||||
});
|
||||
|
||||
|
||||
if (!membership) throw UnauthorizedRequestError();
|
||||
|
||||
if (
|
||||
@ -257,7 +257,7 @@ export const mergeSecretApprovalRequest = async (req: Request, res: Response) =>
|
||||
user: req.authData.authPayload._id,
|
||||
workspace: secretApprovalRequest.workspace
|
||||
});
|
||||
|
||||
|
||||
if (!membership) throw UnauthorizedRequestError();
|
||||
|
||||
if (
|
||||
@ -307,7 +307,7 @@ export const updateSecretApprovalRequestStatus = async (req: Request, res: Respo
|
||||
user: req.authData.authPayload._id,
|
||||
workspace: secretApprovalRequest.workspace
|
||||
});
|
||||
|
||||
|
||||
if (!membership) throw UnauthorizedRequestError();
|
||||
|
||||
if (
|
||||
|
@ -13,7 +13,7 @@ import { validateRequest } from "../../../helpers/validation";
|
||||
import {
|
||||
OrgPermissionActions,
|
||||
OrgPermissionSubjects,
|
||||
getAuthDataOrgPermissions
|
||||
getUserOrgPermissions
|
||||
} from "../../services/RoleService";
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
@ -47,10 +47,7 @@ export const getSSOConfig = async (req: Request, res: Response) => {
|
||||
query: { organizationId }
|
||||
} = await validateRequest(reqValidator.GetSsoConfigv1, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Read,
|
||||
OrgPermissionSubjects.Sso
|
||||
@ -74,10 +71,7 @@ export const updateSSOConfig = async (req: Request, res: Response) => {
|
||||
body: { organizationId, authProvider, isActive, entryPoint, issuer, cert }
|
||||
} = await validateRequest(reqValidator.UpdateSsoConfigv1, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Edit,
|
||||
OrgPermissionSubjects.Sso
|
||||
@ -212,10 +206,7 @@ export const createSSOConfig = async (req: Request, res: Response) => {
|
||||
body: { organizationId, authProvider, isActive, entryPoint, issuer, cert }
|
||||
} = await validateRequest(reqValidator.CreateSsoConfigv1, req);
|
||||
|
||||
const { permission } = await getAuthDataOrgPermissions({
|
||||
authData: req.authData,
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
const { permission } = await getUserOrgPermissions(req.user._id, organizationId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Create,
|
||||
OrgPermissionSubjects.Sso
|
||||
|
@ -2,11 +2,10 @@ import { Request, Response } from "express";
|
||||
import { PipelineStage, Types } from "mongoose";
|
||||
import {
|
||||
Folder,
|
||||
Identity,
|
||||
IdentityMembership,
|
||||
Membership,
|
||||
Secret,
|
||||
ServiceTokenData,
|
||||
ServiceTokenDataV3,
|
||||
TFolderSchema,
|
||||
User,
|
||||
Workspace
|
||||
@ -18,10 +17,10 @@ import {
|
||||
FolderVersion,
|
||||
IPType,
|
||||
ISecretVersion,
|
||||
IdentityActor,
|
||||
SecretSnapshot,
|
||||
SecretVersion,
|
||||
ServiceActor,
|
||||
ServiceActorV3,
|
||||
TFolderRootVersionSchema,
|
||||
TrustedIP,
|
||||
UserActor
|
||||
@ -62,30 +61,15 @@ export const getWorkspaceSecretSnapshots = async (req: Request, res: Response) =
|
||||
#swagger.description = 'Return project secret snapshots ids'
|
||||
|
||||
#swagger.security = [{
|
||||
"apiKeyAuth": [],
|
||||
"bearerAuth": []
|
||||
"apiKeyAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['workspaceId'] = {
|
||||
"description": "ID of project where to get secret snapshots for",
|
||||
"description": "ID of project",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
#swagger.parameters['environment'] = {
|
||||
"description": "Slug of environment where to get secret snapshots for",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"in": "query"
|
||||
}
|
||||
|
||||
#swagger.parameters['directory'] = {
|
||||
"description": "Path where to get secret snapshots for like / or /foo/bar. Default is /",
|
||||
"required": false,
|
||||
"type": "string",
|
||||
"in": "query"
|
||||
}
|
||||
|
||||
#swagger.parameters['offset'] = {
|
||||
"description": "Number of secret snapshots to skip",
|
||||
"required": false,
|
||||
@ -210,12 +194,11 @@ export const rollbackWorkspaceSecretSnapshot = async (req: Request, res: Respons
|
||||
#swagger.description = 'Roll back project secrets to those captured in a secret snapshot version.'
|
||||
|
||||
#swagger.security = [{
|
||||
"apiKeyAuth": [],
|
||||
"bearerAuth": []
|
||||
"apiKeyAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['workspaceId'] = {
|
||||
"description": "ID of project where to roll back",
|
||||
"description": "ID of project",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
@ -227,14 +210,6 @@ export const rollbackWorkspaceSecretSnapshot = async (req: Request, res: Respons
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"environment": {
|
||||
"type": "string",
|
||||
"description": "Slug of environment where to roll back"
|
||||
},
|
||||
"directory": {
|
||||
"type": "string",
|
||||
"description": "Path where to roll back for like / or /foo/bar. Default is /"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer",
|
||||
"description": "Version of secret snapshot to roll back to",
|
||||
@ -694,21 +669,6 @@ export const getWorkspaceAuditLogs = async (req: Request, res: Response) => {
|
||||
ProjectPermissionSub.AuditLogs
|
||||
);
|
||||
|
||||
let actorMetadataQuery = "";
|
||||
if (actor) {
|
||||
switch (actor?.split("-", 2)[0]) {
|
||||
case ActorType.USER:
|
||||
actorMetadataQuery = "actor.metadata.userId";
|
||||
break;
|
||||
case ActorType.SERVICE:
|
||||
actorMetadataQuery = "actor.metadata.serviceId";
|
||||
break;
|
||||
case ActorType.IDENTITY:
|
||||
actorMetadataQuery = "actor.metadata.identityId";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const query = {
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
...(eventType
|
||||
@ -724,9 +684,13 @@ export const getWorkspaceAuditLogs = async (req: Request, res: Response) => {
|
||||
...(actor
|
||||
? {
|
||||
"actor.type": actor.substring(0, actor.lastIndexOf("-")),
|
||||
...({
|
||||
[actorMetadataQuery]: actor.substring(actor.lastIndexOf("-") + 1)
|
||||
})
|
||||
...(actor.split("-", 2)[0] === ActorType.USER
|
||||
? {
|
||||
"actor.metadata.userId": actor.substring(actor.lastIndexOf("-") + 1)
|
||||
}
|
||||
: {
|
||||
"actor.metadata.serviceId": actor.substring(actor.lastIndexOf("-") + 1)
|
||||
})
|
||||
}
|
||||
: {}),
|
||||
...(startDate || endDate
|
||||
@ -738,9 +702,7 @@ export const getWorkspaceAuditLogs = async (req: Request, res: Response) => {
|
||||
}
|
||||
: {})
|
||||
};
|
||||
|
||||
const auditLogs = await AuditLog.find(query).sort({ createdAt: -1 }).skip(offset).limit(limit);
|
||||
|
||||
return res.status(200).send({
|
||||
auditLogs
|
||||
});
|
||||
@ -769,7 +731,6 @@ export const getWorkspaceAuditLogActorFilterOpts = async (req: Request, res: Res
|
||||
const userIds = await Membership.distinct("user", {
|
||||
workspace: new Types.ObjectId(workspaceId)
|
||||
});
|
||||
|
||||
const userActors: UserActor[] = (
|
||||
await User.find({
|
||||
_id: {
|
||||
@ -796,25 +757,19 @@ export const getWorkspaceAuditLogActorFilterOpts = async (req: Request, res: Res
|
||||
}
|
||||
}));
|
||||
|
||||
const identityIds = await IdentityMembership.distinct("identity", {
|
||||
workspace: new Types.ObjectId(workspaceId)
|
||||
});
|
||||
|
||||
const identityActors: IdentityActor[] = (
|
||||
await Identity.find({
|
||||
_id: {
|
||||
$in: identityIds
|
||||
}
|
||||
const serviceV3Actors: ServiceActorV3[] = (
|
||||
await ServiceTokenDataV3.find({
|
||||
workspace: new Types.ObjectId(workspaceId)
|
||||
})
|
||||
).map((identity) => ({
|
||||
type: ActorType.IDENTITY,
|
||||
).map((serviceTokenData) => ({
|
||||
type: ActorType.SERVICE_V3,
|
||||
metadata: {
|
||||
identityId: identity._id.toString(),
|
||||
name: identity.name
|
||||
serviceId: serviceTokenData._id.toString(),
|
||||
name: serviceTokenData.name
|
||||
}
|
||||
}));
|
||||
|
||||
const actors = [...userActors, ...serviceActors, ...identityActors];
|
||||
const actors = [...userActors, ...serviceActors, ...serviceV3Actors];
|
||||
|
||||
return res.status(200).send({
|
||||
actors
|
||||
|
@ -1,5 +1,7 @@
|
||||
import * as serviceTokenDataController from "./serviceTokenDataController";
|
||||
import * as apiKeyDataController from "./apiKeyDataController";
|
||||
|
||||
export {
|
||||
serviceTokenDataController,
|
||||
apiKeyDataController
|
||||
}
|
469
backend/src/ee/controllers/v3/serviceTokenDataController.ts
Normal file
469
backend/src/ee/controllers/v3/serviceTokenDataController.ts
Normal file
@ -0,0 +1,469 @@
|
||||
import jwt from "jsonwebtoken";
|
||||
import { Request, Response } from "express";
|
||||
import { Types } from "mongoose";
|
||||
import {
|
||||
IServiceTokenDataV3,
|
||||
IUser,
|
||||
ServiceTokenDataV3,
|
||||
ServiceTokenDataV3Key,
|
||||
Workspace
|
||||
} from "../../../models";
|
||||
import { IServiceTokenV3TrustedIp } from "../../../models/serviceTokenDataV3";
|
||||
import {
|
||||
ActorType,
|
||||
EventType,
|
||||
Role
|
||||
} from "../../models";
|
||||
import { validateRequest } from "../../../helpers/validation";
|
||||
import * as reqValidator from "../../../validation/serviceTokenDataV3";
|
||||
import { createToken } from "../../../helpers/auth";
|
||||
import {
|
||||
ProjectPermissionActions,
|
||||
ProjectPermissionSub,
|
||||
getAuthDataProjectPermissions
|
||||
} from "../../services/ProjectRoleService";
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { BadRequestError, ResourceNotFoundError, UnauthorizedRequestError } from "../../../utils/errors";
|
||||
import { extractIPDetails, isValidIpOrCidr } from "../../../utils/ip";
|
||||
import { EEAuditLogService, EELicenseService } from "../../services";
|
||||
import { getAuthSecret } from "../../../config";
|
||||
import { ADMIN, AuthTokenType, CUSTOM, MEMBER, VIEWER } from "../../../variables";
|
||||
|
||||
/**
|
||||
* Return project key for service token V3
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const getServiceTokenDataKey = async (req: Request, res: Response) => {
|
||||
const key = await ServiceTokenDataV3Key.findOne({
|
||||
serviceTokenData: (req.authData.authPayload as IServiceTokenDataV3)._id
|
||||
}).populate<{ sender: IUser }>("sender", "publicKey");
|
||||
|
||||
if (!key) throw ResourceNotFoundError({
|
||||
message: "Failed to find project key for service token"
|
||||
});
|
||||
|
||||
const { _id, workspace, encryptedKey, nonce, sender: { publicKey } } = key;
|
||||
|
||||
return res.status(200).send({
|
||||
key: {
|
||||
_id,
|
||||
workspace,
|
||||
encryptedKey,
|
||||
publicKey,
|
||||
nonce
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return access and refresh token as per refresh operation
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const refreshToken = async (req: Request, res: Response) => {
|
||||
const {
|
||||
body: {
|
||||
refresh_token
|
||||
}
|
||||
} = await validateRequest(reqValidator.RefreshTokenV3, req);
|
||||
|
||||
const decodedToken = <jwt.ServiceRefreshTokenJwtPayload>(
|
||||
jwt.verify(refresh_token, await getAuthSecret())
|
||||
);
|
||||
|
||||
if (decodedToken.authTokenType !== AuthTokenType.SERVICE_REFRESH_TOKEN) throw UnauthorizedRequestError();
|
||||
|
||||
let serviceTokenData = await ServiceTokenDataV3.findOne({
|
||||
_id: new Types.ObjectId(decodedToken.serviceTokenDataId),
|
||||
isActive: true
|
||||
});
|
||||
|
||||
if (!serviceTokenData) throw UnauthorizedRequestError();
|
||||
|
||||
if (decodedToken.tokenVersion !== serviceTokenData.tokenVersion) {
|
||||
// raise alarm
|
||||
throw UnauthorizedRequestError();
|
||||
}
|
||||
|
||||
const response: {
|
||||
refresh_token?: string;
|
||||
access_token: string;
|
||||
expires_in: number;
|
||||
token_type: string;
|
||||
} = {
|
||||
refresh_token,
|
||||
access_token: "",
|
||||
expires_in: 0,
|
||||
token_type: "Bearer"
|
||||
};
|
||||
|
||||
if (serviceTokenData.isRefreshTokenRotationEnabled) {
|
||||
serviceTokenData = await ServiceTokenDataV3.findByIdAndUpdate(
|
||||
serviceTokenData._id,
|
||||
{
|
||||
$inc: {
|
||||
tokenVersion: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
new: true
|
||||
}
|
||||
);
|
||||
|
||||
if (!serviceTokenData) throw BadRequestError();
|
||||
|
||||
response.refresh_token = createToken({
|
||||
payload: {
|
||||
serviceTokenDataId: serviceTokenData._id.toString(),
|
||||
authTokenType: AuthTokenType.SERVICE_REFRESH_TOKEN,
|
||||
tokenVersion: serviceTokenData.tokenVersion
|
||||
},
|
||||
secret: await getAuthSecret()
|
||||
});
|
||||
}
|
||||
|
||||
response.access_token = createToken({
|
||||
payload: {
|
||||
serviceTokenDataId: serviceTokenData._id.toString(),
|
||||
authTokenType: AuthTokenType.SERVICE_ACCESS_TOKEN,
|
||||
tokenVersion: serviceTokenData.tokenVersion
|
||||
},
|
||||
expiresIn: serviceTokenData.accessTokenTTL,
|
||||
secret: await getAuthSecret()
|
||||
});
|
||||
|
||||
response.expires_in = serviceTokenData.accessTokenTTL;
|
||||
|
||||
await ServiceTokenDataV3.findByIdAndUpdate(
|
||||
serviceTokenData._id,
|
||||
{
|
||||
refreshTokenLastUsed: new Date(),
|
||||
$inc: { refreshTokenUsageCount: 1 }
|
||||
},
|
||||
{
|
||||
new: true
|
||||
}
|
||||
);
|
||||
|
||||
return res.status(200).send(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create service token data V3
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const createServiceTokenData = async (req: Request, res: Response) => {
|
||||
const {
|
||||
body: {
|
||||
name,
|
||||
workspaceId,
|
||||
publicKey,
|
||||
role,
|
||||
trustedIps,
|
||||
expiresIn,
|
||||
accessTokenTTL,
|
||||
isRefreshTokenRotationEnabled,
|
||||
encryptedKey, // for ServiceTokenDataV3Key
|
||||
nonce, // for ServiceTokenDataV3Key
|
||||
}
|
||||
} = await validateRequest(reqValidator.CreateServiceTokenV3, req);
|
||||
const { permission } = await getAuthDataProjectPermissions({
|
||||
authData: req.authData,
|
||||
workspaceId: new Types.ObjectId(workspaceId)
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
ProjectPermissionSub.ServiceTokens
|
||||
);
|
||||
|
||||
const workspace = await Workspace.findById(workspaceId);
|
||||
if (!workspace) throw BadRequestError({ message: "Workspace not found" });
|
||||
|
||||
const isCustomRole = ![ADMIN, MEMBER, VIEWER].includes(role);
|
||||
|
||||
let customRole;
|
||||
if (isCustomRole) {
|
||||
customRole = await Role.findOne({
|
||||
slug: role,
|
||||
isOrgRole: false,
|
||||
workspace: workspace._id
|
||||
});
|
||||
|
||||
if (!customRole) throw BadRequestError({ message: "Role not found" });
|
||||
}
|
||||
|
||||
const plan = await EELicenseService.getPlan(workspace.organization);
|
||||
|
||||
// validate trusted ips
|
||||
const reformattedTrustedIps = trustedIps.map((trustedIp) => {
|
||||
if (!plan.ipAllowlisting && trustedIp.ipAddress !== "0.0.0.0/0") return res.status(400).send({
|
||||
message: "Failed to add IP access range to service token due to plan restriction. Upgrade plan to add IP access range."
|
||||
});
|
||||
|
||||
const isValidIPOrCidr = isValidIpOrCidr(trustedIp.ipAddress);
|
||||
|
||||
if (!isValidIPOrCidr) return res.status(400).send({
|
||||
message: "The IP is not a valid IPv4, IPv6, or CIDR block"
|
||||
});
|
||||
|
||||
return extractIPDetails(trustedIp.ipAddress);
|
||||
});
|
||||
|
||||
let expiresAt;
|
||||
if (expiresIn) {
|
||||
expiresAt = new Date();
|
||||
expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
|
||||
}
|
||||
|
||||
let user;
|
||||
if (req.authData.actor.type === ActorType.USER) {
|
||||
user = req.authData.authPayload._id;
|
||||
}
|
||||
|
||||
const isActive = true;
|
||||
const serviceTokenData = await new ServiceTokenDataV3({
|
||||
name,
|
||||
user,
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
publicKey,
|
||||
refreshTokenUsageCount: 0,
|
||||
accessTokenUsageCount: 0,
|
||||
tokenVersion: 1,
|
||||
trustedIps: reformattedTrustedIps,
|
||||
role: isCustomRole ? CUSTOM : role,
|
||||
customRole,
|
||||
isActive,
|
||||
expiresAt,
|
||||
accessTokenTTL,
|
||||
isRefreshTokenRotationEnabled
|
||||
}).save();
|
||||
|
||||
await new ServiceTokenDataV3Key({
|
||||
encryptedKey,
|
||||
nonce,
|
||||
sender: req.user._id,
|
||||
serviceTokenData: serviceTokenData._id,
|
||||
workspace: new Types.ObjectId(workspaceId)
|
||||
}).save();
|
||||
|
||||
const refreshToken = createToken({
|
||||
payload: {
|
||||
serviceTokenDataId: serviceTokenData._id.toString(),
|
||||
authTokenType: AuthTokenType.SERVICE_REFRESH_TOKEN,
|
||||
tokenVersion: serviceTokenData.tokenVersion
|
||||
},
|
||||
secret: await getAuthSecret()
|
||||
});
|
||||
|
||||
await EEAuditLogService.createAuditLog(
|
||||
req.authData,
|
||||
{
|
||||
type: EventType.CREATE_SERVICE_TOKEN_V3, // TODO: update
|
||||
metadata: {
|
||||
name,
|
||||
isActive,
|
||||
role,
|
||||
trustedIps: reformattedTrustedIps as Array<IServiceTokenV3TrustedIp>,
|
||||
expiresAt
|
||||
}
|
||||
},
|
||||
{
|
||||
workspaceId: new Types.ObjectId(workspaceId)
|
||||
}
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
serviceTokenData,
|
||||
refreshToken
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update service token V3 data with id [serviceTokenDataId]
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const updateServiceTokenData = async (req: Request, res: Response) => {
|
||||
const {
|
||||
params: { serviceTokenDataId },
|
||||
body: {
|
||||
name,
|
||||
isActive,
|
||||
role,
|
||||
trustedIps,
|
||||
expiresIn,
|
||||
accessTokenTTL,
|
||||
isRefreshTokenRotationEnabled
|
||||
}
|
||||
} = await validateRequest(reqValidator.UpdateServiceTokenV3, req);
|
||||
|
||||
let serviceTokenData = await ServiceTokenDataV3.findById(serviceTokenDataId);
|
||||
if (!serviceTokenData) throw ResourceNotFoundError({
|
||||
message: "Service token not found"
|
||||
});
|
||||
|
||||
const { permission } = await getAuthDataProjectPermissions({
|
||||
authData: req.authData,
|
||||
workspaceId: serviceTokenData.workspace
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Edit,
|
||||
ProjectPermissionSub.ServiceTokens
|
||||
);
|
||||
|
||||
const workspace = await Workspace.findById(serviceTokenData.workspace);
|
||||
if (!workspace) throw BadRequestError({ message: "Workspace not found" });
|
||||
|
||||
let customRole;
|
||||
if (role) {
|
||||
const isCustomRole = ![ADMIN, MEMBER, VIEWER].includes(role);
|
||||
if (isCustomRole) {
|
||||
customRole = await Role.findOne({
|
||||
slug: role,
|
||||
isOrgRole: false,
|
||||
workspace: workspace._id
|
||||
});
|
||||
|
||||
if (!customRole) throw BadRequestError({ message: "Role not found" });
|
||||
}
|
||||
}
|
||||
|
||||
const plan = await EELicenseService.getPlan(workspace.organization);
|
||||
|
||||
// validate trusted ips
|
||||
let reformattedTrustedIps;
|
||||
if (trustedIps) {
|
||||
reformattedTrustedIps = trustedIps.map((trustedIp) => {
|
||||
if (!plan.ipAllowlisting && trustedIp.ipAddress !== "0.0.0.0/0") return res.status(400).send({
|
||||
message: "Failed to update IP access range to service token due to plan restriction. Upgrade plan to update IP access range."
|
||||
});
|
||||
|
||||
const isValidIPOrCidr = isValidIpOrCidr(trustedIp.ipAddress);
|
||||
|
||||
if (!isValidIPOrCidr) return res.status(400).send({
|
||||
message: "The IP is not a valid IPv4, IPv6, or CIDR block"
|
||||
});
|
||||
|
||||
return extractIPDetails(trustedIp.ipAddress);
|
||||
});
|
||||
}
|
||||
|
||||
let expiresAt;
|
||||
if (expiresIn) {
|
||||
expiresAt = new Date();
|
||||
expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
|
||||
}
|
||||
|
||||
serviceTokenData = await ServiceTokenDataV3.findByIdAndUpdate(
|
||||
serviceTokenDataId,
|
||||
{
|
||||
name,
|
||||
isActive,
|
||||
role: customRole ? CUSTOM : role,
|
||||
...(customRole ? {
|
||||
customRole
|
||||
} : {}),
|
||||
...(role && !customRole ? { // non-custom role
|
||||
$unset: {
|
||||
customRole: 1
|
||||
}
|
||||
} : {}),
|
||||
trustedIps: reformattedTrustedIps,
|
||||
expiresAt,
|
||||
accessTokenTTL,
|
||||
isRefreshTokenRotationEnabled
|
||||
},
|
||||
{
|
||||
new: true
|
||||
}
|
||||
);
|
||||
|
||||
if (!serviceTokenData) throw BadRequestError({
|
||||
message: "Failed to update service token"
|
||||
});
|
||||
|
||||
await EEAuditLogService.createAuditLog(
|
||||
req.authData,
|
||||
{
|
||||
type: EventType.UPDATE_SERVICE_TOKEN_V3,
|
||||
metadata: {
|
||||
name: serviceTokenData.name,
|
||||
isActive,
|
||||
role,
|
||||
trustedIps: reformattedTrustedIps as Array<IServiceTokenV3TrustedIp>,
|
||||
expiresAt
|
||||
}
|
||||
},
|
||||
{
|
||||
workspaceId: serviceTokenData.workspace
|
||||
}
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
serviceTokenData
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete service token data with id [serviceTokenDataId]
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const deleteServiceTokenData = async (req: Request, res: Response) => {
|
||||
const {
|
||||
params: { serviceTokenDataId }
|
||||
} = await validateRequest(reqValidator.DeleteServiceTokenV3, req);
|
||||
|
||||
let serviceTokenData = await ServiceTokenDataV3.findById(serviceTokenDataId);
|
||||
if (!serviceTokenData) throw ResourceNotFoundError({
|
||||
message: "Service token not found"
|
||||
});
|
||||
|
||||
const { permission } = await getAuthDataProjectPermissions({
|
||||
authData: req.authData,
|
||||
workspaceId: serviceTokenData.workspace
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Delete,
|
||||
ProjectPermissionSub.ServiceTokens
|
||||
);
|
||||
|
||||
serviceTokenData = await ServiceTokenDataV3.findByIdAndDelete(serviceTokenDataId);
|
||||
|
||||
if (!serviceTokenData) throw BadRequestError({
|
||||
message: "Failed to delete service token"
|
||||
});
|
||||
|
||||
await ServiceTokenDataV3Key.findOneAndDelete({
|
||||
serviceTokenData: serviceTokenData._id
|
||||
});
|
||||
|
||||
await EEAuditLogService.createAuditLog(
|
||||
req.authData,
|
||||
{
|
||||
type: EventType.DELETE_SERVICE_TOKEN_V3,
|
||||
metadata: {
|
||||
name: serviceTokenData.name,
|
||||
isActive: serviceTokenData.isActive,
|
||||
role: serviceTokenData.role,
|
||||
trustedIps: serviceTokenData.trustedIps as Array<IServiceTokenV3TrustedIp>,
|
||||
expiresAt: serviceTokenData.expiresAt
|
||||
}
|
||||
},
|
||||
{
|
||||
workspaceId: serviceTokenData.workspace
|
||||
}
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
serviceTokenData
|
||||
});
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
import { Types } from "mongoose";
|
||||
import {
|
||||
LDAPConfig,
|
||||
SSOConfig
|
||||
} from "../models";
|
||||
import {
|
||||
@ -62,53 +61,4 @@ export const getSSOConfigHelper = async ({
|
||||
issuer,
|
||||
cert
|
||||
});
|
||||
}
|
||||
|
||||
export const getLdapConfigHelper = async ({
|
||||
organizationId
|
||||
}: {
|
||||
organizationId: Types.ObjectId;
|
||||
}) => {
|
||||
|
||||
const ldapConfig = await LDAPConfig.findOne({
|
||||
organization: organizationId
|
||||
});
|
||||
|
||||
if (!ldapConfig) throw new Error("Failed to find organization LDAP data");
|
||||
|
||||
const key = await BotOrgService.getSymmetricKey(
|
||||
ldapConfig.organization
|
||||
);
|
||||
|
||||
const bindDN = client.decryptSymmetric(
|
||||
ldapConfig.encryptedBindDN,
|
||||
key,
|
||||
ldapConfig.bindDNIV,
|
||||
ldapConfig.bindDNTag
|
||||
);
|
||||
|
||||
const bindPass = client.decryptSymmetric(
|
||||
ldapConfig.encryptedBindPass,
|
||||
key,
|
||||
ldapConfig.bindPassIV,
|
||||
ldapConfig.bindPassTag
|
||||
);
|
||||
|
||||
const caCert = client.decryptSymmetric(
|
||||
ldapConfig.encryptedCACert,
|
||||
key,
|
||||
ldapConfig.caCertIV,
|
||||
ldapConfig.caCertTag
|
||||
);
|
||||
|
||||
return ({
|
||||
_id: ldapConfig._id,
|
||||
organization: ldapConfig.organization,
|
||||
isActive: ldapConfig.isActive,
|
||||
url: ldapConfig.url,
|
||||
bindDN,
|
||||
bindPass,
|
||||
searchBase: ldapConfig.searchBase,
|
||||
caCert
|
||||
});
|
||||
}
|
@ -10,7 +10,7 @@ export interface IAuditLog {
|
||||
event: Event;
|
||||
userAgent: string;
|
||||
userAgentType: UserAgentType;
|
||||
expiresAt?: Date;
|
||||
expiresAt: Date;
|
||||
}
|
||||
|
||||
const auditLogSchema = new Schema<IAuditLog>(
|
||||
|
@ -1,17 +1,15 @@
|
||||
export enum ActorType { // would extend to AWS, Azure, ...
|
||||
USER = "user", // userIdentity
|
||||
SERVICE = "service",
|
||||
IDENTITY = "identity"
|
||||
export enum ActorType {
|
||||
USER = "user",
|
||||
SERVICE = "service",
|
||||
SERVICE_V3 = "service-v3",
|
||||
// Machine = "machine"
|
||||
}
|
||||
|
||||
export enum UserAgentType {
|
||||
WEB = "web",
|
||||
CLI = "cli",
|
||||
K8_OPERATOR = "k8-operator",
|
||||
TERRAFORM = "terraform",
|
||||
OTHER = "other",
|
||||
PYTHON_SDK = "InfisicalPythonSDK",
|
||||
NODE_SDK = "InfisicalNodeSDK"
|
||||
OTHER = "other"
|
||||
}
|
||||
|
||||
export enum EventType {
|
||||
@ -34,16 +32,9 @@ export enum EventType {
|
||||
DELETE_TRUSTED_IP = "delete-trusted-ip",
|
||||
CREATE_SERVICE_TOKEN = "create-service-token", // v2
|
||||
DELETE_SERVICE_TOKEN = "delete-service-token", // v2
|
||||
CREATE_IDENTITY = "create-identity",
|
||||
UPDATE_IDENTITY = "update-identity",
|
||||
DELETE_IDENTITY = "delete-identity",
|
||||
LOGIN_IDENTITY_UNIVERSAL_AUTH = "login-identity-universal-auth",
|
||||
ADD_IDENTITY_UNIVERSAL_AUTH = "add-identity-universal-auth",
|
||||
UPDATE_IDENTITY_UNIVERSAL_AUTH = "update-identity-universal-auth",
|
||||
GET_IDENTITY_UNIVERSAL_AUTH = "get-identity-universal-auth",
|
||||
CREATE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "create-identity-universal-auth-client-secret",
|
||||
REVOKE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "revoke-identity-universal-auth-client-secret",
|
||||
GET_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRETS = "get-identity-universal-auth-client-secret",
|
||||
CREATE_SERVICE_TOKEN_V3 = "create-service-token-v3", // v3
|
||||
UPDATE_SERVICE_TOKEN_V3 = "update-service-token-v3", // v3
|
||||
DELETE_SERVICE_TOKEN_V3 = "delete-service-token-v3", // v3
|
||||
CREATE_ENVIRONMENT = "create-environment",
|
||||
UPDATE_ENVIRONMENT = "update-environment",
|
||||
DELETE_ENVIRONMENT = "delete-environment",
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { ActorType, EventType } from "./enums";
|
||||
import { IIdentityTrustedIp } from "../../../models";
|
||||
import { IServiceTokenV3TrustedIp } from "../../../models/serviceTokenDataV3";
|
||||
|
||||
interface UserActorMetadata {
|
||||
userId: string;
|
||||
@ -11,11 +11,6 @@ interface ServiceActorMetadata {
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface IdentityActorMetadata {
|
||||
identityId: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface UserActor {
|
||||
type: ActorType.USER;
|
||||
metadata: UserActorMetadata;
|
||||
@ -26,12 +21,16 @@ export interface ServiceActor {
|
||||
metadata: ServiceActorMetadata;
|
||||
}
|
||||
|
||||
export interface IdentityActor {
|
||||
type: ActorType.IDENTITY;
|
||||
metadata: IdentityActorMetadata;
|
||||
export interface ServiceActorV3 {
|
||||
type: ActorType.SERVICE_V3;
|
||||
metadata: ServiceActorMetadata;
|
||||
}
|
||||
|
||||
export type Actor = UserActor | ServiceActor | IdentityActor;
|
||||
// export interface MachineActor {
|
||||
// type: ActorType.Machine;
|
||||
// }
|
||||
|
||||
export type Actor = UserActor | ServiceActor | ServiceActorV3;
|
||||
|
||||
interface GetSecretsEvent {
|
||||
type: EventType.GET_SECRETS;
|
||||
@ -221,91 +220,36 @@ interface DeleteServiceTokenEvent {
|
||||
};
|
||||
}
|
||||
|
||||
interface CreateIdentityEvent { // note: currently not logging org-role
|
||||
type: EventType.CREATE_IDENTITY;
|
||||
interface CreateServiceTokenV3Event {
|
||||
type: EventType.CREATE_SERVICE_TOKEN_V3;
|
||||
metadata: {
|
||||
identityId: string;
|
||||
name: string;
|
||||
isActive: boolean;
|
||||
role: string;
|
||||
trustedIps: Array<IServiceTokenV3TrustedIp>;
|
||||
expiresAt?: Date;
|
||||
};
|
||||
}
|
||||
|
||||
interface UpdateIdentityEvent {
|
||||
type: EventType.UPDATE_IDENTITY;
|
||||
interface UpdateServiceTokenV3Event {
|
||||
type: EventType.UPDATE_SERVICE_TOKEN_V3;
|
||||
metadata: {
|
||||
identityId: string;
|
||||
name?: string;
|
||||
isActive?: boolean;
|
||||
role?: string;
|
||||
trustedIps?: Array<IServiceTokenV3TrustedIp>;
|
||||
expiresAt?: Date;
|
||||
};
|
||||
}
|
||||
|
||||
interface DeleteIdentityEvent {
|
||||
type: EventType.DELETE_IDENTITY;
|
||||
interface DeleteServiceTokenV3Event {
|
||||
type: EventType.DELETE_SERVICE_TOKEN_V3;
|
||||
metadata: {
|
||||
identityId: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface LoginIdentityUniversalAuthEvent {
|
||||
type: EventType.LOGIN_IDENTITY_UNIVERSAL_AUTH ;
|
||||
metadata: {
|
||||
identityId: string;
|
||||
identityUniversalAuthId: string;
|
||||
clientSecretId: string;
|
||||
identityAccessTokenId: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface AddIdentityUniversalAuthEvent {
|
||||
type: EventType.ADD_IDENTITY_UNIVERSAL_AUTH;
|
||||
metadata: {
|
||||
identityId: string;
|
||||
clientSecretTrustedIps: Array<IIdentityTrustedIp>;
|
||||
accessTokenTTL: number;
|
||||
accessTokenMaxTTL: number;
|
||||
accessTokenNumUsesLimit: number;
|
||||
accessTokenTrustedIps: Array<IIdentityTrustedIp>;
|
||||
};
|
||||
}
|
||||
|
||||
interface UpdateIdentityUniversalAuthEvent {
|
||||
type: EventType.UPDATE_IDENTITY_UNIVERSAL_AUTH;
|
||||
metadata: {
|
||||
identityId: string;
|
||||
clientSecretTrustedIps?: Array<IIdentityTrustedIp>;
|
||||
accessTokenTTL?: number;
|
||||
accessTokenMaxTTL?: number;
|
||||
accessTokenNumUsesLimit?: number;
|
||||
accessTokenTrustedIps?: Array<IIdentityTrustedIp>;
|
||||
};
|
||||
}
|
||||
|
||||
interface GetIdentityUniversalAuthEvent {
|
||||
type: EventType.GET_IDENTITY_UNIVERSAL_AUTH;
|
||||
metadata: {
|
||||
identityId: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface CreateIdentityUniversalAuthClientSecretEvent {
|
||||
type: EventType.CREATE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET ;
|
||||
metadata: {
|
||||
identityId: string;
|
||||
clientSecretId: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface GetIdentityUniversalAuthClientSecretsEvent {
|
||||
type: EventType.GET_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRETS;
|
||||
metadata: {
|
||||
identityId: string;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
interface RevokeIdentityUniversalAuthClientSecretEvent {
|
||||
type: EventType.REVOKE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET ;
|
||||
metadata: {
|
||||
identityId: string;
|
||||
clientSecretId: string;
|
||||
name: string;
|
||||
isActive: boolean;
|
||||
role: string;
|
||||
expiresAt?: Date;
|
||||
trustedIps: Array<IServiceTokenV3TrustedIp>;
|
||||
};
|
||||
}
|
||||
|
||||
@ -551,16 +495,9 @@ export type Event =
|
||||
| DeleteTrustedIPEvent
|
||||
| CreateServiceTokenEvent
|
||||
| DeleteServiceTokenEvent
|
||||
| CreateIdentityEvent
|
||||
| UpdateIdentityEvent
|
||||
| DeleteIdentityEvent
|
||||
| LoginIdentityUniversalAuthEvent
|
||||
| AddIdentityUniversalAuthEvent
|
||||
| UpdateIdentityUniversalAuthEvent
|
||||
| GetIdentityUniversalAuthEvent
|
||||
| CreateIdentityUniversalAuthClientSecretEvent
|
||||
| GetIdentityUniversalAuthClientSecretsEvent
|
||||
| RevokeIdentityUniversalAuthClientSecretEvent
|
||||
| CreateServiceTokenV3Event
|
||||
| UpdateServiceTokenV3Event
|
||||
| DeleteServiceTokenV3Event
|
||||
| CreateEnvironmentEvent
|
||||
| UpdateEnvironmentEvent
|
||||
| DeleteEnvironmentEvent
|
||||
|
@ -3,11 +3,10 @@ export * from "./secretVersion";
|
||||
export * from "./folderVersion";
|
||||
export * from "./role";
|
||||
export * from "./ssoConfig";
|
||||
export * from "./ldapConfig";
|
||||
export * from "./trustedIp";
|
||||
export * from "./auditLog";
|
||||
export * from "./gitRisks";
|
||||
export * from "./gitAppOrganizationInstallation";
|
||||
export * from "./gitAppInstallationSession";
|
||||
export * from "./secretApprovalPolicy";
|
||||
export * from "./secretApprovalRequest";
|
||||
export * from "./secretApprovalRequest";
|
||||
|
@ -1,79 +0,0 @@
|
||||
import { Schema, Types, model } from "mongoose";
|
||||
|
||||
export interface ILDAPConfig {
|
||||
organization: Types.ObjectId;
|
||||
isActive: boolean;
|
||||
url: string;
|
||||
encryptedBindDN: string;
|
||||
bindDNIV: string;
|
||||
bindDNTag: string;
|
||||
encryptedBindPass: string;
|
||||
bindPassIV: string;
|
||||
bindPassTag: string;
|
||||
searchBase: string;
|
||||
encryptedCACert: string;
|
||||
caCertIV: string;
|
||||
caCertTag: string;
|
||||
}
|
||||
|
||||
const ldapConfigSchema = new Schema<ILDAPConfig>(
|
||||
{
|
||||
organization: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "Organization"
|
||||
},
|
||||
isActive: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
url: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
encryptedBindDN: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
bindDNIV: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
bindDNTag: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
encryptedBindPass: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
bindPassIV: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
bindPassTag: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
searchBase: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
encryptedCACert: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
caCertIV: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
caCertTag: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true
|
||||
}
|
||||
);
|
||||
|
||||
export const LDAPConfig = model<ILDAPConfig>("LDAPConfig", ldapConfigSchema);
|
@ -1,31 +0,0 @@
|
||||
import express from "express";
|
||||
const router = express.Router();
|
||||
import { requireAuth } from "../../../middleware";
|
||||
import { AuthMode } from "../../../variables";
|
||||
import { identitiesController } from "../../controllers/v1";
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
identitiesController.createIdentity
|
||||
);
|
||||
|
||||
router.patch(
|
||||
"/:identityId",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT]
|
||||
}),
|
||||
identitiesController.updateIdentity
|
||||
);
|
||||
|
||||
router.delete(
|
||||
"/:identityId",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT]
|
||||
}),
|
||||
identitiesController.deleteIdentity
|
||||
);
|
||||
|
||||
export default router;
|
@ -1,4 +1,3 @@
|
||||
import identities from "./identities";
|
||||
import secret from "./secret";
|
||||
import secretSnapshot from "./secretSnapshot";
|
||||
import organizations from "./organizations";
|
||||
@ -14,7 +13,6 @@ import secretRotationProvider from "./secretRotationProvider";
|
||||
import secretRotation from "./secretRotation";
|
||||
|
||||
export {
|
||||
identities,
|
||||
secret,
|
||||
secretSnapshot,
|
||||
organizations,
|
||||
|
@ -7,7 +7,7 @@ import { workspaceController } from "../../controllers/v1";
|
||||
router.get(
|
||||
"/:workspaceId/secret-snapshots",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
}),
|
||||
workspaceController.getWorkspaceSecretSnapshots
|
||||
);
|
||||
@ -23,7 +23,7 @@ router.get(
|
||||
router.post(
|
||||
"/:workspaceId/secret-snapshots/rollback",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
}),
|
||||
workspaceController.rollbackWorkspaceSecretSnapshot
|
||||
);
|
||||
@ -31,7 +31,7 @@ router.post(
|
||||
router.get(
|
||||
"/:workspaceId/audit-logs",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
}),
|
||||
workspaceController.getWorkspaceAuditLogs
|
||||
);
|
||||
|
@ -1,5 +1,7 @@
|
||||
import serviceTokenData from "./serviceTokenData";
|
||||
import apiKeyData from "./apiKeyData";
|
||||
|
||||
export {
|
||||
serviceTokenData,
|
||||
apiKeyData
|
||||
}
|
44
backend/src/ee/routes/v3/serviceTokenData.ts
Normal file
44
backend/src/ee/routes/v3/serviceTokenData.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import express from "express";
|
||||
const router = express.Router();
|
||||
import { requireAuth } from "../../../middleware";
|
||||
import { AuthMode } from "../../../variables";
|
||||
import { serviceTokenDataController } from "../../controllers/v3";
|
||||
|
||||
router.get(
|
||||
"/me/key",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.SERVICE_ACCESS_TOKEN]
|
||||
}),
|
||||
serviceTokenDataController.getServiceTokenDataKey
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/me/token",
|
||||
serviceTokenDataController.refreshToken
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT]
|
||||
}),
|
||||
serviceTokenDataController.createServiceTokenData
|
||||
);
|
||||
|
||||
router.patch(
|
||||
"/:serviceTokenDataId",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT]
|
||||
}),
|
||||
serviceTokenDataController.updateServiceTokenData
|
||||
);
|
||||
|
||||
router.delete(
|
||||
"/:serviceTokenDataId",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT]
|
||||
}),
|
||||
serviceTokenDataController.deleteServiceTokenData
|
||||
);
|
||||
|
||||
export default router;
|
@ -3,6 +3,7 @@ import { AuditLog, Event } from "../models";
|
||||
import { AuthData } from "../../interfaces/middleware";
|
||||
import EELicenseService from "./EELicenseService";
|
||||
import { Workspace } from "../../models";
|
||||
import { OrganizationNotFoundError } from "../../utils/errors";
|
||||
|
||||
interface EventScope {
|
||||
workspaceId?: Types.ObjectId;
|
||||
@ -13,42 +14,31 @@ type ValidEventScope =
|
||||
| Required<Pick<EventScope, "workspaceId">>
|
||||
| Required<Pick<EventScope, "organizationId">>
|
||||
| Required<EventScope>
|
||||
| Record<string, never>;
|
||||
|
||||
export default class EEAuditLogService {
|
||||
static async createAuditLog(authData: AuthData, event: Event, eventScope: ValidEventScope = {}, shouldSave = true) {
|
||||
static async createAuditLog(authData: AuthData, event: Event, eventScope: ValidEventScope, shouldSave = true) {
|
||||
|
||||
const MS_IN_DAY = 24 * 60 * 60 * 1000;
|
||||
|
||||
let organizationId;
|
||||
if ("organizationId" in eventScope) {
|
||||
organizationId = eventScope.organizationId;
|
||||
}
|
||||
const organizationId = ("organizationId" in eventScope)
|
||||
? eventScope.organizationId
|
||||
: (await Workspace.findById(eventScope.workspaceId).select("organization").lean())?.organization;
|
||||
|
||||
let workspaceId;
|
||||
if ("workspaceId" in eventScope) {
|
||||
workspaceId = eventScope.workspaceId;
|
||||
|
||||
if (!organizationId) {
|
||||
organizationId = (await Workspace.findById(workspaceId).select("organization").lean())?.organization;
|
||||
}
|
||||
}
|
||||
|
||||
let expiresAt;
|
||||
if (organizationId) {
|
||||
const ttl = (await EELicenseService.getPlan(organizationId)).auditLogsRetentionDays * MS_IN_DAY;
|
||||
expiresAt = new Date(Date.now() + ttl);
|
||||
}
|
||||
if (!organizationId) throw OrganizationNotFoundError({
|
||||
message: "createAuditLog: Failed to create audit log due to missing organizationId"
|
||||
});
|
||||
|
||||
const ttl = (await EELicenseService.getPlan(organizationId)).auditLogsRetentionDays * MS_IN_DAY;
|
||||
|
||||
const auditLog = await new AuditLog({
|
||||
actor: authData.actor,
|
||||
organization: organizationId,
|
||||
workspace: workspaceId,
|
||||
workspace: ("workspaceId" in eventScope) ? eventScope.workspaceId : undefined,
|
||||
ipAddress: authData.ipAddress,
|
||||
event,
|
||||
userAgent: authData.userAgent,
|
||||
userAgentType: authData.userAgentType,
|
||||
expiresAt
|
||||
expiresAt: new Date(Date.now() + ttl)
|
||||
});
|
||||
|
||||
if (shouldSave) {
|
||||
|
@ -11,15 +11,10 @@ import { UnauthorizedRequestError } from "../../utils/errors";
|
||||
import { FieldCondition, FieldInstruction, JsInterpreter } from "@ucast/mongo2js";
|
||||
import picomatch from "picomatch";
|
||||
import { AuthData } from "../../interfaces/middleware";
|
||||
import { ActorType, IRole, Role } from "../models";
|
||||
import {
|
||||
IIdentity,
|
||||
IdentityMembership,
|
||||
Membership,
|
||||
ServiceTokenData
|
||||
} from "../../models";
|
||||
import { ADMIN, CUSTOM, MEMBER, NO_ACCESS, VIEWER } from "../../variables";
|
||||
import { BadRequestError } from "../../utils/errors";
|
||||
import { ActorType, IRole } from "../models";
|
||||
import { Membership, ServiceTokenData, ServiceTokenDataV3 } from "../../models";
|
||||
import { ADMIN, CUSTOM, MEMBER, VIEWER } from "../../variables";
|
||||
import { checkIPAgainstBlocklist } from "../../utils/ip";
|
||||
|
||||
const $glob: FieldInstruction<string> = {
|
||||
type: "field",
|
||||
@ -60,8 +55,7 @@ export enum ProjectPermissionSub {
|
||||
Secrets = "secrets",
|
||||
SecretRollback = "secret-rollback",
|
||||
SecretApproval = "secret-approval",
|
||||
SecretRotation = "secret-rotation",
|
||||
Identity = "identity"
|
||||
SecretRotation = "secret-rotation"
|
||||
}
|
||||
|
||||
type SubjectFields = {
|
||||
@ -86,7 +80,6 @@ export type ProjectPermissionSet =
|
||||
| [ProjectPermissionActions, ProjectPermissionSub.ServiceTokens]
|
||||
| [ProjectPermissionActions, ProjectPermissionSub.SecretApproval]
|
||||
| [ProjectPermissionActions, ProjectPermissionSub.SecretRotation]
|
||||
| [ProjectPermissionActions, ProjectPermissionSub.Identity]
|
||||
| [ProjectPermissionActions.Delete, ProjectPermissionSub.Workspace]
|
||||
| [ProjectPermissionActions.Edit, ProjectPermissionSub.Workspace]
|
||||
| [ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback]
|
||||
@ -133,11 +126,6 @@ const buildAdminPermission = () => {
|
||||
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Webhooks);
|
||||
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Webhooks);
|
||||
|
||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.Identity);
|
||||
can(ProjectPermissionActions.Create, ProjectPermissionSub.Identity);
|
||||
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity);
|
||||
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Identity);
|
||||
|
||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.ServiceTokens);
|
||||
can(ProjectPermissionActions.Create, ProjectPermissionSub.ServiceTokens);
|
||||
can(ProjectPermissionActions.Edit, ProjectPermissionSub.ServiceTokens);
|
||||
@ -203,11 +191,6 @@ const buildMemberPermission = () => {
|
||||
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Webhooks);
|
||||
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Webhooks);
|
||||
|
||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.Identity);
|
||||
can(ProjectPermissionActions.Create, ProjectPermissionSub.Identity);
|
||||
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity);
|
||||
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Identity);
|
||||
|
||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.ServiceTokens);
|
||||
can(ProjectPermissionActions.Create, ProjectPermissionSub.ServiceTokens);
|
||||
can(ProjectPermissionActions.Edit, ProjectPermissionSub.ServiceTokens);
|
||||
@ -248,7 +231,6 @@ const buildViewerPermission = () => {
|
||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.Role);
|
||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.Webhooks);
|
||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.Identity);
|
||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.ServiceTokens);
|
||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.Settings);
|
||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.Environments);
|
||||
@ -261,13 +243,6 @@ const buildViewerPermission = () => {
|
||||
|
||||
export const viewerProjectPermission = buildViewerPermission();
|
||||
|
||||
const buildNoAccessProjectPermission = () => {
|
||||
const { build } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
|
||||
return build({ conditionsMatcher });
|
||||
}
|
||||
|
||||
export const noAccessProjectPermissions = buildNoAccessProjectPermission();
|
||||
|
||||
/**
|
||||
* Return permissions for user/service pertaining to workspace with id [workspaceId]
|
||||
*
|
||||
@ -281,7 +256,7 @@ export const getAuthDataProjectPermissions = async ({
|
||||
authData: AuthData;
|
||||
workspaceId: Types.ObjectId;
|
||||
}) => {
|
||||
let role: "admin" | "member" | "viewer" | "no-access" | "custom";
|
||||
let role: "admin" | "member" | "viewer" | "custom";
|
||||
let customRole;
|
||||
|
||||
switch (authData.actor.type) {
|
||||
@ -290,10 +265,10 @@ export const getAuthDataProjectPermissions = async ({
|
||||
user: authData.authPayload._id,
|
||||
workspace: workspaceId
|
||||
})
|
||||
.populate<{
|
||||
customRole: IRole & { permissions: RawRuleOf<MongoAbility<ProjectPermissionSet>>[] };
|
||||
}>("customRole")
|
||||
.exec();
|
||||
.populate<{
|
||||
customRole: IRole & { permissions: RawRuleOf<MongoAbility<ProjectPermissionSet>>[] };
|
||||
}>("customRole")
|
||||
.exec();
|
||||
|
||||
if (!membership || (membership.role === "custom" && !membership.customRole)) {
|
||||
throw UnauthorizedRequestError();
|
||||
@ -309,24 +284,25 @@ export const getAuthDataProjectPermissions = async ({
|
||||
role = "viewer";
|
||||
break;
|
||||
}
|
||||
case ActorType.IDENTITY: {
|
||||
const identityMembership = await IdentityMembership.findOne({
|
||||
identity: authData.authPayload._id,
|
||||
workspace: workspaceId
|
||||
})
|
||||
.populate<{
|
||||
customRole: IRole & { permissions: RawRuleOf<MongoAbility<ProjectPermissionSet>>[] };
|
||||
identity: IIdentity
|
||||
}>("customRole identity")
|
||||
.exec();
|
||||
|
||||
if (!identityMembership || (identityMembership.role === "custom" && !identityMembership.customRole)) {
|
||||
case ActorType.SERVICE_V3: {
|
||||
const serviceTokenData = await ServiceTokenDataV3
|
||||
.findById(authData.authPayload._id)
|
||||
.populate<{
|
||||
customRole: IRole & { permissions: RawRuleOf<MongoAbility<ProjectPermissionSet>>[] };
|
||||
}>("customRole")
|
||||
.exec();
|
||||
|
||||
if (!serviceTokenData || (serviceTokenData.role === "custom" && !serviceTokenData.customRole)) {
|
||||
throw UnauthorizedRequestError();
|
||||
}
|
||||
|
||||
role = identityMembership.role;
|
||||
customRole = identityMembership.customRole;
|
||||
|
||||
checkIPAgainstBlocklist({
|
||||
ipAddress: authData.ipAddress,
|
||||
trustedIps: serviceTokenData.trustedIps
|
||||
});
|
||||
|
||||
role = serviceTokenData.role;
|
||||
customRole = serviceTokenData.customRole;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@ -340,8 +316,6 @@ export const getAuthDataProjectPermissions = async ({
|
||||
return { permission: memberProjectPermissions };
|
||||
case VIEWER:
|
||||
return { permission: viewerProjectPermission };
|
||||
case NO_ACCESS:
|
||||
return { permission: noAccessProjectPermissions };
|
||||
case CUSTOM: {
|
||||
if (!customRole) throw UnauthorizedRequestError();
|
||||
return {
|
||||
@ -355,61 +329,3 @@ export const getAuthDataProjectPermissions = async ({
|
||||
throw UnauthorizedRequestError();
|
||||
}
|
||||
}
|
||||
|
||||
export const getWorkspaceRolePermissions = async (role: string, workspaceId: string) => {
|
||||
const isCustomRole = ![ADMIN, MEMBER, VIEWER, NO_ACCESS].includes(role);
|
||||
if (isCustomRole) {
|
||||
const workspaceRole = await Role.findOne({
|
||||
slug: role,
|
||||
isOrgRole: false,
|
||||
workspace: new Types.ObjectId(workspaceId)
|
||||
});
|
||||
|
||||
if (!workspaceRole) throw BadRequestError({ message: "Role not found" });
|
||||
|
||||
return createMongoAbility<ProjectPermissionSet>(workspaceRole.permissions as RawRuleOf<MongoAbility<ProjectPermissionSet>>[], {
|
||||
conditionsMatcher
|
||||
});
|
||||
}
|
||||
|
||||
switch (role) {
|
||||
case ADMIN:
|
||||
return adminProjectPermissions;
|
||||
case MEMBER:
|
||||
return memberProjectPermissions;
|
||||
case VIEWER:
|
||||
return viewerProjectPermission;
|
||||
case NO_ACCESS:
|
||||
return noAccessProjectPermissions;
|
||||
default:
|
||||
throw BadRequestError({ message: "Role not found" });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts and formats permissions from a CASL Ability object or a raw permission set.
|
||||
* @param ability
|
||||
* @returns
|
||||
*/
|
||||
const extractPermissions = (ability: any) => {
|
||||
return ability.A.map((permission: any) => `${permission.action}_${permission.subject}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two sets of permissions to determine if the first set is at least as privileged as the second set.
|
||||
* The function checks if all permissions in the second set are contained within the first set and if the first set has equal or more permissions.
|
||||
*
|
||||
*/
|
||||
export const isAtLeastAsPrivilegedWorkspace = (permissions1: MongoAbility<ProjectPermissionSet> | ProjectPermissionSet, permissions2: MongoAbility<ProjectPermissionSet> | ProjectPermissionSet) => {
|
||||
|
||||
const set1 = new Set(extractPermissions(permissions1));
|
||||
const set2 = new Set(extractPermissions(permissions2));
|
||||
|
||||
for (const perm of set2) {
|
||||
if (!set1.has(perm)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return set1.size >= set2.size;
|
||||
}
|
@ -1,15 +1,9 @@
|
||||
import { Types } from "mongoose";
|
||||
import { AbilityBuilder, MongoAbility, RawRuleOf, createMongoAbility } from "@casl/ability";
|
||||
import {
|
||||
IIdentity,
|
||||
IdentityMembershipOrg,
|
||||
MembershipOrg
|
||||
} from "../../models";
|
||||
import { ActorType, IRole, Role } from "../models";
|
||||
import { MembershipOrg } from "../../models";
|
||||
import { IRole } from "../models/role";
|
||||
import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors";
|
||||
import { ACCEPTED, ADMIN, CUSTOM, MEMBER, NO_ACCESS} from "../../variables";
|
||||
import { ACCEPTED } from "../../variables";
|
||||
import { conditionsMatcher } from "./ProjectRoleService";
|
||||
import { AuthData } from "../../interfaces/middleware";
|
||||
|
||||
export enum OrgPermissionActions {
|
||||
Read = "read",
|
||||
@ -26,8 +20,7 @@ export enum OrgPermissionSubjects {
|
||||
IncidentAccount = "incident-contact",
|
||||
Sso = "sso",
|
||||
Billing = "billing",
|
||||
SecretScanning = "secret-scanning",
|
||||
Identity = "identity"
|
||||
SecretScanning = "secret-scanning"
|
||||
}
|
||||
|
||||
export type OrgPermissionSet =
|
||||
@ -39,8 +32,7 @@ export type OrgPermissionSet =
|
||||
| [OrgPermissionActions, OrgPermissionSubjects.IncidentAccount]
|
||||
| [OrgPermissionActions, OrgPermissionSubjects.Sso]
|
||||
| [OrgPermissionActions, OrgPermissionSubjects.SecretScanning]
|
||||
| [OrgPermissionActions, OrgPermissionSubjects.Billing]
|
||||
| [OrgPermissionActions, OrgPermissionSubjects.Identity];
|
||||
| [OrgPermissionActions, OrgPermissionSubjects.Billing];
|
||||
|
||||
const buildAdminPermission = () => {
|
||||
const { can, build } = new AbilityBuilder<MongoAbility<OrgPermissionSet>>(createMongoAbility);
|
||||
@ -83,11 +75,6 @@ const buildAdminPermission = () => {
|
||||
can(OrgPermissionActions.Edit, OrgPermissionSubjects.Billing);
|
||||
can(OrgPermissionActions.Delete, OrgPermissionSubjects.Billing);
|
||||
|
||||
can(OrgPermissionActions.Read, OrgPermissionSubjects.Identity);
|
||||
can(OrgPermissionActions.Create, OrgPermissionSubjects.Identity);
|
||||
can(OrgPermissionActions.Edit, OrgPermissionSubjects.Identity);
|
||||
can(OrgPermissionActions.Delete, OrgPermissionSubjects.Identity);
|
||||
|
||||
return build({ conditionsMatcher });
|
||||
};
|
||||
|
||||
@ -111,26 +98,13 @@ const buildMemberPermission = () => {
|
||||
can(OrgPermissionActions.Edit, OrgPermissionSubjects.SecretScanning);
|
||||
can(OrgPermissionActions.Delete, OrgPermissionSubjects.SecretScanning);
|
||||
|
||||
can(OrgPermissionActions.Read, OrgPermissionSubjects.Identity);
|
||||
can(OrgPermissionActions.Create, OrgPermissionSubjects.Identity);
|
||||
can(OrgPermissionActions.Edit, OrgPermissionSubjects.Identity);
|
||||
can(OrgPermissionActions.Delete, OrgPermissionSubjects.Identity);
|
||||
|
||||
return build({ conditionsMatcher });
|
||||
};
|
||||
|
||||
export const memberPermissions = buildMemberPermission();
|
||||
|
||||
const buildNoAccessPermission = () => {
|
||||
const { build } = new AbilityBuilder<MongoAbility<OrgPermissionSet>>(createMongoAbility);
|
||||
return build({ conditionsMatcher });
|
||||
}
|
||||
|
||||
export const noAccessPermissions = buildNoAccessPermission();
|
||||
|
||||
export const getUserOrgPermissions = async (userId: string, orgId: string) => {
|
||||
// TODO(akhilmhdh): speed this up by pulling from cache later
|
||||
|
||||
const membership = await MembershipOrg.findOne({
|
||||
user: userId,
|
||||
organization: orgId,
|
||||
@ -145,13 +119,11 @@ export const getUserOrgPermissions = async (userId: string, orgId: string) => {
|
||||
throw UnauthorizedRequestError({ message: "User doesn't belong to organization" });
|
||||
}
|
||||
|
||||
if (membership.role === ADMIN) return { permission: adminPermissions, membership };
|
||||
if (membership.role === "admin") return { permission: adminPermissions, membership };
|
||||
|
||||
if (membership.role === MEMBER) return { permission: memberPermissions, membership };
|
||||
|
||||
if (membership.role === NO_ACCESS) return { permission: noAccessPermissions, membership }
|
||||
if (membership.role === "member") return { permission: memberPermissions, membership };
|
||||
|
||||
if (membership.role === CUSTOM) {
|
||||
if (membership.role === "custom") {
|
||||
const permission = createMongoAbility<OrgPermissionSet>(membership.customRole.permissions, {
|
||||
conditionsMatcher
|
||||
});
|
||||
@ -160,142 +132,3 @@ export const getUserOrgPermissions = async (userId: string, orgId: string) => {
|
||||
|
||||
throw BadRequestError({ message: "User role not found" });
|
||||
};
|
||||
|
||||
/**
|
||||
* Return permissions for user/service pertaining to organization with id [organizationId]
|
||||
*
|
||||
* Note: should not rely on this function for ST V2 authorization logic
|
||||
* b/c ST V2 does not support role-based access control but also not organization-level resources
|
||||
*/
|
||||
export const getAuthDataOrgPermissions = async ({
|
||||
authData,
|
||||
organizationId
|
||||
}: {
|
||||
authData: AuthData;
|
||||
organizationId: Types.ObjectId;
|
||||
}) => {
|
||||
let role: "admin" | "member" | "no-access" | "custom";
|
||||
let customRole;
|
||||
|
||||
switch (authData.actor.type) {
|
||||
case ActorType.USER: {
|
||||
const membershipOrg = await MembershipOrg.findOne({
|
||||
user: authData.authPayload._id,
|
||||
organization: organizationId,
|
||||
status: ACCEPTED
|
||||
})
|
||||
.populate<{ customRole: IRole & { permissions: RawRuleOf<MongoAbility<OrgPermissionSet>>[] } }>(
|
||||
"customRole"
|
||||
)
|
||||
.exec();
|
||||
|
||||
if (!membershipOrg || (membershipOrg.role === "custom" && !membershipOrg.customRole)) {
|
||||
throw UnauthorizedRequestError({ message: "User doesn't belong to organization" });
|
||||
}
|
||||
|
||||
role = membershipOrg.role;
|
||||
customRole = membershipOrg.customRole;
|
||||
break;
|
||||
}
|
||||
case ActorType.SERVICE: {
|
||||
throw UnauthorizedRequestError({
|
||||
message: "Failed to access organization-level resources with service token"
|
||||
});
|
||||
}
|
||||
case ActorType.IDENTITY: {
|
||||
const identityMembershipOrg = await IdentityMembershipOrg.findOne({
|
||||
identity: authData.authPayload._id,
|
||||
organization: organizationId
|
||||
})
|
||||
.populate<{
|
||||
customRole: IRole & { permissions: RawRuleOf<MongoAbility<OrgPermissionSet>>[] };
|
||||
identity: IIdentity
|
||||
}>("customRole identity")
|
||||
.exec();
|
||||
|
||||
if (!identityMembershipOrg || (identityMembershipOrg.role === "custom" && !identityMembershipOrg.customRole)) {
|
||||
throw UnauthorizedRequestError();
|
||||
}
|
||||
|
||||
role = identityMembershipOrg.role;
|
||||
customRole = identityMembershipOrg.customRole;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw UnauthorizedRequestError();
|
||||
}
|
||||
|
||||
switch (role) {
|
||||
case ADMIN:
|
||||
return { permission: adminPermissions };
|
||||
case MEMBER:
|
||||
return { permission: memberPermissions };
|
||||
case NO_ACCESS:
|
||||
return { permission: noAccessPermissions };
|
||||
case CUSTOM: {
|
||||
if (!customRole) throw UnauthorizedRequestError();
|
||||
return {
|
||||
permission: createMongoAbility<OrgPermissionSet>(
|
||||
customRole.permissions,
|
||||
{ conditionsMatcher }
|
||||
)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const getOrgRolePermissions = async (role: string, orgId: string) => {
|
||||
const isCustomRole = ![ADMIN, MEMBER, NO_ACCESS].includes(role);
|
||||
if (isCustomRole) {
|
||||
const orgRole = await Role.findOne({
|
||||
slug: role,
|
||||
isOrgRole: true,
|
||||
organization: new Types.ObjectId(orgId)
|
||||
});
|
||||
|
||||
if (!orgRole) throw BadRequestError({ message: "Org Role not found" });
|
||||
|
||||
return createMongoAbility<OrgPermissionSet>(orgRole.permissions as RawRuleOf<MongoAbility<OrgPermissionSet>>[], {
|
||||
conditionsMatcher
|
||||
});
|
||||
}
|
||||
|
||||
switch (role) {
|
||||
case ADMIN:
|
||||
return adminPermissions;
|
||||
case MEMBER:
|
||||
return memberPermissions;
|
||||
case NO_ACCESS:
|
||||
return noAccessPermissions;
|
||||
default:
|
||||
throw BadRequestError({ message: "User org role not found" });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts and formats permissions from a CASL Ability object or a raw permission set.
|
||||
* @param ability
|
||||
* @returns
|
||||
*/
|
||||
const extractPermissions = (ability: any) => {
|
||||
return ability.A.map((permission: any) => `${permission.action}_${permission.subject}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two sets of permissions to determine if the first set is at least as privileged as the second set.
|
||||
* The function checks if all permissions in the second set are contained within the first set and if the first set has equal or more permissions.
|
||||
*
|
||||
*/
|
||||
export const isAtLeastAsPrivilegedOrg = (permissions1: MongoAbility<OrgPermissionSet> | OrgPermissionSet, permissions2: MongoAbility<OrgPermissionSet> | OrgPermissionSet) => {
|
||||
|
||||
const set1 = new Set(extractPermissions(permissions1));
|
||||
const set2 = new Set(extractPermissions(permissions2));
|
||||
|
||||
for (const perm of set2) {
|
||||
if (!set1.has(perm)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return set1.size >= set2.size;
|
||||
}
|
@ -13,7 +13,7 @@ import {
|
||||
SECRET_SHARED
|
||||
} from "../variables";
|
||||
import { client, getEncryptionKey, getRootEncryptionKey } from "../config";
|
||||
import { BotNotFoundError, InternalServerError } from "../utils/errors";
|
||||
import { InternalServerError } from "../utils/errors";
|
||||
import { Folder } from "../models";
|
||||
import { getFolderByPath } from "../services/FolderService";
|
||||
import { getAllImportedSecrets } from "../services/SecretImportService";
|
||||
@ -223,7 +223,7 @@ export const getKey = async ({ workspaceId }: { workspaceId: Types.ObjectId }) =
|
||||
workspace: workspaceId
|
||||
}).populate<{ sender: IUser }>("sender", "publicKey");
|
||||
|
||||
if (!botKey) throw BotNotFoundError({ message: `getKey: Failed to find bot key for [workspaceId=${workspaceId}]` })
|
||||
if (!botKey) throw new Error("Failed to find bot key");
|
||||
|
||||
const bot = await Bot.findOne({
|
||||
workspace: workspaceId
|
||||
|
@ -17,7 +17,7 @@ export const validateMembership = async ({
|
||||
}: {
|
||||
userId: Types.ObjectId | string;
|
||||
workspaceId: Types.ObjectId | string;
|
||||
acceptedRoles?: Array<"admin" | "member" | "custom" | "viewer" | "no-access">;
|
||||
acceptedRoles?: Array<"admin" | "member" | "custom" | "viewer">;
|
||||
}) => {
|
||||
const membership = await Membership.findOne({
|
||||
user: userId,
|
||||
|
@ -18,7 +18,7 @@ export const validateMembershipOrg = async ({
|
||||
}: {
|
||||
userId: Types.ObjectId;
|
||||
organizationId: Types.ObjectId;
|
||||
acceptedRoles?: Array<"owner" | "admin" | "member" | "custom" | "no-access">;
|
||||
acceptedRoles?: Array<"owner" | "admin" | "member" | "custom">;
|
||||
acceptedStatuses?: Array<"invited" | "accepted">;
|
||||
}) => {
|
||||
const membershipOrg = await MembershipOrg.findOne({
|
||||
|
@ -4,11 +4,6 @@ import {
|
||||
BotKey,
|
||||
BotOrg,
|
||||
Folder,
|
||||
Identity,
|
||||
IdentityMembership,
|
||||
IdentityMembershipOrg,
|
||||
IdentityUniversalAuth,
|
||||
IdentityUniversalAuthClientSecret,
|
||||
IncidentContactOrg,
|
||||
Integration,
|
||||
IntegrationAuth,
|
||||
@ -21,6 +16,8 @@ import {
|
||||
SecretImport,
|
||||
ServiceToken,
|
||||
ServiceTokenData,
|
||||
ServiceTokenDataV3,
|
||||
ServiceTokenDataV3Key,
|
||||
Tag,
|
||||
Webhook,
|
||||
Workspace
|
||||
@ -126,32 +123,6 @@ export const deleteOrganization = async ({
|
||||
await MembershipOrg.deleteMany({
|
||||
organization: organization._id
|
||||
});
|
||||
|
||||
const identityIds = await IdentityMembershipOrg.distinct("identity", {
|
||||
organization: organization._id
|
||||
});
|
||||
|
||||
await IdentityMembershipOrg.deleteMany({
|
||||
organization: organization._id
|
||||
});
|
||||
|
||||
await Identity.deleteMany({
|
||||
_id: {
|
||||
$in: identityIds
|
||||
}
|
||||
});
|
||||
|
||||
await IdentityUniversalAuth.deleteMany({
|
||||
identity: {
|
||||
$in: identityIds
|
||||
}
|
||||
});
|
||||
|
||||
await IdentityUniversalAuthClientSecret.deleteMany({
|
||||
identity: {
|
||||
$in: identityIds
|
||||
}
|
||||
});
|
||||
|
||||
await BotOrg.deleteMany({
|
||||
organization: organization._id
|
||||
@ -297,7 +268,13 @@ export const deleteOrganization = async ({
|
||||
}
|
||||
});
|
||||
|
||||
await IdentityMembership.deleteMany({
|
||||
await ServiceTokenDataV3.deleteMany({
|
||||
workspace: {
|
||||
$in: workspaceIds
|
||||
}
|
||||
});
|
||||
|
||||
await ServiceTokenDataV3Key.deleteMany({
|
||||
workspace: {
|
||||
$in: workspaceIds
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ export const apiLimiter = rateLimit({
|
||||
// errorHandler: console.error.bind(null, 'rate-limit-mongo')
|
||||
// }),
|
||||
windowMs: 60 * 1000,
|
||||
max: 480,
|
||||
max: 350,
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
skip: (request) => {
|
||||
@ -30,7 +30,7 @@ const authLimit = rateLimit({
|
||||
// collectionName: "expressRateRecords-authLimit",
|
||||
// }),
|
||||
windowMs: 60 * 1000,
|
||||
max: 300,
|
||||
max: 100,
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
keyGenerator: (req, res) => {
|
||||
@ -46,8 +46,8 @@ export const passwordLimiter = rateLimit({
|
||||
// errorHandler: console.error.bind(null, 'rate-limit-mongo'),
|
||||
// collectionName: "expressRateRecords-passwordLimiter",
|
||||
// }),
|
||||
windowMs: 60 * 1000,
|
||||
max: 300,
|
||||
windowMs: 60 * 60 * 1000,
|
||||
max: 10,
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
keyGenerator: (req, res) => {
|
||||
|
@ -579,9 +579,7 @@ export const getSecretsHelper = async ({
|
||||
event: "secrets pulled",
|
||||
distinctId: await TelemetryService.getDistinctId({ authData }),
|
||||
properties: {
|
||||
numberOfSecrets: shouldRecordK8Event
|
||||
? approximateForNoneCapturedEvents
|
||||
: secrets.length,
|
||||
numberOfSecrets: shouldRecordK8Event ? approximateForNoneCapturedEvents : secrets.length,
|
||||
environment,
|
||||
workspaceId,
|
||||
folderId,
|
||||
@ -613,86 +611,42 @@ export const getSecretHelper = async ({
|
||||
type,
|
||||
authData,
|
||||
secretPath = "/",
|
||||
include_imports = true,
|
||||
version
|
||||
include_imports = true
|
||||
}: GetSecretParams) => {
|
||||
const secretBlindIndex = await generateSecretBlindIndexHelper({
|
||||
secretName,
|
||||
workspaceId: new Types.ObjectId(workspaceId)
|
||||
});
|
||||
let secret: ISecret | null | undefined = null;
|
||||
|
||||
// if using service token filter towards the folderId by secretpath
|
||||
|
||||
const folderId = await getFolderIdFromServiceToken(workspaceId, environment, secretPath);
|
||||
|
||||
// try getting personal secret first (if exists)
|
||||
if (version === undefined) {
|
||||
secret = await Secret.findOne({
|
||||
secretBlindIndex,
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
environment,
|
||||
folder: folderId,
|
||||
type: type ?? SECRET_PERSONAL,
|
||||
...(type === SECRET_PERSONAL ? getAuthDataPayloadUserObj(authData) : {})
|
||||
}).lean();
|
||||
|
||||
if (!secret) {
|
||||
// case: failed to find personal secret matching criteria
|
||||
// -> find shared secret matching criteria
|
||||
secret = await Secret.findOne({
|
||||
secretBlindIndex,
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
environment,
|
||||
folder: folderId,
|
||||
type: type ?? SECRET_PERSONAL,
|
||||
...(type === SECRET_PERSONAL ? getAuthDataPayloadUserObj(authData) : {})
|
||||
type: SECRET_SHARED
|
||||
}).lean();
|
||||
} else {
|
||||
const secretVersion = await SecretVersion.findOne({
|
||||
secretBlindIndex,
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
environment,
|
||||
folder: folderId,
|
||||
type: type ?? SECRET_PERSONAL,
|
||||
version
|
||||
}).lean();
|
||||
|
||||
if (secretVersion) {
|
||||
secret = await new Secret({
|
||||
...secretVersion,
|
||||
_id: secretVersion?.secret
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!secret) {
|
||||
// case: failed to find personal secret matching criteria
|
||||
// -> find shared secret matching criteria
|
||||
if (version === undefined) {
|
||||
secret = await Secret.findOne({
|
||||
secretBlindIndex,
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
environment,
|
||||
folder: folderId,
|
||||
type: SECRET_SHARED
|
||||
}).lean();
|
||||
} else {
|
||||
const secretVersion = await SecretVersion.findOne({
|
||||
secretBlindIndex,
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
environment,
|
||||
folder: folderId,
|
||||
type: SECRET_SHARED,
|
||||
version
|
||||
}).lean();
|
||||
|
||||
if (secretVersion) {
|
||||
secret = await new Secret({
|
||||
...secretVersion,
|
||||
_id: secretVersion?.secret
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!secret && include_imports) {
|
||||
// if still no secret found search in imported secret and retreive
|
||||
secret = await getAnImportedSecret(
|
||||
secretName,
|
||||
workspaceId.toString(),
|
||||
environment,
|
||||
folderId,
|
||||
version
|
||||
);
|
||||
secret = await getAnImportedSecret(secretName, workspaceId.toString(), environment, folderId);
|
||||
}
|
||||
|
||||
if (!secret) throw SecretNotFoundError();
|
||||
@ -1187,12 +1141,11 @@ const recursivelyExpandSecret = async (
|
||||
const secRefKey = entities[entities.length - 1];
|
||||
|
||||
const val = await fetchCrossEnv(secRefEnv, secRefPath, secRefKey);
|
||||
if (val !== undefined) {
|
||||
interpolatedValue = interpolatedValue.replaceAll(interpolationSyntax, val);
|
||||
}
|
||||
interpolatedValue = interpolatedValue.replaceAll(interpolationSyntax, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expandedSec[key] = interpolatedValue;
|
||||
return interpolatedValue;
|
||||
};
|
||||
|
@ -3,7 +3,6 @@ import {
|
||||
Bot,
|
||||
BotKey,
|
||||
Folder,
|
||||
IdentityMembership,
|
||||
Integration,
|
||||
IntegrationAuth,
|
||||
Key,
|
||||
@ -13,6 +12,8 @@ import {
|
||||
SecretImport,
|
||||
ServiceToken,
|
||||
ServiceTokenData,
|
||||
ServiceTokenDataV3,
|
||||
ServiceTokenDataV3Key,
|
||||
Tag,
|
||||
Webhook,
|
||||
Workspace
|
||||
@ -177,8 +178,12 @@ export const deleteWorkspace = async ({
|
||||
await ServiceTokenData.deleteMany({
|
||||
workspace: workspace._id
|
||||
});
|
||||
|
||||
await IdentityMembership.deleteMany({
|
||||
|
||||
await ServiceTokenDataV3.deleteMany({
|
||||
workspace: workspace._id
|
||||
});
|
||||
|
||||
await ServiceTokenDataV3Key.deleteMany({
|
||||
workspace: workspace._id
|
||||
});
|
||||
|
||||
|
@ -25,7 +25,6 @@ import {
|
||||
secretSnapshot as eeSecretSnapshotRouter,
|
||||
users as eeUsersRouter,
|
||||
workspace as eeWorkspaceRouter,
|
||||
identities as v1IdentitiesRouter,
|
||||
roles as v1RoleRouter,
|
||||
secretApprovalPolicy as v1SecretApprovalPolicyRouter,
|
||||
secretApprovalRequest as v1SecretApprovalRequestRouter,
|
||||
@ -34,6 +33,7 @@ import {
|
||||
secretScanning as v1SecretScanningRouter
|
||||
} from "./ee/routes/v1";
|
||||
import { apiKeyData as v3apiKeyDataRouter } from "./ee/routes/v3";
|
||||
import { serviceTokenData as v3ServiceTokenDataRouter } from "./ee/routes/v3";
|
||||
import {
|
||||
admin as v1AdminRouter,
|
||||
auth as v1AuthRouter,
|
||||
@ -42,7 +42,6 @@ import {
|
||||
integration as v1IntegrationRouter,
|
||||
inviteOrg as v1InviteOrgRouter,
|
||||
key as v1KeyRouter,
|
||||
ldap as v1LDAPRouter,
|
||||
membershipOrg as v1MembershipOrgRouter,
|
||||
membership as v1MembershipRouter,
|
||||
organization as v1OrganizationRouter,
|
||||
@ -53,7 +52,6 @@ import {
|
||||
secretsFolder as v1SecretsFolder,
|
||||
serviceToken as v1ServiceTokenRouter,
|
||||
signup as v1SignupRouter,
|
||||
universalAuth as v1UniversalAuthRouter,
|
||||
userAction as v1UserActionRouter,
|
||||
user as v1UserRouter,
|
||||
webhooks as v1WebhooksRouter,
|
||||
@ -200,7 +198,6 @@ const main = async () => {
|
||||
}
|
||||
|
||||
// (EE) routes
|
||||
app.use("/api/v1/identities", v1IdentitiesRouter);
|
||||
app.use("/api/v1/secret", eeSecretRouter);
|
||||
app.use("/api/v1/secret-snapshot", eeSecretSnapshotRouter);
|
||||
app.use("/api/v1/users", eeUsersRouter);
|
||||
@ -208,14 +205,14 @@ const main = async () => {
|
||||
app.use("/api/v1/organizations", eeOrganizationsRouter);
|
||||
app.use("/api/v1/sso", eeSSORouter);
|
||||
app.use("/api/v1/cloud-products", eeCloudProductsRouter);
|
||||
app.use("/api/v3/api-key", v3apiKeyDataRouter);
|
||||
app.use("/api/v3/api-key", v3apiKeyDataRouter); // new
|
||||
app.use("/api/v3/service-token", v3ServiceTokenDataRouter); // new
|
||||
app.use("/api/v1/secret-rotation-providers", v1SecretRotationProviderRouter);
|
||||
app.use("/api/v1/secret-rotations", v1SecretRotation);
|
||||
|
||||
// v1 routes
|
||||
app.use("/api/v1/signup", v1SignupRouter);
|
||||
app.use("/api/v1/auth", v1AuthRouter);
|
||||
app.use("/api/v1/auth", v1UniversalAuthRouter); // new
|
||||
app.use("/api/v1/admin", v1AdminRouter);
|
||||
app.use("/api/v1/bot", v1BotRouter);
|
||||
app.use("/api/v1/user", v1UserRouter);
|
||||
@ -223,7 +220,7 @@ const main = async () => {
|
||||
app.use("/api/v1/organization", v1OrganizationRouter);
|
||||
app.use("/api/v1/workspace", v1WorkspaceRouter);
|
||||
app.use("/api/v1/membership-org", v1MembershipOrgRouter);
|
||||
app.use("/api/v1/membership", v1MembershipRouter);
|
||||
app.use("/api/v1/membership", v1MembershipRouter); //
|
||||
app.use("/api/v1/key", v1KeyRouter);
|
||||
app.use("/api/v1/invite-org", v1InviteOrgRouter);
|
||||
app.use("/api/v1/secret", v1SecretRouter); // deprecate
|
||||
@ -238,7 +235,6 @@ const main = async () => {
|
||||
app.use("/api/v1/roles", v1RoleRouter);
|
||||
app.use("/api/v1/secret-approvals", v1SecretApprovalPolicyRouter);
|
||||
app.use("/api/v1/sso", v1SSORouter);
|
||||
app.use("/api/v1/ldap", v1LDAPRouter);
|
||||
app.use("/api/v1/secret-approval-requests", v1SecretApprovalRequestRouter);
|
||||
|
||||
// v2 routes (improvements)
|
||||
@ -251,7 +247,7 @@ const main = async () => {
|
||||
app.use("/api/v2/workspace", v2TagsRouter);
|
||||
app.use("/api/v2/workspace", v2WorkspaceRouter);
|
||||
app.use("/api/v2/secret", v2SecretRouter); // deprecate
|
||||
app.use("/api/v2/secrets", v2SecretsRouter);
|
||||
app.use("/api/v2/secrets", v2SecretsRouter); // note: in the process of moving to v3/secrets
|
||||
app.use("/api/v2/service-token", v2ServiceTokenDataRouter);
|
||||
|
||||
// v3 routes (experimental)
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Types } from "mongoose";
|
||||
import { IIdentity, IServiceTokenData, IUser } from "../../models";
|
||||
import { IdentityActor, ServiceActor, UserActor, UserAgentType } from "../../ee/models";
|
||||
import { IServiceTokenData, IServiceTokenDataV3, IUser } from "../../models";
|
||||
import { ServiceActor, ServiceActorV3, UserActor, UserAgentType } from "../../ee/models";
|
||||
|
||||
interface BaseAuthData {
|
||||
ipAddress: string;
|
||||
@ -14,9 +14,9 @@ export interface UserAuthData extends BaseAuthData {
|
||||
authPayload: IUser;
|
||||
}
|
||||
|
||||
export interface IdentityAuthData extends BaseAuthData {
|
||||
actor: IdentityActor;
|
||||
authPayload: IIdentity;
|
||||
export interface ServiceTokenV3AuthData extends BaseAuthData {
|
||||
actor: ServiceActorV3;
|
||||
authPayload: IServiceTokenDataV3;
|
||||
}
|
||||
|
||||
export interface ServiceTokenAuthData extends BaseAuthData {
|
||||
@ -24,4 +24,4 @@ export interface ServiceTokenAuthData extends BaseAuthData {
|
||||
authPayload: IServiceTokenData;
|
||||
}
|
||||
|
||||
export type AuthData = UserAuthData | IdentityAuthData | ServiceTokenAuthData;
|
||||
export type AuthData = UserAuthData | ServiceTokenV3AuthData | ServiceTokenAuthData;
|
||||
|
@ -38,7 +38,6 @@ export interface GetSecretParams {
|
||||
type?: "shared" | "personal";
|
||||
authData: AuthData;
|
||||
include_imports?: boolean;
|
||||
version?: number;
|
||||
}
|
||||
|
||||
export interface UpdateSecretParams {
|
||||
|
@ -39,9 +39,8 @@ export const requestErrorHandler: ErrorRequestHandler = async (
|
||||
|
||||
Sentry.captureException(error);
|
||||
|
||||
res.status((<RequestError>error).statusCode).send(
|
||||
await error.format(req)
|
||||
);
|
||||
delete (<any>error).stacktrace // remove stack trace from being sent to client
|
||||
res.status((<RequestError>error).statusCode).json(error); // revise json part here
|
||||
|
||||
next();
|
||||
};
|
||||
|
@ -50,7 +50,7 @@ const requireAuth = ({
|
||||
case AuthMode.SERVICE_TOKEN:
|
||||
req.serviceTokenData = authData.authPayload;
|
||||
break;
|
||||
case AuthMode.IDENTITY_ACCESS_TOKEN:
|
||||
case AuthMode.SERVICE_ACCESS_TOKEN:
|
||||
req.serviceTokenData = authData.authPayload;
|
||||
break;
|
||||
case AuthMode.API_KEY:
|
||||
|
@ -1,38 +0,0 @@
|
||||
import { Document, Schema, Types, model } from "mongoose";
|
||||
import { IPType } from "../ee/models";
|
||||
|
||||
export interface IIdentityTrustedIp {
|
||||
ipAddress: string;
|
||||
type: IPType;
|
||||
prefix: number;
|
||||
}
|
||||
|
||||
export enum IdentityAuthMethod {
|
||||
UNIVERSAL_AUTH = "universal-auth"
|
||||
}
|
||||
|
||||
export interface IIdentity extends Document {
|
||||
_id: Types.ObjectId;
|
||||
name: string;
|
||||
authMethod?: IdentityAuthMethod;
|
||||
}
|
||||
|
||||
const identitySchema = new Schema(
|
||||
{
|
||||
name: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
authMethod: {
|
||||
type: String,
|
||||
enum: IdentityAuthMethod,
|
||||
required: false,
|
||||
},
|
||||
|
||||
},
|
||||
{
|
||||
timestamps: true
|
||||
}
|
||||
);
|
||||
|
||||
export const Identity = model<IIdentity>("Identity", identitySchema);
|
@ -1,104 +0,0 @@
|
||||
import { Document, Schema, Types, model } from "mongoose";
|
||||
import { IIdentityTrustedIp } from "./identity";
|
||||
import { IPType } from "../ee/models/trustedIp";
|
||||
|
||||
export interface IIdentityAccessToken extends Document {
|
||||
_id: Types.ObjectId;
|
||||
identity: Types.ObjectId;
|
||||
identityUniversalAuthClientSecret?: Types.ObjectId;
|
||||
accessTokenLastUsedAt?: Date;
|
||||
accessTokenLastRenewedAt?: Date;
|
||||
accessTokenNumUses: number;
|
||||
accessTokenNumUsesLimit: number;
|
||||
accessTokenTTL: number;
|
||||
accessTokenMaxTTL: number;
|
||||
accessTokenTrustedIps: Array<IIdentityTrustedIp>;
|
||||
isAccessTokenRevoked: boolean;
|
||||
updatedAt: Date;
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
const identityAccessTokenSchema = new Schema(
|
||||
{
|
||||
identity: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "Identity",
|
||||
required: false
|
||||
},
|
||||
identityUniversalAuthClientSecret: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "IdentityUniversalAuthClientSecret",
|
||||
required: false
|
||||
},
|
||||
accessTokenLastUsedAt: {
|
||||
type: Date,
|
||||
required: false
|
||||
},
|
||||
accessTokenLastRenewedAt: {
|
||||
type: Date,
|
||||
required: false
|
||||
},
|
||||
accessTokenNumUses: {
|
||||
// number of times access token has been used
|
||||
type: Number,
|
||||
default: 0,
|
||||
required: true
|
||||
},
|
||||
accessTokenNumUsesLimit: {
|
||||
// number of times access token can be used for
|
||||
type: Number,
|
||||
default: 0, // default: used as many times as needed
|
||||
required: true
|
||||
},
|
||||
accessTokenTTL: { // seconds
|
||||
// incremental lifetime
|
||||
type: Number,
|
||||
default: 2592000, // 30 days
|
||||
required: true
|
||||
},
|
||||
accessTokenMaxTTL: { // seconds
|
||||
// max lifetime
|
||||
type: Number,
|
||||
default: 2592000, // 30 days
|
||||
required: true
|
||||
},
|
||||
accessTokenTrustedIps: {
|
||||
type: [
|
||||
{
|
||||
ipAddress: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
enum: [
|
||||
IPType.IPV4,
|
||||
IPType.IPV6
|
||||
],
|
||||
required: true
|
||||
},
|
||||
prefix: {
|
||||
type: Number,
|
||||
required: false
|
||||
}
|
||||
}
|
||||
],
|
||||
default: [{
|
||||
ipAddress: "0.0.0.0",
|
||||
type: IPType.IPV4.toString(),
|
||||
prefix: 0
|
||||
}],
|
||||
required: true
|
||||
},
|
||||
isAccessTokenRevoked: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: true
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true
|
||||
}
|
||||
);
|
||||
|
||||
export const IdentityAccessToken = model<IIdentityAccessToken>("IdentityAccessToken", identityAccessTokenSchema);
|
@ -1,39 +0,0 @@
|
||||
import { Schema, Types, model } from "mongoose";
|
||||
import { ADMIN, CUSTOM, MEMBER, NO_ACCESS, VIEWER } from "../variables";
|
||||
|
||||
export interface IIdentityMembership {
|
||||
_id: Types.ObjectId;
|
||||
identity: Types.ObjectId;
|
||||
workspace: Types.ObjectId;
|
||||
role: "admin" | "member" | "viewer" | "no-access" | "custom";
|
||||
customRole: Types.ObjectId;
|
||||
}
|
||||
|
||||
const identityMembershipSchema = new Schema<IIdentityMembership>(
|
||||
{
|
||||
identity: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "Identity"
|
||||
},
|
||||
workspace: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "Workspace",
|
||||
required: true,
|
||||
index: true,
|
||||
},
|
||||
role: {
|
||||
type: String,
|
||||
enum: [ADMIN, MEMBER, VIEWER, CUSTOM, NO_ACCESS],
|
||||
required: true
|
||||
},
|
||||
customRole: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "Role"
|
||||
}
|
||||
},
|
||||
{
|
||||
timestamps: true
|
||||
}
|
||||
);
|
||||
|
||||
export const IdentityMembership = model<IIdentityMembership>("IdentityMembership", identityMembershipSchema);
|
@ -1,37 +0,0 @@
|
||||
import { Schema, Types, model } from "mongoose";
|
||||
import { ADMIN, CUSTOM, MEMBER, NO_ACCESS} from "../variables";
|
||||
|
||||
export interface IIdentityMembershipOrg {
|
||||
_id: Types.ObjectId;
|
||||
identity: Types.ObjectId;
|
||||
organization: Types.ObjectId;
|
||||
role: "admin" | "member" | "no-access" | "custom";
|
||||
customRole: Types.ObjectId;
|
||||
}
|
||||
|
||||
const identityMembershipOrgSchema = new Schema<IIdentityMembershipOrg>(
|
||||
{
|
||||
identity: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "Identity"
|
||||
},
|
||||
organization: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "Organization"
|
||||
},
|
||||
role: {
|
||||
type: String,
|
||||
enum: [ADMIN, MEMBER, NO_ACCESS, CUSTOM],
|
||||
required: true
|
||||
},
|
||||
customRole: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "Role"
|
||||
}
|
||||
},
|
||||
{
|
||||
timestamps: true
|
||||
}
|
||||
);
|
||||
|
||||
export const IdentityMembershipOrg = model<IIdentityMembershipOrg>("IdentityMembershipOrg", identityMembershipOrgSchema);
|
@ -1,107 +0,0 @@
|
||||
import { Document, Schema, Types, model } from "mongoose";
|
||||
import { IPType } from "../ee/models";
|
||||
import { IIdentityTrustedIp } from "./identity";
|
||||
|
||||
export interface IIdentityUniversalAuth extends Document {
|
||||
_id: Types.ObjectId;
|
||||
identity: Types.ObjectId;
|
||||
clientId: string;
|
||||
clientSecretTrustedIps: Array<IIdentityTrustedIp>;
|
||||
accessTokenTTL: number;
|
||||
accessTokenMaxTTL: number;
|
||||
accessTokenNumUsesLimit: number;
|
||||
accessTokenTrustedIps: Array<IIdentityTrustedIp>;
|
||||
}
|
||||
|
||||
const identityUniversalAuthSchema = new Schema(
|
||||
{
|
||||
identity: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "Identity",
|
||||
required: true
|
||||
},
|
||||
clientId: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
clientSecretTrustedIps: {
|
||||
type: [
|
||||
{
|
||||
ipAddress: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
enum: [
|
||||
IPType.IPV4,
|
||||
IPType.IPV6
|
||||
],
|
||||
required: true
|
||||
},
|
||||
prefix: {
|
||||
type: Number,
|
||||
required: false
|
||||
}
|
||||
}
|
||||
],
|
||||
default: [{
|
||||
ipAddress: "0.0.0.0",
|
||||
type: IPType.IPV4.toString(),
|
||||
prefix: 0
|
||||
}],
|
||||
required: true
|
||||
},
|
||||
accessTokenTTL: { // seconds
|
||||
// incremental lifetime
|
||||
type: Number,
|
||||
default: 7200,
|
||||
required: true
|
||||
},
|
||||
accessTokenMaxTTL: { // seconds
|
||||
// max lifetime
|
||||
type: Number,
|
||||
default: 7200,
|
||||
required: true
|
||||
},
|
||||
accessTokenNumUsesLimit: {
|
||||
// number of times access token can be used for
|
||||
type: Number,
|
||||
default: 0, // default: used as many times as needed
|
||||
required: true
|
||||
},
|
||||
accessTokenTrustedIps: {
|
||||
type: [
|
||||
{
|
||||
ipAddress: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
enum: [
|
||||
IPType.IPV4,
|
||||
IPType.IPV6
|
||||
],
|
||||
required: true
|
||||
},
|
||||
prefix: {
|
||||
type: Number,
|
||||
required: false
|
||||
}
|
||||
}
|
||||
],
|
||||
default: [{
|
||||
ipAddress: "0.0.0.0",
|
||||
type: IPType.IPV4.toString(),
|
||||
prefix: 0
|
||||
}],
|
||||
required: true
|
||||
}
|
||||
},
|
||||
{
|
||||
timestamps: true
|
||||
}
|
||||
);
|
||||
|
||||
export const IdentityUniversalAuth = model<IIdentityUniversalAuth>("IdentityUniversalAuth", identityUniversalAuthSchema);
|
@ -1,81 +0,0 @@
|
||||
import { Document, Schema, Types, model } from "mongoose";
|
||||
|
||||
export interface IIdentityUniversalAuthClientSecret extends Document {
|
||||
_id: Types.ObjectId;
|
||||
identity: Types.ObjectId;
|
||||
identityUniversalAuth : Types.ObjectId;
|
||||
description: string;
|
||||
clientSecretPrefix: string;
|
||||
clientSecretHash: string;
|
||||
clientSecretLastUsedAt?: Date;
|
||||
clientSecretNumUses: number;
|
||||
clientSecretNumUsesLimit: number;
|
||||
clientSecretTTL: number;
|
||||
updatedAt: Date;
|
||||
createdAt: Date;
|
||||
isClientSecretRevoked: boolean;
|
||||
}
|
||||
|
||||
const identityUniversalAuthClientSecretSchema = new Schema(
|
||||
{
|
||||
identity: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "Identity",
|
||||
required: true
|
||||
},
|
||||
identityUniversalAuth: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "IdentityUniversalAuth",
|
||||
required: true
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
clientSecretPrefix: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
clientSecretHash: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
clientSecretLastUsedAt: {
|
||||
type: Date,
|
||||
required: false
|
||||
},
|
||||
clientSecretNumUses: {
|
||||
// number of times client secret has been used
|
||||
// in login operation
|
||||
type: Number,
|
||||
default: 0,
|
||||
required: true
|
||||
},
|
||||
clientSecretNumUsesLimit: {
|
||||
// number of times client secret can be used for
|
||||
// a login operation
|
||||
type: Number,
|
||||
default: 0, // default: used as many times as needed
|
||||
required: true
|
||||
},
|
||||
clientSecretTTL: {
|
||||
type: Number,
|
||||
default: 0, // default: does not expire
|
||||
required: true
|
||||
},
|
||||
isClientSecretRevoked: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
{
|
||||
timestamps: true
|
||||
}
|
||||
);
|
||||
|
||||
identityUniversalAuthClientSecretSchema.index(
|
||||
{ identityUniversalAuth: 1, isClientSecretRevoked: 1 }
|
||||
);
|
||||
|
||||
export const IdentityUniversalAuthClientSecret = model<IIdentityUniversalAuthClientSecret>("IdentityUniversalAuthClientSecret", identityUniversalAuthClientSecretSchema);
|
@ -20,15 +20,8 @@ export * from "./user";
|
||||
export * from "./userAction";
|
||||
export * from "./workspace";
|
||||
export * from "./serviceTokenData"; // TODO: deprecate
|
||||
|
||||
// new
|
||||
export * from "./identity";
|
||||
export * from "./identityMembership";
|
||||
export * from "./identityMembershipOrg";
|
||||
export * from "./identityUniversalAuth";
|
||||
export * from "./identityUniversalAuthClientSecret";
|
||||
export * from "./identityAccessToken";
|
||||
|
||||
export * from "./serviceTokenDataV3";
|
||||
export * from "./serviceTokenDataV3Key";
|
||||
export * from "./apiKeyData"; // TODO: deprecate
|
||||
export * from "./apiKeyDataV2";
|
||||
export * from "./loginSRPDetail";
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Schema, Types, model } from "mongoose";
|
||||
import { ADMIN, CUSTOM, MEMBER, NO_ACCESS, VIEWER } from "../variables";
|
||||
import { ADMIN, CUSTOM, MEMBER, VIEWER } from "../variables";
|
||||
|
||||
export interface IMembershipPermission {
|
||||
environmentSlug: string;
|
||||
@ -11,7 +11,7 @@ export interface IMembership {
|
||||
user: Types.ObjectId;
|
||||
inviteEmail?: string;
|
||||
workspace: Types.ObjectId;
|
||||
role: "admin" | "member" | "viewer" | "no-access" | "custom";
|
||||
role: "admin" | "member" | "viewer" | "custom";
|
||||
customRole: Types.ObjectId;
|
||||
deniedPermissions: IMembershipPermission[];
|
||||
}
|
||||
@ -44,7 +44,7 @@ const membershipSchema = new Schema<IMembership>(
|
||||
},
|
||||
role: {
|
||||
type: String,
|
||||
enum: [ADMIN, MEMBER, VIEWER, NO_ACCESS, CUSTOM],
|
||||
enum: [ADMIN, MEMBER, VIEWER, CUSTOM],
|
||||
required: true
|
||||
},
|
||||
customRole: {
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { Document, Schema, Types, model } from "mongoose";
|
||||
import { ACCEPTED, ADMIN, CUSTOM, INVITED, MEMBER, NO_ACCESS } from "../variables";
|
||||
import { ACCEPTED, ADMIN, CUSTOM, INVITED, MEMBER } from "../variables";
|
||||
|
||||
export interface IMembershipOrg extends Document {
|
||||
_id: Types.ObjectId;
|
||||
user: Types.ObjectId;
|
||||
inviteEmail: string;
|
||||
organization: Types.ObjectId;
|
||||
role: "admin" | "member" | "no-access" | "custom";
|
||||
role: "owner" | "admin" | "member" | "custom";
|
||||
customRole: Types.ObjectId;
|
||||
status: "invited" | "accepted";
|
||||
}
|
||||
@ -26,7 +26,7 @@ const membershipOrgSchema = new Schema(
|
||||
},
|
||||
role: {
|
||||
type: String,
|
||||
enum: [ADMIN, MEMBER, NO_ACCESS, CUSTOM],
|
||||
enum: [ADMIN, MEMBER, CUSTOM],
|
||||
required: true
|
||||
},
|
||||
status: {
|
||||
|
137
backend/src/models/serviceTokenDataV3.ts
Normal file
137
backend/src/models/serviceTokenDataV3.ts
Normal file
@ -0,0 +1,137 @@
|
||||
import { Document, Schema, Types, model } from "mongoose";
|
||||
import { IPType } from "../ee/models";
|
||||
import { ADMIN, CUSTOM, MEMBER, VIEWER } from "../variables";
|
||||
|
||||
export interface IServiceTokenV3TrustedIp {
|
||||
ipAddress: string;
|
||||
type: IPType;
|
||||
prefix: number;
|
||||
}
|
||||
|
||||
export interface IServiceTokenDataV3 extends Document {
|
||||
_id: Types.ObjectId;
|
||||
name: string;
|
||||
workspace: Types.ObjectId;
|
||||
user: Types.ObjectId;
|
||||
publicKey: string;
|
||||
isActive: boolean;
|
||||
refreshTokenLastUsed?: Date;
|
||||
accessTokenLastUsed?: Date;
|
||||
refreshTokenUsageCount: number;
|
||||
accessTokenUsageCount: number;
|
||||
tokenVersion: number;
|
||||
isRefreshTokenRotationEnabled: boolean;
|
||||
expiresAt?: Date;
|
||||
accessTokenTTL: number;
|
||||
role: "admin" | "member" | "viewer" | "custom";
|
||||
customRole: Types.ObjectId;
|
||||
trustedIps: Array<IServiceTokenV3TrustedIp>;
|
||||
}
|
||||
|
||||
const serviceTokenDataV3Schema = new Schema(
|
||||
{
|
||||
name: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
workspace: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "Workspace",
|
||||
required: true
|
||||
},
|
||||
user: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "User",
|
||||
required: true
|
||||
},
|
||||
publicKey: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
isActive: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
required: true
|
||||
},
|
||||
refreshTokenLastUsed: {
|
||||
type: Date,
|
||||
required: false
|
||||
},
|
||||
accessTokenLastUsed: {
|
||||
type: Date,
|
||||
required: false
|
||||
},
|
||||
refreshTokenUsageCount: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
required: true
|
||||
},
|
||||
accessTokenUsageCount: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
required: true
|
||||
},
|
||||
tokenVersion: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
required: true
|
||||
},
|
||||
isRefreshTokenRotationEnabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: true
|
||||
},
|
||||
expiresAt: { // consider revising field name
|
||||
type: Date,
|
||||
required: false,
|
||||
// expires: 0
|
||||
},
|
||||
accessTokenTTL: { // seconds
|
||||
type: Number,
|
||||
default: 7200,
|
||||
required: true
|
||||
},
|
||||
role: {
|
||||
type: String,
|
||||
enum: [ADMIN, MEMBER, VIEWER, CUSTOM],
|
||||
required: true
|
||||
},
|
||||
customRole: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "Role"
|
||||
},
|
||||
trustedIps: {
|
||||
type: [
|
||||
{
|
||||
ipAddress: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
enum: [
|
||||
IPType.IPV4,
|
||||
IPType.IPV6
|
||||
],
|
||||
required: true
|
||||
},
|
||||
prefix: {
|
||||
type: Number,
|
||||
required: false
|
||||
}
|
||||
}
|
||||
],
|
||||
default: [{
|
||||
ipAddress: "0.0.0.0",
|
||||
type: IPType.IPV4.toString(),
|
||||
prefix: 0
|
||||
}],
|
||||
required: true
|
||||
}
|
||||
},
|
||||
{
|
||||
timestamps: true
|
||||
}
|
||||
);
|
||||
|
||||
export const ServiceTokenDataV3 = model<IServiceTokenDataV3>("ServiceTokenDataV3", serviceTokenDataV3Schema);
|
43
backend/src/models/serviceTokenDataV3Key.ts
Normal file
43
backend/src/models/serviceTokenDataV3Key.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import { Document, Schema, Types, model } from "mongoose";
|
||||
|
||||
export interface IServiceTokenDataV3Key extends Document {
|
||||
_id: Types.ObjectId;
|
||||
encryptedKey: string;
|
||||
nonce: string;
|
||||
sender: Types.ObjectId;
|
||||
serviceTokenData: Types.ObjectId;
|
||||
workspace: Types.ObjectId;
|
||||
}
|
||||
|
||||
const serviceTokenDataV3KeySchema = new Schema(
|
||||
{
|
||||
encryptedKey: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
nonce: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
sender: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "User",
|
||||
required: true
|
||||
},
|
||||
serviceTokenData: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "ServiceTokenDataV3",
|
||||
required: true,
|
||||
},
|
||||
workspace: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "Workspace",
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
{
|
||||
timestamps: true
|
||||
}
|
||||
);
|
||||
|
||||
export const ServiceTokenDataV3Key = model<IServiceTokenDataV3Key>("ServiceTokenDataV3Key", serviceTokenDataV3KeySchema);
|
@ -7,8 +7,7 @@ export enum AuthMethod {
|
||||
GITLAB = "gitlab",
|
||||
OKTA_SAML = "okta-saml",
|
||||
AZURE_SAML = "azure-saml",
|
||||
JUMPCLOUD_SAML = "jumpcloud-saml",
|
||||
LDAP = "ldap"
|
||||
JUMPCLOUD_SAML = "jumpcloud-saml"
|
||||
}
|
||||
|
||||
export interface IUser extends Document {
|
||||
@ -56,7 +55,7 @@ const userSchema = new Schema<IUser>(
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
required: false,
|
||||
required: true,
|
||||
unique: true
|
||||
},
|
||||
firstName: {
|
||||
|
@ -1,8 +1,6 @@
|
||||
import signup from "./signup";
|
||||
import bot from "./bot";
|
||||
import auth from "./auth";
|
||||
import universalAuth from "./universalAuth";
|
||||
import ldap from "./ldap";
|
||||
import user from "./user";
|
||||
import userAction from "./userAction";
|
||||
import organization from "./organization";
|
||||
@ -25,7 +23,6 @@ import admin from "./admin";
|
||||
export {
|
||||
signup,
|
||||
auth,
|
||||
universalAuth,
|
||||
bot,
|
||||
user,
|
||||
userAction,
|
||||
@ -44,6 +41,5 @@ export {
|
||||
webhooks,
|
||||
secretImps,
|
||||
sso,
|
||||
ldap,
|
||||
admin
|
||||
};
|
||||
|
@ -156,20 +156,12 @@ router.get(
|
||||
integrationAuthController.getIntegrationAuthTeamCityBuildConfigs
|
||||
);
|
||||
|
||||
router.delete(
|
||||
"/",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT]
|
||||
}),
|
||||
integrationAuthController.deleteIntegrationAuths
|
||||
);
|
||||
|
||||
router.delete(
|
||||
"/:integrationAuthId",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT]
|
||||
}),
|
||||
integrationAuthController.deleteIntegrationAuthById
|
||||
integrationAuthController.deleteIntegrationAuth
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -1,41 +0,0 @@
|
||||
import express from "express";
|
||||
const router = express.Router();
|
||||
import passport from "passport";
|
||||
import { requireAuth } from "../../middleware";
|
||||
import { ldapController } from "../../controllers/v1";
|
||||
import { AuthMode } from "../../variables";
|
||||
|
||||
router.post(
|
||||
"/login",
|
||||
passport.authenticate("ldapauth", {
|
||||
session: false
|
||||
}) as any,
|
||||
ldapController.redirectLDAP
|
||||
);
|
||||
|
||||
router.get(
|
||||
"/config",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT]
|
||||
}),
|
||||
ldapController.getLDAPConfig
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/config",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT]
|
||||
}),
|
||||
ldapController.createLDAPConfig
|
||||
);
|
||||
|
||||
router.patch(
|
||||
"/config",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT]
|
||||
}),
|
||||
ldapController.updateLDAPConfig
|
||||
);
|
||||
|
||||
|
||||
export default router;
|
@ -7,7 +7,7 @@ import { AuthMode } from "../../variables";
|
||||
router.post(
|
||||
"/",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY]
|
||||
}),
|
||||
secretImpsController.createSecretImp
|
||||
);
|
||||
@ -15,7 +15,7 @@ router.post(
|
||||
router.put(
|
||||
"/:id",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY]
|
||||
}),
|
||||
secretImpsController.updateSecretImport
|
||||
);
|
||||
@ -23,7 +23,7 @@ router.put(
|
||||
router.delete(
|
||||
"/:id",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY]
|
||||
}),
|
||||
secretImpsController.deleteSecretImport
|
||||
);
|
||||
@ -31,7 +31,7 @@ router.delete(
|
||||
router.get(
|
||||
"/",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY]
|
||||
}),
|
||||
secretImpsController.getSecretImports
|
||||
);
|
||||
|
@ -12,7 +12,7 @@ import { AuthMode } from "../../variables";
|
||||
router.post(
|
||||
"/",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY]
|
||||
}),
|
||||
createFolder
|
||||
);
|
||||
@ -20,7 +20,7 @@ router.post(
|
||||
router.patch(
|
||||
"/:folderName",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY]
|
||||
}),
|
||||
updateFolderById
|
||||
);
|
||||
@ -28,7 +28,7 @@ router.patch(
|
||||
router.delete(
|
||||
"/:folderName",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY]
|
||||
}),
|
||||
deleteFolder
|
||||
);
|
||||
@ -36,7 +36,7 @@ router.delete(
|
||||
router.get(
|
||||
"/",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY]
|
||||
}),
|
||||
getFolders
|
||||
);
|
||||
|
@ -1,66 +0,0 @@
|
||||
|
||||
import express from "express";
|
||||
const router = express.Router();
|
||||
import { requireAuth } from "../../middleware";
|
||||
import { universalAuthController } from "../../controllers/v1";
|
||||
import { AuthMode } from "../../variables";
|
||||
|
||||
router.post(
|
||||
"/token/renew",
|
||||
universalAuthController.renewAccessToken
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/universal-auth/login",
|
||||
universalAuthController.loginIdentityUniversalAuth
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/universal-auth/identities/:identityId",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
universalAuthController.attachIdentityUniversalAuth
|
||||
);
|
||||
|
||||
router.patch(
|
||||
"/universal-auth/identities/:identityId",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
universalAuthController.updateIdentityUniversalAuth
|
||||
);
|
||||
|
||||
router.get(
|
||||
"/universal-auth/identities/:identityId",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
universalAuthController.getIdentityUniversalAuth
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/universal-auth/identities/:identityId/client-secrets",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
universalAuthController.createUniversalAuthClientSecret
|
||||
);
|
||||
|
||||
router.get(
|
||||
"/universal-auth/identities/:identityId/client-secrets",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
universalAuthController.getUniversalAuthClientSecretsDetails
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/universal-auth/identities/:identityId/client-secrets/:clientSecretId/revoke",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
universalAuthController.revokeUniversalAuthClientSecret
|
||||
);
|
||||
|
||||
export default router;
|
@ -26,7 +26,7 @@ router.post(
|
||||
);
|
||||
|
||||
//remove above ones after depreciation
|
||||
router.post("/mfa/send", authLimiter, requireMfaAuth, authController.sendMfaToken);
|
||||
router.post("/mfa/send", authLimiter, authController.sendMfaToken);
|
||||
|
||||
router.post("/mfa/verify", authLimiter, requireMfaAuth, authController.verifyMfaToken);
|
||||
|
||||
|
@ -7,7 +7,7 @@ import { AuthMode } from "../../variables";
|
||||
router.post(
|
||||
"/:workspaceId/environments",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
}),
|
||||
environmentController.createWorkspaceEnvironment
|
||||
);
|
||||
@ -15,7 +15,7 @@ router.post(
|
||||
router.put(
|
||||
"/:workspaceId/environments",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
}),
|
||||
environmentController.renameWorkspaceEnvironment
|
||||
);
|
||||
@ -23,7 +23,7 @@ router.put(
|
||||
router.patch(
|
||||
"/:workspaceId/environments",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
}),
|
||||
environmentController.reorderWorkspaceEnvironments
|
||||
);
|
||||
@ -31,7 +31,7 @@ router.patch(
|
||||
router.delete(
|
||||
"/:workspaceId/environments",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
}),
|
||||
environmentController.deleteWorkspaceEnvironment
|
||||
);
|
||||
|
@ -9,7 +9,7 @@ import { organizationsController } from "../../controllers/v2";
|
||||
router.get(
|
||||
"/:organizationId/memberships",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
}),
|
||||
organizationsController.getOrganizationMemberships
|
||||
);
|
||||
@ -17,7 +17,7 @@ router.get(
|
||||
router.patch(
|
||||
"/:organizationId/memberships/:membershipId",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
}),
|
||||
organizationsController.updateOrganizationMembership
|
||||
);
|
||||
@ -25,7 +25,7 @@ router.patch(
|
||||
router.delete(
|
||||
"/:organizationId/memberships/:membershipId",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
}),
|
||||
organizationsController.deleteOrganizationMembership
|
||||
);
|
||||
@ -33,7 +33,7 @@ router.delete(
|
||||
router.get(
|
||||
"/:organizationId/workspaces",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
}),
|
||||
organizationsController.getOrganizationWorkspaces
|
||||
);
|
||||
@ -54,12 +54,4 @@ router.delete(
|
||||
organizationsController.deleteOrganizationById
|
||||
);
|
||||
|
||||
router.get(
|
||||
"/:organizationId/identity-memberships",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT]
|
||||
}),
|
||||
organizationsController.getOrganizationIdentityMemberships
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -6,7 +6,7 @@ import {
|
||||
import { AuthMode } from "../../variables";
|
||||
import { serviceTokenDataController } from "../../controllers/v2";
|
||||
|
||||
router.get( // TODO: deprecate (moving to identity)
|
||||
router.get( // TODO: deprecate (moving to ST V3)
|
||||
"/",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.SERVICE_TOKEN]
|
||||
@ -14,7 +14,7 @@ router.get( // TODO: deprecate (moving to identity)
|
||||
serviceTokenDataController.getServiceTokenData
|
||||
);
|
||||
|
||||
router.post( // TODO: deprecate (moving to identity)
|
||||
router.post( // TODO: deprecate (moving to ST V3)
|
||||
"/",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT]
|
||||
@ -22,7 +22,7 @@ router.post( // TODO: deprecate (moving to identity)
|
||||
serviceTokenDataController.createServiceTokenData
|
||||
);
|
||||
|
||||
router.delete( // TODO: deprecate (moving to identity)
|
||||
router.delete( // TODO: deprecate (moving to ST V3)
|
||||
"/:serviceTokenDataId",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT]
|
||||
|
@ -62,7 +62,7 @@ router.get(
|
||||
// new - TODO: rewire dashboard to this route
|
||||
"/:workspaceId/memberships",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
}),
|
||||
workspaceController.getWorkspaceMemberships
|
||||
);
|
||||
@ -71,7 +71,7 @@ router.patch(
|
||||
// TODO - rewire dashboard to this route
|
||||
"/:workspaceId/memberships/:membershipId",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
}),
|
||||
workspaceController.updateWorkspaceMembership
|
||||
);
|
||||
@ -80,7 +80,7 @@ router.delete(
|
||||
// TODO - rewire dashboard to this route
|
||||
"/:workspaceId/memberships/:membershipId",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
}),
|
||||
workspaceController.deleteWorkspaceMembership
|
||||
);
|
||||
@ -93,37 +93,4 @@ router.patch(
|
||||
workspaceController.toggleAutoCapitalization
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/:workspaceId/identity-memberships/:identityId",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
workspaceController.addIdentityToWorkspace
|
||||
);
|
||||
|
||||
router.patch(
|
||||
"/:workspaceId/identity-memberships/:identityId",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
workspaceController.updateIdentityWorkspaceRole
|
||||
);
|
||||
|
||||
router.delete(
|
||||
"/:workspaceId/identity-memberships/:identityId",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
workspaceController.deleteIdentityFromWorkspace
|
||||
);
|
||||
|
||||
router.get(
|
||||
"/:workspaceId/identity-memberships",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
workspaceController.getWorkspaceIdentityMemberships
|
||||
);
|
||||
|
||||
|
||||
export default router;
|
||||
|
@ -7,7 +7,7 @@ import { AuthMode } from "../../variables";
|
||||
router.get(
|
||||
"/raw",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN, AuthMode.SERVICE_ACCESS_TOKEN]
|
||||
}),
|
||||
secretsController.getSecretsRaw
|
||||
);
|
||||
@ -15,7 +15,7 @@ router.get(
|
||||
router.get(
|
||||
"/raw/:secretName",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN, AuthMode.SERVICE_ACCESS_TOKEN]
|
||||
}),
|
||||
requireBlindIndicesEnabled({
|
||||
locationWorkspaceId: "query"
|
||||
@ -29,7 +29,7 @@ router.get(
|
||||
router.post(
|
||||
"/raw/:secretName",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN, AuthMode.SERVICE_ACCESS_TOKEN]
|
||||
}),
|
||||
requireBlindIndicesEnabled({
|
||||
locationWorkspaceId: "body"
|
||||
@ -43,7 +43,7 @@ router.post(
|
||||
router.patch(
|
||||
"/raw/:secretName",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN, AuthMode.SERVICE_ACCESS_TOKEN]
|
||||
}),
|
||||
requireBlindIndicesEnabled({
|
||||
locationWorkspaceId: "body"
|
||||
@ -57,7 +57,7 @@ router.patch(
|
||||
router.delete(
|
||||
"/raw/:secretName",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN, AuthMode.SERVICE_ACCESS_TOKEN]
|
||||
}),
|
||||
requireBlindIndicesEnabled({
|
||||
locationWorkspaceId: "body"
|
||||
@ -71,7 +71,7 @@ router.delete(
|
||||
router.get(
|
||||
"/",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN, AuthMode.SERVICE_ACCESS_TOKEN]
|
||||
}),
|
||||
requireBlindIndicesEnabled({
|
||||
locationWorkspaceId: "query"
|
||||
@ -116,7 +116,7 @@ router.delete(
|
||||
router.post(
|
||||
"/:secretName",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN, AuthMode.SERVICE_ACCESS_TOKEN]
|
||||
}),
|
||||
requireBlindIndicesEnabled({
|
||||
locationWorkspaceId: "body"
|
||||
@ -127,7 +127,7 @@ router.post(
|
||||
router.get(
|
||||
"/:secretName",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN, AuthMode.SERVICE_ACCESS_TOKEN]
|
||||
}),
|
||||
requireBlindIndicesEnabled({
|
||||
locationWorkspaceId: "query"
|
||||
@ -138,7 +138,7 @@ router.get(
|
||||
router.patch(
|
||||
"/:secretName",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN, AuthMode.SERVICE_ACCESS_TOKEN]
|
||||
}),
|
||||
requireBlindIndicesEnabled({
|
||||
locationWorkspaceId: "body"
|
||||
@ -149,7 +149,7 @@ router.patch(
|
||||
router.delete(
|
||||
"/:secretName",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN, AuthMode.SERVICE_ACCESS_TOKEN]
|
||||
}),
|
||||
requireBlindIndicesEnabled({
|
||||
locationWorkspaceId: "body"
|
||||
|
@ -34,4 +34,12 @@ router.post(
|
||||
|
||||
// --
|
||||
|
||||
router.get(
|
||||
"/:workspaceId/service-token",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT]
|
||||
}),
|
||||
workspacesController.getWorkspaceServiceTokenData
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { Types } from "mongoose";
|
||||
import { generateSecretBlindIndexHelper } from "../helpers";
|
||||
import { SecretVersion } from "../ee/models";
|
||||
import { Folder, ISecret, Secret, SecretImport } from "../models";
|
||||
import { getFolderByPath } from "./FolderService";
|
||||
|
||||
@ -10,8 +9,7 @@ export const getAnImportedSecret = async (
|
||||
secretName: string,
|
||||
workspaceId: string,
|
||||
environment: string,
|
||||
folderId = "root",
|
||||
version?: number
|
||||
folderId = "root"
|
||||
) => {
|
||||
const secretBlindIndex = await generateSecretBlindIndexHelper({
|
||||
secretName,
|
||||
@ -50,26 +48,10 @@ export const getAnImportedSecret = async (
|
||||
});
|
||||
if (importedSecByFid.length === 0) return;
|
||||
|
||||
let secret;
|
||||
if (version === undefined) {
|
||||
secret = await Secret.findOne({
|
||||
workspace: workspaceId,
|
||||
secretBlindIndex
|
||||
}).or(importedSecByFid.map(({ environment, folderId }) => ({ environment, folder: folderId }))).lean()
|
||||
} else {
|
||||
const secretVersion = await SecretVersion.findOne({
|
||||
workspace: workspaceId,
|
||||
secretBlindIndex,
|
||||
version
|
||||
}).or(importedSecByFid.map(({ environment, folderId }) => ({ environment, folder: folderId }))).lean();
|
||||
|
||||
if (secretVersion) {
|
||||
secret = await new Secret({
|
||||
...secretVersion,
|
||||
_id: secretVersion.secret,
|
||||
});
|
||||
}
|
||||
}
|
||||
const secret = await Secret.findOne({
|
||||
workspace: workspaceId,
|
||||
secretBlindIndex
|
||||
}).or(importedSecByFid.map(({ environment, folderId }) => ({ environment, folder: folderId }))).lean()
|
||||
|
||||
return secret;
|
||||
};
|
||||
|
@ -8,12 +8,12 @@ import {
|
||||
getTelemetryEnabled,
|
||||
} from "../config";
|
||||
import {
|
||||
Identity,
|
||||
ServiceTokenData,
|
||||
User
|
||||
User,
|
||||
} from "../models";
|
||||
import {
|
||||
AccountNotFoundError,
|
||||
BadRequestError,
|
||||
} from "../utils/errors";
|
||||
|
||||
class Telemetry {
|
||||
@ -22,7 +22,7 @@ class Telemetry {
|
||||
*/
|
||||
static logTelemetryMessage = async () => {
|
||||
|
||||
if (!(await getTelemetryEnabled())) {
|
||||
if(!(await getTelemetryEnabled())){
|
||||
[
|
||||
"To improve, Infisical collects telemetry data about general usage.",
|
||||
"This helps us understand how the product is doing and guide our product development to create the best possible platform; it also helps us demonstrate growth as we support Infisical as open-source software.",
|
||||
@ -42,8 +42,8 @@ class Telemetry {
|
||||
postHogClient = new PostHog(await getPostHogProjectApiKey(), {
|
||||
host: await getPostHogHost(),
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return postHogClient;
|
||||
}
|
||||
|
||||
@ -52,7 +52,6 @@ class Telemetry {
|
||||
}: {
|
||||
authData: AuthData;
|
||||
}) => {
|
||||
|
||||
let distinctId = "";
|
||||
if (authData.authPayload instanceof User) {
|
||||
distinctId = authData.authPayload.email;
|
||||
@ -60,14 +59,14 @@ class Telemetry {
|
||||
if (authData.authPayload.user) {
|
||||
const user = await User.findById(authData.authPayload.user, "email");
|
||||
if (!user) throw AccountNotFoundError();
|
||||
distinctId = user.email;
|
||||
distinctId = user.email;
|
||||
}
|
||||
} else if (authData.authPayload instanceof Identity) {
|
||||
distinctId = `identity-${authData.authPayload._id.toString()}`
|
||||
} else {
|
||||
distinctId = "unknown-auth-data"
|
||||
}
|
||||
|
||||
|
||||
if (distinctId === "") throw BadRequestError({
|
||||
message: "Failed to obtain distinct id for logging telemetry",
|
||||
});
|
||||
|
||||
return distinctId;
|
||||
}
|
||||
}
|
||||
|
1
backend/src/types/express/index.d.ts
vendored
1
backend/src/types/express/index.d.ts
vendored
@ -40,7 +40,6 @@ declare global {
|
||||
requestData: {
|
||||
[key: string]: string
|
||||
};
|
||||
organizationId: string;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,104 +0,0 @@
|
||||
import jwt from "jsonwebtoken";
|
||||
import { IIdentity, IdentityAccessToken } from "../../../models";
|
||||
import { getAuthSecret } from "../../../config";
|
||||
import { AuthTokenType } from "../../../variables";
|
||||
import { UnauthorizedRequestError } from "../../errors";
|
||||
import { checkIPAgainstBlocklist } from "../../../utils/ip";
|
||||
|
||||
interface ValidateIdentityParams {
|
||||
authTokenValue: string;
|
||||
ipAddress: string;
|
||||
}
|
||||
|
||||
export const validateIdentity = async ({
|
||||
authTokenValue,
|
||||
ipAddress
|
||||
}: ValidateIdentityParams) => {
|
||||
const decodedToken = <jwt.IdentityAccessTokenJwtPayload>(
|
||||
jwt.verify(authTokenValue, await getAuthSecret())
|
||||
);
|
||||
|
||||
if (decodedToken.authTokenType !== AuthTokenType.IDENTITY_ACCESS_TOKEN) throw UnauthorizedRequestError();
|
||||
|
||||
const identityAccessToken = await IdentityAccessToken
|
||||
.findOne({
|
||||
_id: decodedToken.identityAccessTokenId,
|
||||
isAccessTokenRevoked: false
|
||||
})
|
||||
.populate<{ identity: IIdentity }>("identity");
|
||||
|
||||
if (!identityAccessToken || !identityAccessToken?.identity) throw UnauthorizedRequestError();
|
||||
|
||||
const {
|
||||
accessTokenNumUsesLimit,
|
||||
accessTokenNumUses,
|
||||
accessTokenTTL,
|
||||
accessTokenLastRenewedAt,
|
||||
accessTokenMaxTTL,
|
||||
createdAt: accessTokenCreatedAt
|
||||
} = identityAccessToken;
|
||||
|
||||
checkIPAgainstBlocklist({
|
||||
ipAddress,
|
||||
trustedIps: identityAccessToken.accessTokenTrustedIps
|
||||
});
|
||||
|
||||
// ttl check
|
||||
if (accessTokenTTL > 0) {
|
||||
const currentDate = new Date();
|
||||
if (accessTokenLastRenewedAt) {
|
||||
// access token has been renewed
|
||||
const accessTokenRenewed = new Date(accessTokenLastRenewedAt);
|
||||
const ttlInMilliseconds = accessTokenTTL * 1000;
|
||||
const expirationDate = new Date(accessTokenRenewed.getTime() + ttlInMilliseconds);
|
||||
|
||||
if (currentDate > expirationDate) throw UnauthorizedRequestError({
|
||||
message: "Failed to authenticate identity access token due to TTL expiration"
|
||||
});
|
||||
} else {
|
||||
// access token has never been renewed
|
||||
const accessTokenCreated = new Date(accessTokenCreatedAt);
|
||||
const ttlInMilliseconds = accessTokenTTL * 1000;
|
||||
const expirationDate = new Date(accessTokenCreated.getTime() + ttlInMilliseconds);
|
||||
|
||||
if (currentDate > expirationDate) throw UnauthorizedRequestError({
|
||||
message: "Failed to authenticate identity access token due to TTL expiration"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// max ttl check
|
||||
if (accessTokenMaxTTL > 0) {
|
||||
const accessTokenCreated = new Date(accessTokenCreatedAt);
|
||||
const ttlInMilliseconds = accessTokenMaxTTL * 1000;
|
||||
const currentDate = new Date();
|
||||
const expirationDate = new Date(accessTokenCreated.getTime() + ttlInMilliseconds);
|
||||
|
||||
if (currentDate > expirationDate) throw UnauthorizedRequestError({
|
||||
message: "Failed to authenticate identity access token due to Max TTL expiration"
|
||||
});
|
||||
}
|
||||
|
||||
// num uses check
|
||||
if (
|
||||
accessTokenNumUsesLimit > 0
|
||||
&& accessTokenNumUses === accessTokenNumUsesLimit
|
||||
) {
|
||||
throw UnauthorizedRequestError({
|
||||
message: "Failed to authenticate MI access token due to access token number of uses limit reached"
|
||||
});
|
||||
}
|
||||
|
||||
await IdentityAccessToken.findByIdAndUpdate(
|
||||
identityAccessToken._id,
|
||||
{
|
||||
accessTokenLastUsedAt: new Date(),
|
||||
$inc: { accessTokenNumUses: 1 }
|
||||
},
|
||||
{
|
||||
new: true
|
||||
}
|
||||
);
|
||||
|
||||
return identityAccessToken.identity;
|
||||
}
|
@ -2,4 +2,4 @@ export * from "./apiKey";
|
||||
export * from "./apiKeyV2";
|
||||
export * from "./jwt";
|
||||
export * from "./serviceTokenV2";
|
||||
export * from "./identity";
|
||||
export * from "./serviceTokenV3";
|
64
backend/src/utils/authn/authModeValidators/serviceTokenV3.ts
Normal file
64
backend/src/utils/authn/authModeValidators/serviceTokenV3.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import jwt from "jsonwebtoken";
|
||||
import { Types } from "mongoose";
|
||||
import { ServiceTokenDataV3 } from "../../../models";
|
||||
import { getAuthSecret } from "../../../config";
|
||||
import { AuthTokenType } from "../../../variables";
|
||||
import { UnauthorizedRequestError } from "../../errors";
|
||||
|
||||
interface ValidateServiceTokenV3Params {
|
||||
authTokenValue: string;
|
||||
}
|
||||
|
||||
export const validateServiceTokenV3 = async ({
|
||||
authTokenValue
|
||||
}: ValidateServiceTokenV3Params) => {
|
||||
const decodedToken = <jwt.ServiceRefreshTokenJwtPayload>(
|
||||
jwt.verify(authTokenValue, await getAuthSecret())
|
||||
);
|
||||
|
||||
if (decodedToken.authTokenType !== AuthTokenType.SERVICE_ACCESS_TOKEN) throw UnauthorizedRequestError();
|
||||
|
||||
const serviceTokenData = await ServiceTokenDataV3.findOne({
|
||||
_id: new Types.ObjectId(decodedToken.serviceTokenDataId),
|
||||
isActive: true
|
||||
});
|
||||
|
||||
if (!serviceTokenData) {
|
||||
throw UnauthorizedRequestError({
|
||||
message: "Failed to authenticate"
|
||||
});
|
||||
} else if (serviceTokenData?.expiresAt && new Date(serviceTokenData.expiresAt) < new Date()) {
|
||||
// case: service token expired
|
||||
await ServiceTokenDataV3.findByIdAndUpdate(
|
||||
serviceTokenData._id,
|
||||
{
|
||||
isActive: false
|
||||
},
|
||||
{
|
||||
new: true
|
||||
}
|
||||
);
|
||||
|
||||
throw UnauthorizedRequestError({
|
||||
message: "Failed to authenticate",
|
||||
});
|
||||
} else if (decodedToken.tokenVersion !== serviceTokenData.tokenVersion) {
|
||||
// TODO: raise alarm
|
||||
throw UnauthorizedRequestError({
|
||||
message: "Failed to authenticate",
|
||||
});
|
||||
}
|
||||
|
||||
await ServiceTokenDataV3.findByIdAndUpdate(
|
||||
serviceTokenData._id,
|
||||
{
|
||||
accessTokenLastUsed: new Date(),
|
||||
$inc: { accessTokenUsageCount: 1 }
|
||||
},
|
||||
{
|
||||
new: true
|
||||
}
|
||||
);
|
||||
|
||||
return serviceTokenData;
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import { AuthData } from "../../../interfaces/middleware";
|
||||
import {
|
||||
Identity,
|
||||
ServiceTokenData,
|
||||
ServiceTokenDataV3,
|
||||
User
|
||||
} from "../../../models";
|
||||
|
||||
@ -19,7 +19,7 @@ import {
|
||||
return { serviceTokenDataId: authData.authPayload._id };
|
||||
}
|
||||
|
||||
if (authData.authPayload instanceof Identity) {
|
||||
if (authData.authPayload instanceof ServiceTokenDataV3) {
|
||||
return { serviceTokenDataId: authData.authPayload._id };
|
||||
}
|
||||
};
|
||||
@ -38,7 +38,7 @@ export const getAuthDataPayloadUserObj = (authData: AuthData) => {
|
||||
return { user: authData.authPayload.user };
|
||||
}
|
||||
|
||||
if (authData.authPayload instanceof Identity) {
|
||||
return {};
|
||||
if (authData.authPayload instanceof ServiceTokenDataV3) {
|
||||
return { user: authData.authPayload.user };
|
||||
}
|
||||
}
|
@ -7,9 +7,9 @@ import { UnauthorizedRequestError } from "../../errors";
|
||||
import {
|
||||
validateAPIKey,
|
||||
validateAPIKeyV2,
|
||||
validateIdentity,
|
||||
validateJWT,
|
||||
validateServiceTokenV2
|
||||
validateServiceTokenV2,
|
||||
validateServiceTokenV3
|
||||
} from "../authModeValidators";
|
||||
import { getUserAgentType } from "../../posthog";
|
||||
|
||||
@ -36,7 +36,7 @@ interface GetAuthDataParams {
|
||||
* - SERVICE_TOKEN
|
||||
* - API_KEY
|
||||
* - JWT
|
||||
* - IDENTITY_ACCESS_TOKEN (from identity)
|
||||
* - SERVICE_ACCESS_TOKEN (from ST V3)
|
||||
* - API_KEY_V2
|
||||
* @param {Object} params
|
||||
* @param {Object.<string, (string|string[]|undefined)>} params.headers - The HTTP request headers, usually from Express's `req.headers`.
|
||||
@ -77,8 +77,8 @@ export const extractAuthMode = async ({
|
||||
return { authMode: AuthMode.JWT, authTokenValue };
|
||||
case AuthTokenType.API_KEY:
|
||||
return { authMode: AuthMode.API_KEY_V2, authTokenValue };
|
||||
case AuthTokenType.IDENTITY_ACCESS_TOKEN:
|
||||
return { authMode: AuthMode.IDENTITY_ACCESS_TOKEN, authTokenValue };
|
||||
case AuthTokenType.SERVICE_ACCESS_TOKEN:
|
||||
return { authMode: AuthMode.SERVICE_ACCESS_TOKEN, authTokenValue };
|
||||
default:
|
||||
throw UnauthorizedRequestError({
|
||||
message: "Failed to authenticate unknown authentication method"
|
||||
@ -115,21 +115,20 @@ export const getAuthData = async ({
|
||||
userAgentType
|
||||
}
|
||||
}
|
||||
case AuthMode.IDENTITY_ACCESS_TOKEN: {
|
||||
const identity = await validateIdentity({
|
||||
authTokenValue,
|
||||
ipAddress
|
||||
case AuthMode.SERVICE_ACCESS_TOKEN: {
|
||||
const serviceTokenData = await validateServiceTokenV3({
|
||||
authTokenValue
|
||||
});
|
||||
|
||||
return {
|
||||
actor: {
|
||||
type: ActorType.IDENTITY,
|
||||
type: ActorType.SERVICE_V3,
|
||||
metadata: {
|
||||
identityId: identity._id.toString(),
|
||||
name: identity.name
|
||||
serviceId: serviceTokenData._id.toString(),
|
||||
name: serviceTokenData.name
|
||||
}
|
||||
},
|
||||
authPayload: identity,
|
||||
authPayload: serviceTokenData,
|
||||
ipAddress,
|
||||
userAgent,
|
||||
userAgentType
|
||||
|
@ -2,4 +2,3 @@ export { initializeGoogleStrategy } from "./google";
|
||||
export { initializeGitHubStrategy } from "./github";
|
||||
export { initializeGitLabStrategy } from "./gitlab";
|
||||
export { initializeSamlStrategy } from "./saml";
|
||||
export { initializeLdapStrategy } from "./ldap";
|
||||
|
@ -1,141 +0,0 @@
|
||||
import { Types } from "mongoose";
|
||||
import {
|
||||
AuthMethod,
|
||||
MembershipOrg,
|
||||
Organization,
|
||||
User
|
||||
} from "../../../models";
|
||||
import passport from "passport";
|
||||
import { OrganizationNotFoundError } from "../../errors";
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const LdapStrategy = require("passport-ldapauth");
|
||||
import { getAuthSecret, getJwtProviderAuthLifetime } from "../../../config";
|
||||
import { createToken } from "../../../helpers/auth";
|
||||
import {
|
||||
ACCEPTED,
|
||||
AuthTokenType,
|
||||
MEMBER
|
||||
} from "../../../variables";
|
||||
import { getLdapConfigHelper } from "../../../ee/helpers/organizations";
|
||||
|
||||
const getLDAPConfiguration = (req: any, callback: any) => {
|
||||
const {
|
||||
organizationId
|
||||
} = req.body as {
|
||||
organizationId: string;
|
||||
};
|
||||
|
||||
req.organizationId = organizationId;
|
||||
|
||||
const boot = async () => {
|
||||
const ldapConfig = await getLdapConfigHelper({
|
||||
organizationId: new Types.ObjectId(organizationId)
|
||||
});
|
||||
|
||||
// example
|
||||
// var opts = {
|
||||
// server: {
|
||||
// // url: 'ldaps://openldap:636', // connection over SSL/TLS
|
||||
// url: 'ldap://openldap:389',
|
||||
// bindDN: 'cn=admin,dc=acme,dc=com',
|
||||
// bindCredentials: 'admin',
|
||||
// searchBase: 'ou=people,dc=acme,dc=com',
|
||||
// searchFilter: '(uid={{username}})',
|
||||
// searchAttributes: ['uid', 'givenName', 'sn'], // optional, defaults to all (get username too)
|
||||
// // tlsOptions: {
|
||||
// // ca: [caCert]
|
||||
// // }
|
||||
// },
|
||||
// passReqToCallback: true
|
||||
// };
|
||||
|
||||
const opts = {
|
||||
server: {
|
||||
url: ldapConfig.url,
|
||||
bindDN: ldapConfig.bindDN,
|
||||
bindCredentials: ldapConfig.bindPass,
|
||||
searchBase: ldapConfig.searchBase,
|
||||
searchFilter: "(uid={{username}})",
|
||||
searchAttributes: ["uid", "givenName", "sn"],
|
||||
...(ldapConfig.caCert !== "" ? {
|
||||
tlsOptions: {
|
||||
ca: [ldapConfig.caCert]
|
||||
}
|
||||
} : {}
|
||||
)
|
||||
},
|
||||
passReqToCallback: true
|
||||
};
|
||||
|
||||
callback(null, opts);
|
||||
}
|
||||
|
||||
process.nextTick(async () => {
|
||||
await boot();
|
||||
});
|
||||
};
|
||||
|
||||
export const initializeLdapStrategy = async () => {
|
||||
passport.use(new LdapStrategy(getLDAPConfiguration,
|
||||
async (req: any, user: any, done: any) => {
|
||||
|
||||
const organization = await Organization.findById(req.organizationId);
|
||||
if (!organization) return done(OrganizationNotFoundError());
|
||||
|
||||
const ldapUsername = user.uid;
|
||||
const firstName = user.givenName;
|
||||
const lastName = user.sn;
|
||||
const ldapEmail = `ldap-${ldapUsername}-${organization._id.toString()}@ldap.com`;
|
||||
|
||||
try {
|
||||
let user = await User.findOne({
|
||||
email: ldapEmail
|
||||
}).select("+publicKey");
|
||||
|
||||
if (!user) {
|
||||
user = await new User({
|
||||
email: ldapEmail,
|
||||
authMethods: [AuthMethod.LDAP],
|
||||
firstName,
|
||||
lastName,
|
||||
organization
|
||||
}).save();
|
||||
|
||||
await new MembershipOrg({
|
||||
user: user._id,
|
||||
organization: organization._id,
|
||||
role: MEMBER,
|
||||
status: ACCEPTED
|
||||
}).save();
|
||||
}
|
||||
|
||||
const isUserCompleted = !!user.publicKey;
|
||||
const providerAuthToken = createToken({
|
||||
payload: {
|
||||
authTokenType: AuthTokenType.PROVIDER_TOKEN,
|
||||
userId: user._id.toString(),
|
||||
email: user.email,
|
||||
firstName,
|
||||
lastName,
|
||||
organizationName: organization?.name,
|
||||
organizationId: organization?._id,
|
||||
authMethod: AuthMethod.LDAP,
|
||||
isUserCompleted,
|
||||
...(req.body.RelayState ? {
|
||||
callbackPort: JSON.parse(req.body.RelayState).callbackPort as string
|
||||
} : {})
|
||||
},
|
||||
expiresIn: await getJwtProviderAuthLifetime(),
|
||||
secret: await getAuthSecret(),
|
||||
});
|
||||
|
||||
req.isUserCompleted = isUserCompleted;
|
||||
req.providerAuthToken = providerAuthToken;
|
||||
|
||||
return done(null, user);
|
||||
} catch (err) {
|
||||
return done(null, false);
|
||||
}
|
||||
}
|
||||
));
|
||||
}
|
@ -75,7 +75,7 @@ export const initializeSamlStrategy = async () => {
|
||||
const organization = await Organization.findById(req.ssoConfig.organization);
|
||||
|
||||
if (!organization) return done(OrganizationNotFoundError());
|
||||
|
||||
|
||||
const email = profile.email;
|
||||
const firstName = profile.firstName;
|
||||
const lastName = profile.lastName;
|
||||
@ -154,7 +154,6 @@ export const initializeSamlStrategy = async () => {
|
||||
firstName,
|
||||
lastName,
|
||||
organizationName: organization?.name,
|
||||
organizationId: organization?._id,
|
||||
authMethod: req.ssoConfig.authProvider,
|
||||
isUserCompleted,
|
||||
...(req.body.RelayState ? {
|
||||
|
@ -7,14 +7,8 @@ export const getUserAgentType = function (userAgent: string | undefined) {
|
||||
return UserAgentType.CLI;
|
||||
} else if (userAgent == UserAgentType.K8_OPERATOR) {
|
||||
return UserAgentType.K8_OPERATOR;
|
||||
} else if (userAgent == UserAgentType.TERRAFORM) {
|
||||
return UserAgentType.TERRAFORM;
|
||||
} else if (userAgent.toLowerCase().includes("mozilla")) {
|
||||
return UserAgentType.WEB;
|
||||
} else if (userAgent.includes(UserAgentType.NODE_SDK)) {
|
||||
return UserAgentType.NODE_SDK;
|
||||
} else if (userAgent.includes(UserAgentType.PYTHON_SDK)) {
|
||||
return UserAgentType.PYTHON_SDK;
|
||||
} else {
|
||||
return UserAgentType.OTHER;
|
||||
}
|
||||
|
@ -53,10 +53,9 @@ export default class RequestError extends Error {
|
||||
){
|
||||
|
||||
super(message)
|
||||
this._logLevel = logLevel || LogLevel.INFO;
|
||||
this._logLevel = logLevel || LogLevel.INFO
|
||||
this._logName = LogLevel[this._logLevel];
|
||||
this.statusCode = statusCode;
|
||||
this.message = message;
|
||||
this.statusCode = statusCode
|
||||
this.type = type
|
||||
this.context = context || {}
|
||||
this.extra = []
|
||||
|
@ -30,7 +30,6 @@ import {
|
||||
initializeGitHubStrategy,
|
||||
initializeGitLabStrategy,
|
||||
initializeGoogleStrategy,
|
||||
initializeLdapStrategy,
|
||||
initializeSamlStrategy
|
||||
} from "../authn/passport";
|
||||
import { logger } from "../logging";
|
||||
@ -68,7 +67,6 @@ export const setup = async () => {
|
||||
await initializeGitHubStrategy();
|
||||
await initializeGitLabStrategy();
|
||||
await initializeSamlStrategy();
|
||||
await initializeLdapStrategy();
|
||||
|
||||
// re-encrypt any data previously encrypted under server hex 128-bit ENCRYPTION_KEY
|
||||
// to base64 256-bit ROOT_ENCRYPTION_KEY
|
||||
|
@ -84,107 +84,15 @@ export const ResetPasswordV1 = z.object({
|
||||
})
|
||||
});
|
||||
|
||||
export const RenewAccessTokenV1 = z.object({
|
||||
export const SendMfaTokenV2 = z.object({
|
||||
body: z.object({
|
||||
accessToken: z.string().trim(),
|
||||
})
|
||||
});
|
||||
|
||||
export const LoginUniversalAuthV1 = z.object({
|
||||
body: z.object({
|
||||
clientId: z.string().trim(),
|
||||
clientSecret: z.string().trim()
|
||||
})
|
||||
});
|
||||
|
||||
export const AddUniversalAuthToIdentityV1 = z.object({
|
||||
params: z.object({
|
||||
identityId: z.string().trim()
|
||||
}),
|
||||
body: z.object({
|
||||
clientSecretTrustedIps: z
|
||||
.object({
|
||||
ipAddress: z.string().trim(),
|
||||
})
|
||||
.array()
|
||||
.min(1)
|
||||
.default([{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }]),
|
||||
accessTokenTrustedIps: z
|
||||
.object({
|
||||
ipAddress: z.string().trim(),
|
||||
})
|
||||
.array()
|
||||
.min(1)
|
||||
.default([{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }]),
|
||||
accessTokenTTL: z.number().int().min(1).refine(value => value !== 0, {
|
||||
message: "accessTokenTTL must have a non zero number",
|
||||
}).default(2592000),
|
||||
accessTokenMaxTTL: z.number().int().refine(value => value !== 0, {
|
||||
message: "accessTokenMaxTTL must have a non zero number",
|
||||
}).default(2592000), // 30 days
|
||||
accessTokenNumUsesLimit: z.number().int().min(0).default(0)
|
||||
})
|
||||
});
|
||||
|
||||
export const UpdateUniversalAuthToIdentityV1 = z.object({
|
||||
params: z.object({
|
||||
identityId: z.string()
|
||||
}),
|
||||
body: z.object({
|
||||
clientSecretTrustedIps: z
|
||||
.object({
|
||||
ipAddress: z.string().trim()
|
||||
})
|
||||
.array()
|
||||
.min(1)
|
||||
.optional(),
|
||||
accessTokenTrustedIps: z
|
||||
.object({
|
||||
ipAddress: z.string().trim(),
|
||||
})
|
||||
.array()
|
||||
.min(1)
|
||||
.optional(),
|
||||
accessTokenTTL: z.number().int().min(0).optional(),
|
||||
accessTokenNumUsesLimit: z.number().int().min(0).optional(),
|
||||
accessTokenMaxTTL: z.number().int().refine(value => value !== 0, {
|
||||
message: "accessTokenMaxTTL must have a non zero number",
|
||||
}).optional(),
|
||||
}),
|
||||
});
|
||||
|
||||
export const GetUniversalAuthForIdentityV1 = z.object({
|
||||
params: z.object({
|
||||
identityId: z.string().trim()
|
||||
})
|
||||
});
|
||||
|
||||
export const CreateUniversalAuthClientSecretV1 = z.object({
|
||||
params: z.object({
|
||||
identityId: z.string()
|
||||
}),
|
||||
body: z.object({
|
||||
description: z.string().trim().default(""),
|
||||
numUsesLimit: z.number().min(0).default(0),
|
||||
ttl: z.number().min(0).default(0),
|
||||
}),
|
||||
});
|
||||
|
||||
export const GetUniversalAuthClientSecretsV1 = z.object({
|
||||
params: z.object({
|
||||
identityId: z.string()
|
||||
})
|
||||
});
|
||||
|
||||
export const RevokeUniversalAuthClientSecretV1 = z.object({
|
||||
params: z.object({
|
||||
identityId: z.string(),
|
||||
clientSecretId: z.string()
|
||||
email: z.string().email().trim()
|
||||
})
|
||||
});
|
||||
|
||||
export const VerifyMfaTokenV2 = z.object({
|
||||
body: z.object({
|
||||
email: z.string().email().trim(),
|
||||
mfaToken: z.string().trim()
|
||||
})
|
||||
});
|
||||
|
@ -1,26 +0,0 @@
|
||||
import { z } from "zod";
|
||||
import { NO_ACCESS } from "../variables";
|
||||
|
||||
export const CreateIdentityV1 = z.object({
|
||||
body: z.object({
|
||||
name: z.string().trim(),
|
||||
organizationId: z.string().trim(),
|
||||
role: z.string().trim().min(1).default(NO_ACCESS)
|
||||
})
|
||||
});
|
||||
|
||||
export const UpdateIdentityV1 = z.object({
|
||||
params: z.object({
|
||||
identityId: z.string()
|
||||
}),
|
||||
body: z.object({
|
||||
name: z.string().trim().optional(),
|
||||
role: z.string().trim().min(1).optional()
|
||||
}),
|
||||
});
|
||||
|
||||
export const DeleteIdentityV1 = z.object({
|
||||
params: z.object({
|
||||
identityId: z.string()
|
||||
}),
|
||||
});
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user