Compare commits

..

245 Commits

Author SHA1 Message Date
Maidul Islam
e3e049b66c Update build-staging-and-deploy.yml 2024-03-20 22:14:46 -04:00
Maidul Islam
878e4a79e7 Merge pull request #1606 from Infisical/daniel/ui-imported-folders-fix
Fix: UI indicator for imports
2024-03-20 22:08:50 -04:00
Daniel Hougaard
609ce8e5cc Fix: Improved UI import indicators 2024-03-21 03:06:14 +01:00
Maidul Islam
04c1ea9b11 Update build-staging-and-deploy.yml 2024-03-20 18:03:49 -04:00
Maidul Islam
3baca73e53 add seperate step for ecr build 2024-03-20 16:25:54 -04:00
Daniel Hougaard
36adf6863b Fix: UI secret import indicator 2024-03-20 21:09:54 +01:00
Daniel Hougaard
6363e7d30a Update index.ts 2024-03-20 20:26:28 +01:00
Daniel Hougaard
f9621fad8e Fix: Remove duplicate type 2024-03-20 20:26:00 +01:00
Daniel Hougaard
90be28b87a Feat: Import indicator 2024-03-20 20:25:48 +01:00
Daniel Hougaard
671adee4d7 Feat: Indicator for wether or not secrets are imported 2024-03-20 20:24:06 +01:00
Daniel Hougaard
c9cb90c98e Feat: Add center property to tooltip 2024-03-20 20:23:21 +01:00
Akhil Mohan
9f691df395 Merge pull request #1607 from Infisical/fix-secrets-by-name
set imports=true for get secret by name
2024-03-20 23:50:15 +05:30
Maidul Islam
d702a61586 set imports=true for get secret by name 2024-03-20 14:06:16 -04:00
Maidul Islam
1c16f406a7 remove debug 2024-03-20 13:06:29 -04:00
Maidul Islam
90f739caa6 correct repo name env 2024-03-20 13:05:59 -04:00
Maidul Islam
ede8b6f286 add .env context for ecr tag 2024-03-20 13:00:06 -04:00
Maidul Islam
232c547d75 correct ecr image tag 2024-03-20 11:54:33 -04:00
Maidul Islam
fe08bbb691 push to ecr 2024-03-20 11:46:47 -04:00
Maidul Islam
2bd06ecde4 login into ecr 2024-03-20 11:31:39 -04:00
Daniel Hougaard
08b79d65ea Fix: Remove unused lint disable 2024-03-20 14:55:02 +01:00
Daniel Hougaard
4e1733ba6c Fix: More reverting 2024-03-20 14:44:40 +01:00
Daniel Hougaard
a4e495ea1c Fix: Restructured frontend 2024-03-20 14:42:34 +01:00
Daniel Hougaard
a750d68363 Fix: Reverted backend changes 2024-03-20 14:40:24 +01:00
Daniel Hougaard
d7161a353d Fix: Better variable naming 2024-03-20 13:15:51 +01:00
Daniel Hougaard
12c414817f Fix: Remove debugging logs 2024-03-20 13:14:18 +01:00
Daniel Hougaard
e5e494d0ee Fix: Also display imported folder indicator for nested folders 2024-03-20 13:13:50 +01:00
Daniel Hougaard
5a21b85e9e Fix: Removed overlap from other working branch 2024-03-20 13:13:19 +01:00
Daniel Hougaard
348fdf6429 Feat: Visualize imported folders in overview page (include imported folders in response) 2024-03-20 12:56:11 +01:00
Daniel Hougaard
88e609cb66 Feat: New types for imported folders 2024-03-20 12:55:40 +01:00
Daniel Hougaard
78058d691a Enhancement: Add disabled prop to Tooltip component 2024-03-20 12:55:08 +01:00
Daniel Hougaard
1d465a50c3 Feat: Visualize imported folders in overview page 2024-03-20 12:54:44 +01:00
Maidul Islam
ffc7249c7c update diagram 2024-03-19 23:44:12 -04:00
Maidul Islam
90bcf23097 Update README.md 2024-03-19 23:36:07 -04:00
Maidul Islam
5fa4d9029d Merge pull request #1577 from Salman2301/fix-notification-z-index
fix: notification error behind detail sidebar
2024-03-19 18:56:14 -04:00
Maidul Islam
7160cf58ee Merge branch 'main' into fix-notification-z-index 2024-03-19 18:50:58 -04:00
Maidul Islam
6b2d757e39 remove outdated healthcheck 2024-03-19 17:21:46 -04:00
Daniel Hougaard
c075fcceca Merge pull request #1591 from Infisical/daniel/prettier-fix
Chore: Prettier formatting
2024-03-19 21:23:11 +01:00
Maidul Islam
e25f5dd65f Merge pull request #1605 from Infisical/creation-policy-k8s
add managed secret creation policy
2024-03-19 15:23:16 -04:00
Maidul Islam
3eef023c30 add managed secret creation policy 2024-03-19 14:58:17 -04:00
Tuan Dang
e63deb0860 Patch org role slug validation 2024-03-19 10:23:00 -07:00
Maidul Islam
02b2851990 Merge pull request #1601 from Infisical/fix/db-host
fix(server): updated secret rotation to pick on db host in validation
2024-03-19 10:03:12 -04:00
Akhil Mohan
cb828200e1 fix(server): updated secret rotation to pick on db host in validation 2024-03-19 13:56:21 +00:00
Akhil Mohan
77d068ae2c Merge pull request #1599 from Infisical/daniel/improve-create-project
Fix: Remove required org slug from create project route
2024-03-19 18:31:16 +05:30
Daniel Hougaard
8702af671d Fix: Typings error 2024-03-19 13:45:57 +01:00
Daniel Hougaard
31c0fd96ea Update UserInfoStep.tsx 2024-03-19 13:39:34 +01:00
Daniel Hougaard
2c539697df Feat: Remove orgSlug from create project endpoint 2024-03-19 13:39:24 +01:00
Daniel Hougaard
ae97b74933 Feat: Improve create project, remove organization slug from frontend 2024-03-19 13:38:58 +01:00
Akhil Mohan
3e6af2dae5 Merge pull request #1597 from Infisical/daniel/api-endpoint-fix
Fix: Mintlify interactive API docs defaulting to localhost server
2024-03-19 16:32:49 +05:30
Daniel Hougaard
3c91e1127f Fix: Mintlify docs defaulting to localhost endpoint 2024-03-19 11:58:01 +01:00
vmatsiiako
0e31a9146a Update ee.mdx 2024-03-18 22:10:09 -07:00
Daniel Hougaard
fa1b28b33f Update .eslintrc.js 2024-03-18 16:07:49 +01:00
Daniel Hougaard
415cf31b2d Fix: Lint bug (Cannot read properties of undefined (reading 'getTokens')) 2024-03-18 16:01:14 +01:00
Daniel Hougaard
9002e6cb33 Fix: Format entire frontend properly 2024-03-18 16:00:03 +01:00
Daniel Hougaard
1ede551c3e Fix: Format entire frontend properly 2024-03-18 15:59:47 +01:00
Daniel Hougaard
b7b43858f6 Fix: Format entire frontend properly 2024-03-18 15:55:01 +01:00
Akhil Mohan
c91789e6d0 Merge pull request #1590 from Infisical/daniel/ts-comments
Fix: Github warnings / Lint warnings
2024-03-18 20:19:26 +05:30
Daniel Hougaard
db0ba4be10 Fix: Github warnings / Lint warnings 2024-03-18 15:47:03 +01:00
Akhil Mohan
f73c807aa0 Merge pull request #1589 from Infisical/daniel/ui-improvements
Fix: Select organization UX & project card enhancements
2024-03-18 20:14:39 +05:30
Daniel Hougaard
d1dacd81aa Chore: UX improvements to project cards 2024-03-18 15:36:59 +01:00
Daniel Hougaard
e8b635ce37 Fix: Code cleanup & truncate organization names 2024-03-18 15:36:01 +01:00
Daniel Hougaard
1d3e03e308 Merge pull request #1583 from Infisical/daniel/saml-cli-fix
Fix: SAML CLI login
2024-03-18 10:44:48 +01:00
Daniel Hougaard
88e2eff7eb Fix: SAML CLI login 2024-03-18 10:37:49 +01:00
Maidul Islam
cd192ee228 Update sdks.mdx 2024-03-17 15:04:14 -04:00
Maidul Islam
1e657968f6 Merge pull request #1449 from Infisical/daniel/slug-projects
(Feat): V2 slugified project routes
2024-03-17 13:54:20 -04:00
Daniel Hougaard
b8ba51512c Fix: Rebase errors 2024-03-17 18:51:19 +01:00
Daniel Hougaard
1ac8ddbd92 Fix: Rebase errors 2024-03-17 18:49:30 +01:00
Daniel Hougaard
a257743fa5 Update secret-approval-request-service.ts 2024-03-17 18:49:30 +01:00
Daniel Hougaard
b5a7240375 Fix: Select org when using init 2024-03-17 18:49:30 +01:00
Maidul Islam
5c2a108c52 add small helpful comment 2024-03-17 18:49:30 +01:00
Daniel Hougaard
b78d8d28ed Feat: CLI support for scoped JWT tokens 2024-03-17 18:49:30 +01:00
Daniel Hougaard
9c9ade52db Feat: Scoped JWT to organization, Add authMethod to services 2024-03-17 18:49:30 +01:00
Daniel Hougaard
4d229ec745 Fix: Email signup and switching organization 2024-03-17 18:49:30 +01:00
Daniel Hougaard
605dad29ca Fix: Rebase error 2024-03-17 18:49:30 +01:00
Daniel Hougaard
bebdad8159 parent 10a292bca563efbe5972d7ffc33ee4b96a868e42
author Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> 1709970985 +0100
committer Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> 1710345579 +0100

parent 10a292bca563efbe5972d7ffc33ee4b96a868e42
author Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> 1709970985 +0100
committer Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> 1710345572 +0100

parent 10a292bca563efbe5972d7ffc33ee4b96a868e42
author Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> 1709970985 +0100
committer Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> 1710345563 +0100

parent 10a292bca563efbe5972d7ffc33ee4b96a868e42
author Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> 1709970985 +0100
committer Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> 1710345551 +0100

parent 10a292bca563efbe5972d7ffc33ee4b96a868e42
author Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> 1709970985 +0100
committer Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> 1710345540 +0100

parent 10a292bca563efbe5972d7ffc33ee4b96a868e42
author Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> 1709970985 +0100
committer Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> 1710345533 +0100

parent 10a292bca563efbe5972d7ffc33ee4b96a868e42
author Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> 1709970985 +0100
committer Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> 1710345529 +0100

parent 10a292bca563efbe5972d7ffc33ee4b96a868e42
author Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> 1709970985 +0100
committer Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> 1710345522 +0100

parent 10a292bca563efbe5972d7ffc33ee4b96a868e42
author Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> 1709970985 +0100
committer Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> 1710345503 +0100

parent 10a292bca563efbe5972d7ffc33ee4b96a868e42
author Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> 1709970985 +0100
committer Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> 1710345496 +0100

parent 10a292bca563efbe5972d7ffc33ee4b96a868e42
author Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> 1709970985 +0100
committer Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> 1710345489 +0100

parent 10a292bca563efbe5972d7ffc33ee4b96a868e42
author Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> 1709970985 +0100
committer Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> 1710345357 +0100

parent 10a292bca563efbe5972d7ffc33ee4b96a868e42
author Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> 1709970985 +0100
committer Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> 1710345061 +0100

parent 10a292bca563efbe5972d7ffc33ee4b96a868e42
author Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> 1709970985 +0100
committer Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> 1710345029 +0100

Feat: Org Scoped JWT Tokens

Add link button

Fix: Avoid invalidating all queries on logout to prevent UI glitch

Update _app.tsx

Feat: Scoped JWT to organization, add authMethod to request

Feat: Scoped JWT to organization, Add authMethod to services

Feat: Scoped JWT to organization, require organization on all requests by default on JWT requests

Update index.ts

Feat: Scoped JWT to organization

Chore: Move SAML org check to permission service

Feat: Scoped JWT to organization, actorAuthMethod to create project DTO

Fix: Invalidate after selecting organization

Chore: Optional 'invalidate' option for create org hook

Fix: Creating dummy workspaces

Fix: Select org after creation

Feat: Org Scoped JWT's, remove inline service

Fix: ActorType unresolved

Fix: Better type checking

Feat: Org scoped JWT's

Fix: Add missing actor org ID

Fix: Add missing actor org ID

Fix: Return access token

Update auth-type.ts

Fix: Add actor org ID

Chore: Remove unused code

Fix: Add missing actor org ID to permission check

Fix: Add missing actor auth method to permission checks

Fix: Include actor org id

Chore: Remove redundant lint comment

Fix: Add missing actorOrgId to service handlers

Fix: Rebase fixes

Fix: Rebase LDAP fixes

Chore: Export Cli login interface

Update queries.tsx

Feat: Org scoped JWT's CLI support

Update inject-permission.ts

Fix: MFA

Remove log

Fix: Admin signup, select organization

Improvement: Use select organization hook

Update permission-service.ts

Fix: Make API keys compatible with old endpoints

Update inject-permission.ts

Chore: Better error messages

Update index.ts

Fix: Signup not redirecting to backup PDF page due to error

Select org on signup

Type improvements

Chore: Removed code that spans out of scope

Fix: Better types

Chore: Move comment

Chore: Change order

Fix: Code readability

Fix: Code readability

Update auth-token-service.ts

Chore: Remove old comments

Fix: Cleanup

Chore: Minor code cleanup

Fix: Add auth method and organization ID to test JWT

Fix: Get org ID in getOrgIdentityPermission DAL operation
2024-03-17 18:49:30 +01:00
Daniel Hougaard
b547309ae4 Fix: Get org ID in getOrgIdentityPermission DAL operation 2024-03-17 18:49:30 +01:00
Daniel Hougaard
d1ebdbcc03 Fix: Add auth method and organization ID to test JWT 2024-03-17 18:49:30 +01:00
Daniel Hougaard
c94caa6fb5 Chore: Minor code cleanup 2024-03-17 18:49:30 +01:00
Daniel Hougaard
f53fa46c51 Fix: Cleanup 2024-03-17 18:49:30 +01:00
Daniel Hougaard
c42d407cda Chore: Remove old comments 2024-03-17 18:49:30 +01:00
Daniel Hougaard
80b4bc18ec Update auth-token-service.ts 2024-03-17 18:49:30 +01:00
Daniel Hougaard
1dbf80d4e6 Fix: Code readability 2024-03-17 18:49:30 +01:00
Daniel Hougaard
700a072ec5 Fix: Code readability 2024-03-17 18:49:30 +01:00
Daniel Hougaard
8f42914df5 Chore: Change order 2024-03-17 18:49:30 +01:00
Daniel Hougaard
831da10073 Chore: Move comment 2024-03-17 18:49:30 +01:00
Daniel Hougaard
6904cd3bda Fix: Better types 2024-03-17 18:49:30 +01:00
Daniel Hougaard
52fd09b87b Chore: Removed code that spans out of scope 2024-03-17 18:49:30 +01:00
Daniel Hougaard
0081bbdf9e Type improvements 2024-03-17 18:49:30 +01:00
Daniel Hougaard
c9e5f2bb75 Select org on signup 2024-03-17 18:49:30 +01:00
Daniel Hougaard
73cc97cf17 Fix: Signup not redirecting to backup PDF page due to error 2024-03-17 18:49:30 +01:00
Daniel Hougaard
0c1d37cc75 Update index.ts 2024-03-17 18:49:30 +01:00
Daniel Hougaard
60fbd8ac44 Chore: Better error messages 2024-03-17 18:49:30 +01:00
Daniel Hougaard
36efa6ba63 Update inject-permission.ts 2024-03-17 18:49:30 +01:00
Daniel Hougaard
961a73f712 Fix: Re-add API key support 2024-03-17 18:49:30 +01:00
Daniel Hougaard
6e2f3800d4 Fix: Make API keys compatible with old endpoints 2024-03-17 18:49:30 +01:00
Daniel Hougaard
258c9e45d4 Update permission-service.ts 2024-03-17 18:49:30 +01:00
Daniel Hougaard
8573263379 Update permission-service.ts 2024-03-17 18:49:30 +01:00
Daniel Hougaard
9a724db6ab Improvement: Use select organization hook 2024-03-17 18:49:30 +01:00
Daniel Hougaard
60a37e784b Fix: member invites, select org 2024-03-17 18:49:30 +01:00
Daniel Hougaard
14c60bd075 Fix: Admin signup, select organization 2024-03-17 18:49:30 +01:00
Daniel Hougaard
de715c03ad Fix: Org scoped JWT's, MFA support 2024-03-17 18:49:30 +01:00
Daniel Hougaard
ddb1d5a1ab Remove log 2024-03-17 18:49:30 +01:00
Daniel Hougaard
41323f205d Fix: MFA 2024-03-17 18:49:30 +01:00
Daniel Hougaard
771498b817 Update inject-permission.ts 2024-03-17 18:49:30 +01:00
Daniel Hougaard
22b2fb4c98 Feat: Org scoped JWT's CLI support 2024-03-17 18:49:30 +01:00
Daniel Hougaard
9bbba92768 Feat: Org scoped JWT's CLI support 2024-03-17 18:49:30 +01:00
Daniel Hougaard
46eea972f7 Feat: Org scoped JWT's CLI support 2024-03-17 18:49:30 +01:00
Daniel Hougaard
9eb2a74bdf Feat: Org scoped JWT's, CLI support 2024-03-17 18:49:30 +01:00
Daniel Hougaard
b80579fdef Update queries.tsx 2024-03-17 18:49:30 +01:00
Daniel Hougaard
214894c88b Chore: Export Cli login interface 2024-03-17 18:49:29 +01:00
Daniel Hougaard
8ff37e3ec9 Fix: Rebase LDAP fixes 2024-03-17 18:49:29 +01:00
Daniel Hougaard
926f719967 Fix: Rebase fixes 2024-03-17 18:49:29 +01:00
Daniel Hougaard
c3a56f469a Fix: Better type checking 2024-03-17 18:49:29 +01:00
Daniel Hougaard
2bd9914373 Fix: Add missing actorOrgId to service handlers 2024-03-17 18:49:29 +01:00
Daniel Hougaard
354bac486a Fix: Don't allow org select screen when token already has an organization ID 2024-03-17 18:49:29 +01:00
Daniel Hougaard
ba22a7fca6 Chore: Remove redundant lint comment 2024-03-17 18:49:29 +01:00
Daniel Hougaard
4aef8ab8ee Fix: Include actor org id 2024-03-17 18:49:29 +01:00
Daniel Hougaard
e89503f00f Fix: Add missing actor auth method to permission checks 2024-03-17 18:49:29 +01:00
Daniel Hougaard
f5f20fbdca Fix: Add missing actor org ID to permission check 2024-03-17 18:49:29 +01:00
Daniel Hougaard
4d4887059a Chore: Remove unused code 2024-03-17 18:49:29 +01:00
Daniel Hougaard
c11c5ec85e Fix: Add actor org ID 2024-03-17 18:49:29 +01:00
Daniel Hougaard
f0e3c9a4b2 Update auth-type.ts 2024-03-17 18:49:29 +01:00
Daniel Hougaard
eace4f1bdc Fix: Return access token 2024-03-17 18:49:29 +01:00
Daniel Hougaard
0bd3f32c6e Fix: Add missing actor org ID 2024-03-17 18:49:29 +01:00
Daniel Hougaard
ad0504e957 Fix: Add missing actor org ID 2024-03-17 18:49:29 +01:00
Daniel Hougaard
1e20d780ec Feat: Org scoped JWT's 2024-03-17 18:49:29 +01:00
Daniel Hougaard
7e2685d604 Fix: Better type checking 2024-03-17 18:49:29 +01:00
Daniel Hougaard
92fd2d080d Fix: ActorType unresolved 2024-03-17 18:49:29 +01:00
Daniel Hougaard
6d60413593 Feat: Org Scoped JWT's, service handler 2024-03-17 18:49:29 +01:00
Daniel Hougaard
f59a75d790 Feat: Org Scoped JWT's, remove inline service 2024-03-17 18:49:29 +01:00
Daniel Hougaard
835c36d161 Fix: Select org after creation 2024-03-17 18:49:29 +01:00
Daniel Hougaard
e4dba6d5c8 Fix: Formatting and support for selecting org (line 109-122) 2024-03-17 18:49:29 +01:00
Daniel Hougaard
b9986be387 Fix: Creating dummy workspaces 2024-03-17 18:49:29 +01:00
Daniel Hougaard
5f5d62a285 Fix: Selecting SAML enforced organization 2024-03-17 18:49:29 +01:00
Daniel Hougaard
667fa7a9e3 Chore: Optional 'invalidate' option for create org hook 2024-03-17 18:49:29 +01:00
Daniel Hougaard
27dcb06083 Fix: Invalidate after selecting organization 2024-03-17 18:49:29 +01:00
Daniel Hougaard
9b1a15331a Fix: Creating dummy workspaces 2024-03-17 18:49:29 +01:00
Daniel Hougaard
65776b7ab9 Feat: Scoped JWT to organization, actorAuthMethod to create project DTO 2024-03-17 18:49:29 +01:00
Daniel Hougaard
a9c1f278a1 Feat: Scoped JWT to organization, add actorAuthMethod to DTO's 2024-03-17 18:49:29 +01:00
Daniel Hougaard
900facdb36 Feat: Scoped JWT to organization, add actorAuthMethod to services 2024-03-17 18:49:29 +01:00
Daniel Hougaard
fe638ce2c1 Feat: Scoped JWT to organization, add actorAuthMethod to services 2024-03-17 18:49:29 +01:00
Daniel Hougaard
750a43c978 Feat: Scoped JWT to organization 2024-03-17 18:48:10 +01:00
Daniel Hougaard
08b5975f26 Chore: Move SAML org check to permission service 2024-03-17 18:48:10 +01:00
Daniel Hougaard
885d1fbd7f Feat: Scoped JWT to organization 2024-03-17 18:48:10 +01:00
Daniel Hougaard
bb2413d659 Update index.ts 2024-03-17 18:48:10 +01:00
Daniel Hougaard
dac5529b6c Feat: Scoped JWT to organization, require organization on all requests by default on JWT requests 2024-03-17 18:48:10 +01:00
Daniel Hougaard
bd92e35729 Feat: Scoped JWT to organization, add actorAuthMethod to Permission types 2024-03-17 18:48:10 +01:00
Daniel Hougaard
5b7562a76d Feat: Scoped JWT to organization, Add actorAuthMethod to DTO 2024-03-17 18:48:10 +01:00
Daniel Hougaard
edbf459d04 Feat: Scoped JWT to organization 2024-03-17 18:48:10 +01:00
Daniel Hougaard
560274bde8 Feat: Scoped JWT to organization, Add authMethod to services 2024-03-17 18:48:10 +01:00
Daniel Hougaard
7df614a018 Feat: Scoped JWT to organization, SAML helper functions 2024-03-17 18:48:10 +01:00
Daniel Hougaard
47287be5bf Feat: Scoped JWT to organization, add authMethod to request 2024-03-17 18:48:10 +01:00
Daniel Hougaard
6e96f2338c Feat: Scoped JWT to organization, include authMethod on all service calls 2024-03-17 18:48:10 +01:00
Daniel Hougaard
7fd6b63b5d Feat: Navigate to select org 2024-03-17 18:48:10 +01:00
Daniel Hougaard
995777d76f Formatting and navigating to select org 2024-03-17 18:48:10 +01:00
Daniel Hougaard
2a6032a8cf Navigate to select org instead of dashboard 2024-03-17 18:48:10 +01:00
Daniel Hougaard
ec4d1dd1b2 Update _app.tsx 2024-03-17 18:48:10 +01:00
Daniel Hougaard
143de12d67 Feat: Select organization on login 2024-03-17 18:48:10 +01:00
Daniel Hougaard
52cf937826 Fix: Avoid invalidating all queries on logout to prevent UI glitch 2024-03-17 18:48:10 +01:00
Daniel Hougaard
dbd7561037 Add link button 2024-03-17 18:48:10 +01:00
Daniel Hougaard
d287c3e152 Feat: Org Scoped JWT Tokens 2024-03-17 18:48:10 +01:00
Daniel Hougaard
8fc081973d Feat: Org Scoped JWT Tokens 2024-03-17 18:48:09 +01:00
Daniel Hougaard
c42bbbea8b Feat: Org Scoped JWT Tokens 2024-03-17 18:48:09 +01:00
Daniel Hougaard
29b2b12ec7 Feat: Org Scoped JWT Tokens 2024-03-17 18:48:09 +01:00
Daniel Hougaard
4f80234afa Feat: Org Scoped JWT Tokens 2024-03-17 18:48:09 +01:00
Daniel Hougaard
a1fa0c652d Feat: Org Scoped JWT Tokens 2024-03-17 18:48:09 +01:00
Daniel Hougaard
8327f41b8e Feat: Org Scoped JWT Tokens 2024-03-17 18:48:09 +01:00
Daniel Hougaard
c2bfeb89e8 Feat: Org Scoped JWT Tokens 2024-03-17 18:48:09 +01:00
Daniel Hougaard
4a0668e92e Feat: Org Scoped JWT Tokens 2024-03-17 18:48:09 +01:00
Daniel Hougaard
716e705c2a Fix: Removed legacy create project code 2024-03-17 18:48:09 +01:00
Daniel Hougaard
f860fd3abe Update project-types.ts 2024-03-17 18:48:09 +01:00
Daniel Hougaard
30e7fe8a45 Fix: Rebase errors 2024-03-17 18:48:09 +01:00
Daniel Hougaard
307b89e799 Update inject-identity.ts 2024-03-17 18:48:09 +01:00
Daniel Hougaard
dbf498b44a Fix: Remove orgId from service token 2024-03-17 18:48:09 +01:00
Daniel Hougaard
5eb3258311 Fix: Remove org ID from JWT 2024-03-17 18:48:09 +01:00
Daniel Hougaard
bd3cbb3c7b feat: fix project query by slug (now accepts an org ID) 2024-03-17 18:48:09 +01:00
Daniel Hougaard
96abbd9f80 feat: standardize org ID's on auth requests 2024-03-17 18:48:09 +01:00
Daniel Hougaard
92441e018f Slug projects and filter type 2024-03-17 18:48:09 +01:00
Daniel Hougaard
a9bba02f44 Draft 2024-03-17 18:48:09 +01:00
Daniel Hougaard
aaca3ac229 Fix: Change org ID to org slug 2024-03-17 18:48:09 +01:00
Daniel Hougaard
f0383dd55c Fix: Change org ID to org slug 2024-03-17 18:48:09 +01:00
Daniel Hougaard
a766329de5 Fix: Non-existant variable being passed to Posthog 2024-03-17 18:48:09 +01:00
Daniel Hougaard
c0b0c0754b Feat: List secrets by project slug 2024-03-17 18:48:09 +01:00
Daniel Hougaard
34618041ca Update inject-identity.ts 2024-03-17 18:48:09 +01:00
Daniel Hougaard
f36a056c62 Update inject-identity.ts 2024-03-17 18:48:09 +01:00
Daniel Hougaard
e7b11eac2b Fix: Remove orgId from service token 2024-03-17 18:48:09 +01:00
Daniel Hougaard
0f14fab915 Update index.ts 2024-03-17 18:48:09 +01:00
Daniel Hougaard
12a6fba645 Feat: Create project via org slug instead of org ID 2024-03-17 18:48:09 +01:00
Maidul Islam
ce057f44ac nit: update error message 2024-03-17 18:48:09 +01:00
Daniel Hougaard
2032063c24 Fix: Remove org ID from JWT 2024-03-17 18:48:09 +01:00
Daniel Hougaard
bbceb37d06 feat: fix project query by slug (now accepts an org ID) 2024-03-17 18:48:09 +01:00
Daniel Hougaard
e917b744f4 feat: standardize org ID's on auth requests 2024-03-17 18:48:09 +01:00
Daniel Hougaard
7438c114dd Remove API key auth mode 2024-03-17 18:48:09 +01:00
Daniel Hougaard
8e3fc044ca Slug projects and filter type 2024-03-17 18:48:09 +01:00
Daniel Hougaard
9473de2212 Draft 2024-03-17 18:48:09 +01:00
BlackMagiq
744c510a51 Merge pull request #1580 from Infisical/render-integration-auto-deploy
Render integration auto-redeployment capability on secret sync
2024-03-16 16:56:54 -07:00
Tuan Dang
f845749a4d Complete render integration auto-redeploy feature 2024-03-16 16:08:59 -07:00
BlackMagiq
56fc5a2a8c Merge pull request #1398 from Salman2301/feat-github-integration
Github Integrations
2024-03-15 20:44:09 -07:00
Tuan Dang
7edebbabaa Update docs for github integration 2024-03-15 20:38:02 -07:00
Tuan Dang
0e698e9355 Clean GitHub integration 2024-03-15 20:25:04 -07:00
Salman
ee215bccfa fix: notification error behind detail sidebar 2024-03-16 08:34:21 +05:30
Salman
00b99e7255 fix: remove purple banner screenshot 2024-03-16 08:14:29 +05:30
Salman
2b7784718d fix: disabled repo env until repo selected 2024-03-16 08:01:28 +05:30
Tuan Dang
5f99e58674 Update github integration org/envs support 2024-03-15 18:45:55 -07:00
Tuan Dang
f77942c702 Fix merge conflicts 2024-03-15 13:07:29 -07:00
vmatsiiako
2d3fddd0e9 Merge pull request #1570 from Infisical/daniel/api-endpoint-docs
Feat: Documentation improvements
2024-03-14 16:34:00 -07:00
Daniel Hougaard
519b92d592 Update mint.json 2024-03-14 19:05:34 +01:00
BlackMagiq
c3d5e882f8 Merge pull request #1571 from Infisical/aws-ps-integration
Remove recursive behavior in AWS PS integration
2024-03-14 10:36:16 -07:00
Tuan Dang
4c354eb3ea Remove recursive behavior in AWS PS integration 2024-03-14 10:32:05 -07:00
Daniel Hougaard
97eff2b480 Fix: Moved categories and renamed Libs -> SDKs 2024-03-14 18:04:27 +01:00
Tuan Dang
c621592807 Add .pem loader to tsup 2024-03-14 09:31:58 -07:00
BlackMagiq
bd400a6196 Merge pull request #1569 from Infisical/render-integration
Add pagination to getAppsRender
2024-03-14 09:30:42 -07:00
Tuan Dang
a93c2d9236 Add pagination to getAppsRender 2024-03-14 09:24:04 -07:00
Daniel Hougaard
11dfeda501 Fix: No nested groups 2024-03-14 17:13:18 +01:00
Daniel Hougaard
70bd64d54b Fix: Shorter sidebar titles 2024-03-14 17:09:45 +01:00
Daniel Hougaard
0c88a5466c Feat: Documentation improvements 2024-03-14 17:09:37 +01:00
Daniel Hougaard
36266b30d5 Fix: Shorter sidebar title 2024-03-14 17:09:21 +01:00
Daniel Hougaard
288577b455 Fix: OpenAPI specification URL 2024-03-14 15:41:32 +01:00
Daniel Hougaard
5194be14fd Update mint.json 2024-03-14 15:40:53 +01:00
Daniel Hougaard
bab8f95fde Feat: Added descriptions to all public API endpoints 2024-03-14 15:40:38 +01:00
Daniel Hougaard
b4f372f883 Fix: Delete folder docs page not rendering 2024-03-14 15:40:08 +01:00
Daniel Hougaard
b13365ecf5 Feat: Written API explanations 2024-03-14 15:39:29 +01:00
BlackMagiq
bb6e09a895 Merge pull request #1568 from Infisical/license-docs
Add simple docs for Infisical EE
2024-03-13 18:07:46 -07:00
Tuan Dang
715b193a8e Add simple docs for Infisical EE 2024-03-13 18:05:13 -07:00
BlackMagiq
57be493da8 Merge pull request #1567 from Infisical/offline-license
Add EE license support for air-gapped / offline environments
2024-03-13 17:25:35 -07:00
Tuan Dang
cc731fe031 Add EE license support for air-gapped / offline environments 2024-03-13 17:18:55 -07:00
Daniel Hougaard
70618420d7 Merge pull request #1565 from Infisical/daniel/deprecate-service-tokens-and-api-keys
Feat: Deprecate API keys
2024-03-13 17:58:05 +01:00
Daniel Hougaard
7feb7ef9c6 Fix: Change title 2024-03-13 17:52:24 +01:00
Daniel Hougaard
ab1b9fb164 Feat: Deprecate API keys 2024-03-13 17:46:48 +01:00
Daniel Hougaard
8c028889a6 Chore: Remove unused frontend V2 API key code 2024-03-13 17:45:19 +01:00
Akhil Mohan
7dc366baf0 Merge pull request #1562 from rhythmbhiwani/fix-typo
Fix typo on Copy button tooltip
2024-03-13 16:28:42 +05:30
Akhil Mohan
2124d2669f Merge pull request #1564 from myapos/bugfix_1563_remove_member
fix issue on removing member from project
2024-03-13 16:24:18 +05:30
myapos
af83fbea14 fix issue on removing member from project 2024-03-13 12:32:11 +02:00
Rhythm Bhiwani
9657b64ab2 Fix typo on Copy button tooltip 2024-03-13 15:54:42 +05:30
Salman
6b92a5f4db Update delete key set to org name for github org integration 2024-02-20 04:28:59 +05:30
Salman
81e961e8bc Update remove group field for github integration section 2024-02-19 17:47:23 +05:30
Salman
6a7a6ce942 Update github integrations docs based on diff scope 2024-02-19 11:39:08 +05:30
Salman
1695412278 Update integration-sync-secret for org and env 2024-02-16 04:46:41 +05:30
Salman
b4fa07334d Update use repo id to avoid ambiguous repo name 2024-02-16 02:53:39 +05:30
Salman
29c244c635 Update repo name display owner/repo instead just the repo 2024-02-16 02:13:16 +05:30
Salman
b80a5989a8 Fix reset env on repo change 2024-02-16 00:58:10 +05:30
Salman
dc696f8932 Update integration section for github repo, org and env 2024-02-15 15:59:18 +05:30
Salman
c8f0796952 Update github integrations ui for organization and environment 2024-02-15 09:41:57 +05:30
Salman
9282dd08d9 Merge remote-tracking branch 'origin/main' into feat-github-integration 2024-02-15 00:00:05 +05:30
Salman
df459d456a Update github form ui add scope and init org api 2024-02-14 04:21:50 +05:30
Salman
c8cfb43316 Update github integration refactored to react-hook-form 2024-02-13 05:42:29 +05:30
372 changed files with 5924 additions and 4897 deletions

View File

@@ -8,6 +8,15 @@ jobs:
steps:
- name: ☁️ Checkout source
uses: actions/checkout@v3
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID_FOR_ECR }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_FOR_ECR }}
aws-region: us-east-1
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
- name: 📦 Install dependencies to test all dependencies
run: npm ci --only-production
working-directory: backend
@@ -35,16 +44,7 @@ jobs:
context: .
file: Dockerfile.standalone-infisical
tags: infisical/infisical:test
# - name: ⏻ Spawn backend container and dependencies
# run: |
# docker compose -f .github/resources/docker-compose.be-test.yml up --wait --quiet-pull
# - name: 🧪 Test backend image
# run: |
# ./.github/resources/healthcheck.sh infisical-backend-test
# - name: ⏻ Shut down backend container and dependencies
# run: |
# docker compose -f .github/resources/docker-compose.be-test.yml down
- name: 🏗️ Build backend and push
- name: 🏗️ Build backend and push to docker hub
uses: depot/build-push-action@v1
with:
project: 64mmf0n610
@@ -59,6 +59,8 @@ jobs:
build-args: |
POSTHOG_API_KEY=${{ secrets.PUBLIC_POSTHOG_API_KEY }}
INFISICAL_PLATFORM_VERSION=${{ steps.extract_version.outputs.version }}
postgres-migration:
name: Run latest migration files
runs-on: ubuntu-latest

View File

@@ -118,9 +118,6 @@ WORKDIR /backend
ENV TELEMETRY_ENABLED true
HEALTHCHECK --interval=10s --timeout=3s --start-period=10s \
CMD node healthcheck.js
EXPOSE 8080
EXPOSE 443

View File

@@ -10,7 +10,8 @@
<a href="https://infisical.com/">Infisical Cloud</a> |
<a href="https://infisical.com/docs/self-hosting/overview">Self-Hosting</a> |
<a href="https://infisical.com/docs/documentation/getting-started/introduction">Docs</a> |
<a href="https://www.infisical.com">Website</a>
<a href="https://www.infisical.com">Website</a> |
<a href="https://infisical.com/careers">Hiring (Remote/SF)</a>
</h4>
<p align="center">

View File

@@ -19,7 +19,7 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
.min(1)
.trim()
.refine(
(val) => Object.keys(OrgMembershipRole).includes(val),
(val) => !Object.keys(OrgMembershipRole).includes(val),
"Please choose a different slug, the slug you have entered is reserved"
)
.refine((v) => slugify(v) === v, {

View File

@@ -2,6 +2,7 @@ import { z } from "zod";
import { AuditLogsSchema, SecretSnapshotsSchema } from "@app/db/schemas";
import { EventType, UserAgentType } from "@app/ee/services/audit-log/audit-log-types";
import { AUDIT_LOGS, PROJECTS } from "@app/lib/api-docs";
import { removeTrailingSlash } from "@app/lib/fn";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
@@ -19,13 +20,13 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
}
],
params: z.object({
workspaceId: z.string().trim()
workspaceId: z.string().trim().describe(PROJECTS.GET_SNAPSHOTS.workspaceId)
}),
querystring: z.object({
environment: z.string().trim(),
path: z.string().trim().default("/").transform(removeTrailingSlash),
offset: z.coerce.number().default(0),
limit: z.coerce.number().default(20)
environment: z.string().trim().describe(PROJECTS.GET_SNAPSHOTS.environment),
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(PROJECTS.GET_SNAPSHOTS.path),
offset: z.coerce.number().default(0).describe(PROJECTS.GET_SNAPSHOTS.offset),
limit: z.coerce.number().default(20).describe(PROJECTS.GET_SNAPSHOTS.limit)
}),
response: {
200: z.object({
@@ -91,16 +92,16 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
}
],
params: z.object({
workspaceId: z.string().trim()
workspaceId: z.string().trim().describe(AUDIT_LOGS.EXPORT.workspaceId)
}),
querystring: z.object({
eventType: z.nativeEnum(EventType).optional(),
userAgentType: z.nativeEnum(UserAgentType).optional(),
startDate: z.string().datetime().optional(),
endDate: z.string().datetime().optional(),
offset: z.coerce.number().default(0),
limit: z.coerce.number().default(20),
actor: z.string().optional()
eventType: z.nativeEnum(EventType).optional().describe(AUDIT_LOGS.EXPORT.eventType),
userAgentType: z.nativeEnum(UserAgentType).optional().describe(AUDIT_LOGS.EXPORT.userAgentType),
startDate: z.string().datetime().optional().describe(AUDIT_LOGS.EXPORT.startDate),
endDate: z.string().datetime().optional().describe(AUDIT_LOGS.EXPORT.endDate),
offset: z.coerce.number().default(0).describe(AUDIT_LOGS.EXPORT.offset),
limit: z.coerce.number().default(20).describe(AUDIT_LOGS.EXPORT.limit),
actor: z.string().optional().describe(AUDIT_LOGS.EXPORT.actor)
}),
response: {
200: z.object({

View File

@@ -1,6 +1,7 @@
import { z } from "zod";
import { SecretSnapshotsSchema, SecretTagsSchema, SecretVersionsSchema } from "@app/db/schemas";
import { PROJECTS } from "@app/lib/api-docs";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
@@ -66,7 +67,7 @@ export const registerSnapshotRouter = async (server: FastifyZodProvider) => {
}
],
params: z.object({
secretSnapshotId: z.string().trim()
secretSnapshotId: z.string().trim().describe(PROJECTS.ROLLBACK_TO_SNAPSHOT.secretSnapshotId)
}),
response: {
200: z.object({

View File

@@ -8,6 +8,7 @@ import { ForbiddenError } from "@casl/ability";
import { TKeyStoreFactory } from "@app/keystore/keystore";
import { getConfig } from "@app/lib/config/env";
import { verifyOfflineLicense } from "@app/lib/crypto";
import { BadRequestError } from "@app/lib/errors";
import { logger } from "@app/lib/logger";
import { TOrgDALFactory } from "@app/services/org/org-dal";
@@ -26,6 +27,7 @@ import {
TFeatureSet,
TGetOrgBillInfoDTO,
TGetOrgTaxIdDTO,
TOfflineLicenseContents,
TOrgInvoiceDTO,
TOrgLicensesDTO,
TOrgPlanDTO,
@@ -96,6 +98,36 @@ export const licenseServiceFactory = ({
}
return;
}
if (appCfg.LICENSE_KEY_OFFLINE) {
let isValidOfflineLicense = true;
const contents: TOfflineLicenseContents = JSON.parse(
Buffer.from(appCfg.LICENSE_KEY_OFFLINE, "base64").toString("utf8")
);
const isVerified = await verifyOfflineLicense(JSON.stringify(contents.license), contents.signature);
if (!isVerified) {
isValidOfflineLicense = false;
logger.warn(`Infisical EE offline license verification failed`);
}
if (contents.license.terminatesAt) {
const terminationDate = new Date(contents.license.terminatesAt);
if (terminationDate < new Date()) {
isValidOfflineLicense = false;
logger.warn(`Infisical EE offline license has expired`);
}
}
if (isValidOfflineLicense) {
onPremFeatures = contents.license.features;
instanceType = InstanceType.EnterpriseOnPrem;
logger.info(`Instance type: ${InstanceType.EnterpriseOnPrem}`);
isValidLicense = true;
return;
}
}
// this means this is self hosted oss version
// else it would reach catch statement
isValidLicense = true;

View File

@@ -6,6 +6,21 @@ export enum InstanceType {
Cloud = "cloud"
}
export type TOfflineLicenseContents = {
license: TOfflineLicense;
signature: string;
};
export type TOfflineLicense = {
issuedTo: string;
licenseId: string;
customerId: string | null;
issuedAt: string;
expiresAt: string | null;
terminatesAt: string | null;
features: TFeatureSet;
};
export type TFeatureSet = {
_id: null;
slug: null;

View File

@@ -9,6 +9,7 @@ import jmespath from "jmespath";
import knex from "knex";
import { getConfig } from "@app/lib/config/env";
import { getDbConnectionHost } from "@app/lib/knex";
import { alphaNumericNanoId } from "@app/lib/nanoid";
import { TAssignOp, TDbProviderClients, TDirectAssignOp, THttpProviderFunction } from "../templates/types";
@@ -89,7 +90,7 @@ export const secretRotationDbFn = async ({
const appCfg = getConfig();
const ssl = ca ? { rejectUnauthorized: false, ca } : undefined;
if (host === "localhost" || host === "127.0.0.1" || appCfg.DB_CONNECTION_URI.includes(host))
if (host === "localhost" || host === "127.0.0.1" || getDbConnectionHost(appCfg.DB_CONNECTION_URI) === host)
throw new Error("Invalid db host");
const db = knex({

View File

@@ -0,0 +1,287 @@
export const IDENTITIES = {
CREATE: {
name: "The name of the identity to create.",
organizationId: "The organization ID to which the identity belongs.",
role: "The role of the identity. Possible values are 'no-access', 'member', and 'admin'."
},
UPDATE: {
identityId: "The ID of the identity to update.",
name: "The new name of the identity.",
role: "The new role of the identity."
},
DELETE: {
identityId: "The ID of the identity to delete."
}
} as const;
export const UNIVERSAL_AUTH = {
LOGIN: {
clientId: "Your Machine Identity Client ID.",
clientSecret: "Your Machine Identity Client Secret."
},
ATTACH: {
identityId: "The ID of the identity to attach the configuration onto.",
clientSecretTrustedIps:
"A list of IPs or CIDR ranges that the Client Secret can be used from together with the Client ID to get back an access token. You can use 0.0.0.0/0, to allow usage from any network address.",
accessTokenTrustedIps:
"A list of IPs or CIDR ranges that access tokens can be used from. You can use 0.0.0.0/0, to allow usage from any network address.",
accessTokenTTL: "The lifetime for an access token in seconds. This value will be referenced at renewal time.",
accessTokenMaxTTL:
"The maximum lifetime for an access token in seconds. This value will be referenced at renewal time.",
accessTokenNumUsesLimit:
"The maximum number of times that an access token can be used; a value of 0 implies infinite number of uses."
},
RETRIEVE: {
identityId: "The ID of the identity to retrieve."
},
UPDATE: {
identityId: "The ID of the identity to update.",
clientSecretTrustedIps: "The new list of IPs or CIDR ranges that the Client Secret can be used from.",
accessTokenTrustedIps: "The new list of IPs or CIDR ranges that access tokens can be used from.",
accessTokenTTL: "The new lifetime for an access token in seconds.",
accessTokenMaxTTL: "The new maximum lifetime for an access token in seconds.",
accessTokenNumUsesLimit: "The new maximum number of times that an access token can be used."
},
CREATE_CLIENT_SECRET: {
identityId: "The ID of the identity to create a client secret for.",
description: "The description of the client secret.",
numUsesLimit:
"The maximum number of times that the client secret can be used; a value of 0 implies infinite number of uses.",
ttl: "The lifetime for the client secret in seconds."
},
LIST_CLIENT_SECRETS: {
identityId: "The ID of the identity to list client secrets for."
},
REVOKE_CLIENT_SECRET: {
identityId: "The ID of the identity to revoke the client secret from.",
clientSecretId: "The ID of the client secret to revoke."
},
RENEW_ACCESS_TOKEN: {
accessToken: "The access token to renew."
}
} as const;
export const ORGANIZATIONS = {
LIST_USER_MEMBERSHIPS: {
organizationId: "The ID of the organization to get memberships from."
},
UPDATE_USER_MEMBERSHIP: {
organizationId: "The ID of the organization to update the membership for.",
membershipId: "The ID of the membership to update.",
role: "The new role of the membership."
},
DELETE_USER_MEMBERSHIP: {
organizationId: "The ID of the organization to delete the membership from.",
membershipId: "The ID of the membership to delete."
},
LIST_IDENTITY_MEMBERSHIPS: {
orgId: "The ID of the organization to get identity memberships from."
},
GET_PROJECTS: {
organizationId: "The ID of the organization to get projects from."
}
} as const;
export const PROJECTS = {
CREATE: {
organizationSlug: "The slug of the organization to create the project in.",
projectName: "The name of the project to create.",
slug: "An optional slug for the project."
},
DELETE: {
workspaceId: "The ID of the project to delete."
},
GET: {
workspaceId: "The ID of the project."
},
UPDATE: {
workspaceId: "The ID of the project to update.",
name: "The new name of the project.",
autoCapitalization: "Disable or enable auto-capitalization for the project."
},
INVITE_MEMBER: {
projectId: "The ID of the project to invite the member to.",
emails: "A list of organization member emails to invite to the project.",
usernames: "A list of usernames to invite to the project."
},
REMOVE_MEMBER: {
projectId: "The ID of the project to remove the member from.",
emails: "A list of organization member emails to remove from the project.",
usernames: "A list of usernames to remove from the project."
},
GET_USER_MEMBERSHIPS: {
workspaceId: "The ID of the project to get memberships from."
},
UPDATE_USER_MEMBERSHIP: {
workspaceId: "The ID of the project to update the membership for.",
membershipId: "The ID of the membership to update.",
roles: "A list of roles to update the membership to."
},
LIST_IDENTITY_MEMBERSHIPS: {
projectId: "The ID of the project to get identity memberships from."
},
UPDATE_IDENTITY_MEMBERSHIP: {
projectId: "The ID of the project to update the identity membership for.",
identityId: "The ID of the identity to update the membership for.",
roles: "A list of roles to update the membership to."
},
DELETE_IDENTITY_MEMBERSHIP: {
projectId: "The ID of the project to delete the identity membership from.",
identityId: "The ID of the identity to delete the membership from."
},
GET_KEY: {
workspaceId: "The ID of the project to get the key from."
},
GET_SNAPSHOTS: {
workspaceId: "The ID of the project to get snapshots from.",
environment: "The environment to get snapshots from.",
path: "The secret path to get snapshots from.",
offset: "The offset to start from. If you enter 10, it will start from the 10th snapshot.",
limit: "The number of snapshots to return."
},
ROLLBACK_TO_SNAPSHOT: {
secretSnapshotId: "The ID of the snapshot to rollback to."
}
} as const;
export const ENVIRONMENTS = {
CREATE: {
workspaceId: "The ID of the project to create the environment in.",
name: "The name of the environment to create.",
slug: "The slug of the environment to create."
},
UPDATE: {
workspaceId: "The ID of the project to update the environment in.",
id: "The ID of the environment to update.",
name: "The new name of the environment.",
slug: "The new slug of the environment.",
position: "The new position of the environment. The lowest number will be displayed as the first environment."
},
DELETE: {
workspaceId: "The ID of the project to delete the environment from.",
id: "The ID of the environment to delete."
}
} as const;
export const FOLDERS = {
LIST: {
workspaceId: "The ID of the project to list folders from.",
environment: "The slug of the environment to list folders from.",
path: "The path to list folders from.",
directory: "The directory to list folders from. (Deprecated in favor of path)"
},
CREATE: {
workspaceId: "The ID of the project to create the folder in.",
environment: "The slug of the environment to create the folder in.",
name: "The name of the folder to create.",
path: "The path of the folder to create.",
directory: "The directory of the folder to create. (Deprecated in favor of path)"
},
UPDATE: {
folderId: "The ID of the folder to update.",
environment: "The slug of the environment where the folder is located.",
name: "The new name of the folder.",
path: "The path of the folder to update.",
directory: "The new directory of the folder to update. (Deprecated in favor of path)",
workspaceId: "The ID of the project where the folder is located."
},
DELETE: {
folderIdOrName: "The ID or name of the folder to delete.",
workspaceId: "The ID of the project to delete the folder from.",
environment: "The slug of the environment where the folder is located.",
directory: "The directory of the folder to delete. (Deprecated in favor of path)",
path: "The path of the folder to delete."
}
} as const;
export const RAW_SECRETS = {
LIST: {
workspaceId: "The ID of the project to list secrets from.",
workspaceSlug: "The slug of the project to list secrets from. This parameter is only usable by machine identities.",
environment: "The slug of the environment to list secrets from.",
secretPath: "The secret path to list secrets from.",
includeImports: "Weather to include imported secrets or not."
},
CREATE: {
secretName: "The name of the secret to create.",
environment: "The slug of the environment to create the secret in.",
secretComment: "Attach a comment to the secret.",
secretPath: "The path to create the secret in.",
secretValue: "The value of the secret to create.",
skipMultilineEncoding: "Skip multiline encoding for the secret value.",
type: "The type of the secret to create.",
workspaceId: "The ID of the project to create the secret in."
},
GET: {
secretName: "The name of the secret to get.",
workspaceId: "The ID of the project to get the secret from.",
environment: "The slug of the environment to get the secret from.",
secretPath: "The path of the secret to get.",
version: "The version of the secret to get.",
type: "The type of the secret to get.",
includeImports: "Weather to include imported secrets or not."
},
UPDATE: {
secretName: "The name of the secret to update.",
environment: "The slug of the environment where the secret is located.",
secretPath: "The path of the secret to update",
secretValue: "The new value of the secret.",
skipMultilineEncoding: "Skip multiline encoding for the secret value.",
type: "The type of the secret to update.",
workspaceId: "The ID of the project to update the secret in."
},
DELETE: {
secretName: "The name of the secret to delete.",
environment: "The slug of the environment where the secret is located.",
secretPath: "The path of the secret.",
type: "The type of the secret to delete.",
workspaceId: "The ID of the project where the secret is located."
}
} as const;
export const SECRET_IMPORTS = {
LIST: {
workspaceId: "The ID of the project to list secret imports from.",
environment: "The slug of the environment to list secret imports from.",
path: "The path to list secret imports from."
},
CREATE: {
environment: "The slug of the environment to import into.",
path: "The path to import into.",
workspaceId: "The ID of the project you are working in.",
import: {
environment: "The slug of the environment to import from.",
path: "The path to import from."
}
},
UPDATE: {
secretImportId: "The ID of the secret import to update.",
environment: "The slug of the environment where the secret import is located.",
import: {
environment: "The new environment slug to import from.",
path: "The new path to import from.",
position: "The new position of the secret import. The lowest number will be displayed as the first import."
},
path: "The path of the secret import to update.",
workspaceId: "The ID of the project where the secret import is located."
},
DELETE: {
workspaceId: "The ID of the project to delete the secret import from.",
secretImportId: "The ID of the secret import to delete.",
environment: "The slug of the environment where the secret import is located.",
path: "The path of the secret import to delete."
}
} as const;
export const AUDIT_LOGS = {
EXPORT: {
workspaceId: "The ID of the project to export audit logs from.",
eventType: "The type of the event to export.",
userAgentType: "Choose which consuming application to export audit logs for.",
startDate: "The date to start the export from.",
endDate: "The date to end the export at.",
offset: "The offset to start from. If you enter 10, it will start from the 10th audit log.",
limit: "The number of audit logs to return.",
actor: "The actor to filter the audit logs by."
}
} as const;

View File

@@ -0,0 +1 @@
export * from "./constants";

View File

@@ -106,6 +106,7 @@ const envSchema = z
LICENSE_SERVER_URL: zpStr(z.string().optional().default("https://portal.infisical.com")),
LICENSE_SERVER_KEY: zpStr(z.string().optional()),
LICENSE_KEY: zpStr(z.string().optional()),
LICENSE_KEY_OFFLINE: zpStr(z.string().optional()),
// GENERIC
STANDALONE_MODE: z

View File

@@ -17,4 +17,5 @@ export {
decryptSecrets,
decryptSecretVersions
} from "./secret-encryption";
export { verifyOfflineLicense } from "./signing";
export { generateSrpServerKey, srpCheckClientProof } from "./srp";

View File

@@ -0,0 +1,8 @@
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEApchBY3BXTu4zWGBguB7nM/pjpVLY3V7VGZOAxmR5ueQTJOwiGM13
5HN3EM9fDlQnZu9VSc0OFqRM/bUeUaI1oLPE6WzTHjdHyKjDI/S+TLx3VGEsvhM1
uukZpYX+3KX2w4wzRHBaBWyglFy0CVNth9UJhhpD+KKfv7dzcRmsbyoUWi9wGfJu
wLYCwaCwZRXIt1sLGmMncPz14vfwdnm2a5Tj1Jbt0GTyBl+1/ZqLbO6SsslLg2G+
o7FfGS9z8OUTkvDdu16qxL+p2wCEFZMnOz5BB4oakuT2gS9iOO2l5AOPcT4WzPzy
PYbX3d7cN9BkOY9I5z0cX4wzqHjQTvGNLQIDAQAB
-----END RSA PUBLIC KEY-----

View File

@@ -0,0 +1,22 @@
import crypto, { KeyObject } from "crypto";
import fs from "fs/promises";
import path from "path";
export const verifySignature = (data: string, signature: Buffer, publicKey: KeyObject) => {
const verify = crypto.createVerify("SHA256");
verify.update(data);
verify.end();
return verify.verify(publicKey, signature);
};
export const verifyOfflineLicense = async (licenseContents: string, signature: string) => {
const publicKeyPem = await fs.readFile(path.join(__dirname, "license_public_key.pem"), "utf8");
const publicKey = crypto.createPublicKey({
key: publicKeyPem,
format: "pem",
type: "pkcs1"
});
return verifySignature(licenseContents, Buffer.from(signature, "base64"), publicKey);
};

View File

@@ -0,0 +1,11 @@
import { URL } from "url"; // Import the URL class
export const getDbConnectionHost = (urlString: string) => {
try {
const url = new URL(urlString);
// Split hostname and port (if provided)
return url.hostname.split(":")[0];
} catch (error) {
return null;
}
};

View File

@@ -4,6 +4,7 @@ import { Tables } from "knex/types/tables";
import { DatabaseError } from "../errors";
export * from "./connection";
export * from "./join";
export * from "./select";

View File

@@ -14,13 +14,13 @@ export const fastifySwagger = fp(async (fastify) => {
version: "0.0.1"
},
servers: [
{
url: "http://localhost:8080",
description: "Local server"
},
{
url: "https://app.infisical.com",
description: "Production server"
},
{
url: "http://localhost:8080",
description: "Local server"
}
],
components: {

View File

@@ -1,5 +1,7 @@
import { z } from "zod";
import { UNIVERSAL_AUTH } from "@app/lib/api-docs";
export const registerIdentityAccessTokenRouter = async (server: FastifyZodProvider) => {
server.route({
url: "/token/renew",
@@ -7,7 +9,7 @@ export const registerIdentityAccessTokenRouter = async (server: FastifyZodProvid
schema: {
description: "Renew access token",
body: z.object({
accessToken: z.string().trim()
accessToken: z.string().trim().describe(UNIVERSAL_AUTH.RENEW_ACCESS_TOKEN.accessToken)
}),
response: {
200: z.object({

View File

@@ -2,6 +2,7 @@ import { z } from "zod";
import { IdentitiesSchema, OrgMembershipRole } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { IDENTITIES } from "@app/lib/api-docs";
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
@@ -20,9 +21,9 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
}
],
body: z.object({
name: z.string().trim(),
organizationId: z.string().trim(),
role: z.string().trim().min(1).default(OrgMembershipRole.NoAccess)
name: z.string().trim().describe(IDENTITIES.CREATE.name),
organizationId: z.string().trim().describe(IDENTITIES.CREATE.organizationId),
role: z.string().trim().min(1).default(OrgMembershipRole.NoAccess).describe(IDENTITIES.CREATE.role)
}),
response: {
200: z.object({
@@ -79,11 +80,11 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
}
],
params: z.object({
identityId: z.string()
identityId: z.string().describe(IDENTITIES.UPDATE.identityId)
}),
body: z.object({
name: z.string().trim().optional(),
role: z.string().trim().min(1).optional()
name: z.string().trim().optional().describe(IDENTITIES.UPDATE.name),
role: z.string().trim().min(1).optional().describe(IDENTITIES.UPDATE.role)
}),
response: {
200: z.object({
@@ -129,7 +130,7 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
}
],
params: z.object({
identityId: z.string()
identityId: z.string().describe(IDENTITIES.DELETE.identityId)
}),
response: {
200: z.object({

View File

@@ -2,6 +2,7 @@ import { z } from "zod";
import { IdentityUaClientSecretsSchema, IdentityUniversalAuthsSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { UNIVERSAL_AUTH } from "@app/lib/api-docs";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
@@ -26,8 +27,8 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
schema: {
description: "Login with Universal Auth",
body: z.object({
clientId: z.string().trim(),
clientSecret: z.string().trim()
clientId: z.string().trim().describe(UNIVERSAL_AUTH.LOGIN.clientId),
clientSecret: z.string().trim().describe(UNIVERSAL_AUTH.LOGIN.clientSecret)
}),
response: {
200: z.object({
@@ -76,7 +77,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
}
],
params: z.object({
identityId: z.string().trim()
identityId: z.string().trim().describe(UNIVERSAL_AUTH.ATTACH.identityId)
}),
body: z.object({
clientSecretTrustedIps: z
@@ -85,14 +86,16 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
})
.array()
.min(1)
.default([{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }]),
.default([{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }])
.describe(UNIVERSAL_AUTH.ATTACH.clientSecretTrustedIps),
accessTokenTrustedIps: z
.object({
ipAddress: z.string().trim()
})
.array()
.min(1)
.default([{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }]),
.default([{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }])
.describe(UNIVERSAL_AUTH.ATTACH.accessTokenTrustedIps),
accessTokenTTL: z
.number()
.int()
@@ -100,15 +103,22 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
.refine((value) => value !== 0, {
message: "accessTokenTTL must have a non zero number"
})
.default(2592000),
.default(2592000)
.describe(UNIVERSAL_AUTH.ATTACH.accessTokenTTL), // 30 days
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)
.default(2592000)
.describe(UNIVERSAL_AUTH.ATTACH.accessTokenMaxTTL), // 30 days
accessTokenNumUsesLimit: z
.number()
.int()
.min(0)
.default(0)
.describe(UNIVERSAL_AUTH.ATTACH.accessTokenNumUsesLimit)
}),
response: {
200: z.object({
@@ -157,7 +167,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
}
],
params: z.object({
identityId: z.string()
identityId: z.string().describe(UNIVERSAL_AUTH.UPDATE.identityId)
}),
body: z.object({
clientSecretTrustedIps: z
@@ -166,16 +176,23 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
})
.array()
.min(1)
.optional(),
.optional()
.describe(UNIVERSAL_AUTH.UPDATE.clientSecretTrustedIps),
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(),
.optional()
.describe(UNIVERSAL_AUTH.UPDATE.accessTokenTrustedIps),
accessTokenTTL: z.number().int().min(0).optional().describe(UNIVERSAL_AUTH.UPDATE.accessTokenTTL),
accessTokenNumUsesLimit: z
.number()
.int()
.min(0)
.optional()
.describe(UNIVERSAL_AUTH.UPDATE.accessTokenNumUsesLimit),
accessTokenMaxTTL: z
.number()
.int()
@@ -183,6 +200,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
message: "accessTokenMaxTTL must have a non zero number"
})
.optional()
.describe(UNIVERSAL_AUTH.UPDATE.accessTokenMaxTTL)
}),
response: {
200: z.object({
@@ -232,7 +250,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
}
],
params: z.object({
identityId: z.string()
identityId: z.string().describe(UNIVERSAL_AUTH.RETRIEVE.identityId)
}),
response: {
200: z.object({
@@ -276,12 +294,12 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
}
],
params: z.object({
identityId: z.string()
identityId: z.string().describe(UNIVERSAL_AUTH.CREATE_CLIENT_SECRET.identityId)
}),
body: z.object({
description: z.string().trim().default(""),
numUsesLimit: z.number().min(0).default(0),
ttl: z.number().min(0).default(0)
description: z.string().trim().default("").describe(UNIVERSAL_AUTH.CREATE_CLIENT_SECRET.description),
numUsesLimit: z.number().min(0).default(0).describe(UNIVERSAL_AUTH.CREATE_CLIENT_SECRET.numUsesLimit),
ttl: z.number().min(0).default(0).describe(UNIVERSAL_AUTH.CREATE_CLIENT_SECRET.ttl)
}),
response: {
200: z.object({
@@ -328,7 +346,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
}
],
params: z.object({
identityId: z.string()
identityId: z.string().describe(UNIVERSAL_AUTH.LIST_CLIENT_SECRETS.identityId)
}),
response: {
200: z.object({
@@ -371,8 +389,8 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
}
],
params: z.object({
identityId: z.string(),
clientSecretId: z.string()
identityId: z.string().describe(UNIVERSAL_AUTH.REVOKE_CLIENT_SECRET.identityId),
clientSecretId: z.string().describe(UNIVERSAL_AUTH.REVOKE_CLIENT_SECRET.clientSecretId)
}),
response: {
200: z.object({

View File

@@ -352,6 +352,68 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
}
});
server.route({
url: "/:integrationAuthId/github/orgs",
method: "GET",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
params: z.object({
integrationAuthId: z.string().trim()
}),
response: {
200: z.object({
orgs: z.object({ name: z.string(), orgId: z.string() }).array()
})
}
},
handler: async (req) => {
const orgs = await server.services.integrationAuth.getGithubOrgs({
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
id: req.params.integrationAuthId
});
if (!orgs) throw new Error("No organization found.");
return { orgs };
}
});
server.route({
url: "/:integrationAuthId/github/envs",
method: "GET",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
params: z.object({
integrationAuthId: z.string().trim()
}),
querystring: z.object({
repoOwner: z.string().trim(),
repoName: z.string().trim()
}),
response: {
200: z.object({
envs: z.object({ name: z.string(), envId: z.string() }).array()
})
}
},
handler: async (req) => {
const envs = await server.services.integrationAuth.getGithubEnvs({
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId,
actorAuthMethod: req.permission.authMethod,
repoName: req.query.repoName,
repoOwner: req.query.repoOwner
});
if (!envs) throw new Error("No organization found.");
return { envs };
}
});
server.route({
url: "/:integrationAuthId/qovery/orgs",
method: "GET",

View File

@@ -33,6 +33,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
secretPrefix: z.string().optional(),
secretSuffix: z.string().optional(),
initialSyncBehavior: z.string().optional(),
shouldAutoRedeploy: z.boolean().optional(),
secretGCPLabel: z
.object({
labelName: z.string(),

View File

@@ -2,6 +2,7 @@ import { z } from "zod";
import { ProjectEnvironmentsSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { ENVIRONMENTS } from "@app/lib/api-docs";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
@@ -18,11 +19,11 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
}
],
params: z.object({
workspaceId: z.string().trim()
workspaceId: z.string().trim().describe(ENVIRONMENTS.CREATE.workspaceId)
}),
body: z.object({
name: z.string().trim(),
slug: z.string().trim()
name: z.string().trim().describe(ENVIRONMENTS.CREATE.name),
slug: z.string().trim().describe(ENVIRONMENTS.CREATE.slug)
}),
response: {
200: z.object({
@@ -74,13 +75,13 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
}
],
params: z.object({
workspaceId: z.string().trim(),
id: z.string().trim()
workspaceId: z.string().trim().describe(ENVIRONMENTS.UPDATE.workspaceId),
id: z.string().trim().describe(ENVIRONMENTS.UPDATE.id)
}),
body: z.object({
slug: z.string().trim().optional(),
name: z.string().trim().optional(),
position: z.number().optional()
slug: z.string().trim().optional().describe(ENVIRONMENTS.UPDATE.slug),
name: z.string().trim().optional().describe(ENVIRONMENTS.UPDATE.name),
position: z.number().optional().describe(ENVIRONMENTS.UPDATE.position)
}),
response: {
200: z.object({
@@ -138,8 +139,8 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
}
],
params: z.object({
workspaceId: z.string().trim(),
id: z.string().trim()
workspaceId: z.string().trim().describe(ENVIRONMENTS.DELETE.workspaceId),
id: z.string().trim().describe(ENVIRONMENTS.DELETE.id)
}),
response: {
200: z.object({

View File

@@ -9,6 +9,7 @@ import {
UsersSchema
} from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { PROJECTS } from "@app/lib/api-docs";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
import { ProjectUserMembershipTemporaryMode } from "@app/services/project-membership/project-membership-types";
@@ -26,7 +27,7 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
}
],
params: z.object({
workspaceId: z.string().trim()
workspaceId: z.string().trim().describe(PROJECTS.GET_USER_MEMBERSHIPS.workspaceId)
}),
response: {
200: z.object({
@@ -136,8 +137,8 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
}
],
params: z.object({
workspaceId: z.string().trim(),
membershipId: z.string().trim()
workspaceId: z.string().trim().describe(PROJECTS.UPDATE_USER_MEMBERSHIP.workspaceId),
membershipId: z.string().trim().describe(PROJECTS.UPDATE_USER_MEMBERSHIP.membershipId)
}),
body: z.object({
roles: z
@@ -158,6 +159,7 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
)
.min(1)
.refine((data) => data.some(({ isTemporary }) => !isTemporary), "At least long lived role is required")
.describe(PROJECTS.UPDATE_USER_MEMBERSHIP.roles)
}),
response: {
200: z.object({

View File

@@ -7,6 +7,7 @@ import {
UserEncryptionKeysSchema,
UsersSchema
} from "@app/db/schemas";
import { PROJECTS } from "@app/lib/api-docs";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
import { ProjectFilterType } from "@app/services/project/project-types";
@@ -128,7 +129,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
method: "GET",
schema: {
params: z.object({
workspaceId: z.string().trim()
workspaceId: z.string().trim().describe(PROJECTS.GET.workspaceId)
}),
response: {
200: z.object({
@@ -157,7 +158,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
method: "DELETE",
schema: {
params: z.object({
workspaceId: z.string().trim()
workspaceId: z.string().trim().describe(PROJECTS.DELETE.workspaceId)
}),
response: {
200: z.object({
@@ -220,11 +221,16 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
method: "PATCH",
schema: {
params: z.object({
workspaceId: z.string().trim()
workspaceId: z.string().trim().describe(PROJECTS.UPDATE.workspaceId)
}),
body: z.object({
name: z.string().trim().max(64, { message: "Name must be 64 or fewer characters" }).optional(),
autoCapitalization: z.boolean().optional()
name: z
.string()
.trim()
.max(64, { message: "Name must be 64 or fewer characters" })
.optional()
.describe(PROJECTS.UPDATE.name),
autoCapitalization: z.boolean().optional().describe(PROJECTS.UPDATE.autoCapitalization)
}),
response: {
200: z.object({

View File

@@ -2,6 +2,7 @@ import { z } from "zod";
import { SecretFoldersSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { FOLDERS } from "@app/lib/api-docs";
import { removeTrailingSlash } from "@app/lib/fn";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
@@ -19,12 +20,12 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
}
],
body: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
name: z.string().trim(),
path: z.string().trim().default("/").transform(removeTrailingSlash),
workspaceId: z.string().trim().describe(FOLDERS.CREATE.workspaceId),
environment: z.string().trim().describe(FOLDERS.CREATE.environment),
name: z.string().trim().describe(FOLDERS.CREATE.name),
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(FOLDERS.CREATE.path),
// backward compatiability with cli
directory: z.string().trim().default("/").transform(removeTrailingSlash)
directory: z.string().trim().default("/").transform(removeTrailingSlash).describe(FOLDERS.CREATE.directory)
}),
response: {
200: z.object({
@@ -74,15 +75,15 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
],
params: z.object({
// old way this was name
folderId: z.string()
folderId: z.string().describe(FOLDERS.UPDATE.folderId)
}),
body: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
name: z.string().trim(),
path: z.string().trim().default("/").transform(removeTrailingSlash),
workspaceId: z.string().trim().describe(FOLDERS.UPDATE.workspaceId),
environment: z.string().trim().describe(FOLDERS.UPDATE.environment),
name: z.string().trim().describe(FOLDERS.UPDATE.name),
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(FOLDERS.UPDATE.path),
// backward compatiability with cli
directory: z.string().trim().default("/").transform(removeTrailingSlash)
directory: z.string().trim().default("/").transform(removeTrailingSlash).describe(FOLDERS.UPDATE.directory)
}),
response: {
200: z.object({
@@ -121,6 +122,7 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
}
});
// TODO(daniel): Expose this route in api reference and write docs for it.
server.route({
url: "/:folderIdOrName",
method: "DELETE",
@@ -133,14 +135,14 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
}
],
params: z.object({
folderIdOrName: z.string()
folderIdOrName: z.string().describe(FOLDERS.DELETE.folderIdOrName)
}),
body: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
path: z.string().trim().default("/").transform(removeTrailingSlash),
workspaceId: z.string().trim().describe(FOLDERS.DELETE.workspaceId),
environment: z.string().trim().describe(FOLDERS.DELETE.environment),
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(FOLDERS.DELETE.path),
// keep this here as cli need directory
directory: z.string().trim().default("/").transform(removeTrailingSlash)
directory: z.string().trim().default("/").transform(removeTrailingSlash).describe(FOLDERS.DELETE.directory)
}),
response: {
200: z.object({
@@ -190,11 +192,11 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
}
],
querystring: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
path: z.string().trim().default("/").transform(removeTrailingSlash),
workspaceId: z.string().trim().describe(FOLDERS.LIST.workspaceId),
environment: z.string().trim().describe(FOLDERS.LIST.environment),
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(FOLDERS.LIST.path),
// backward compatiability with cli
directory: z.string().trim().default("/").transform(removeTrailingSlash)
directory: z.string().trim().default("/").transform(removeTrailingSlash).describe(FOLDERS.LIST.directory)
}),
response: {
200: z.object({

View File

@@ -2,6 +2,7 @@ import { z } from "zod";
import { SecretImportsSchema, SecretsSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { SECRET_IMPORTS } from "@app/lib/api-docs";
import { removeTrailingSlash } from "@app/lib/fn";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
@@ -19,12 +20,12 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
}
],
body: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
path: z.string().trim().default("/").transform(removeTrailingSlash),
workspaceId: z.string().trim().describe(SECRET_IMPORTS.CREATE.workspaceId),
environment: z.string().trim().describe(SECRET_IMPORTS.CREATE.environment),
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(SECRET_IMPORTS.CREATE.path),
import: z.object({
environment: z.string().trim(),
path: z.string().trim().transform(removeTrailingSlash)
environment: z.string().trim().describe(SECRET_IMPORTS.CREATE.import.environment),
path: z.string().trim().transform(removeTrailingSlash).describe(SECRET_IMPORTS.CREATE.import.path)
})
}),
response: {
@@ -81,20 +82,21 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
}
],
params: z.object({
secretImportId: z.string().trim()
secretImportId: z.string().trim().describe(SECRET_IMPORTS.UPDATE.secretImportId)
}),
body: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
path: z.string().trim().default("/").transform(removeTrailingSlash),
workspaceId: z.string().trim().describe(SECRET_IMPORTS.UPDATE.workspaceId),
environment: z.string().trim().describe(SECRET_IMPORTS.UPDATE.environment),
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(SECRET_IMPORTS.UPDATE.path),
import: z.object({
environment: z.string().trim().optional(),
environment: z.string().trim().optional().describe(SECRET_IMPORTS.UPDATE.import.environment),
path: z
.string()
.trim()
.optional()
.transform((val) => (val ? removeTrailingSlash(val) : val)),
position: z.number().optional()
.transform((val) => (val ? removeTrailingSlash(val) : val))
.describe(SECRET_IMPORTS.UPDATE.import.path),
position: z.number().optional().describe(SECRET_IMPORTS.UPDATE.import.position)
})
}),
response: {
@@ -152,12 +154,12 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
}
],
params: z.object({
secretImportId: z.string().trim()
secretImportId: z.string().trim().describe(SECRET_IMPORTS.DELETE.secretImportId)
}),
body: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
path: z.string().trim().default("/").transform(removeTrailingSlash)
workspaceId: z.string().trim().describe(SECRET_IMPORTS.DELETE.workspaceId),
environment: z.string().trim().describe(SECRET_IMPORTS.DELETE.environment),
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(SECRET_IMPORTS.DELETE.path)
}),
response: {
200: z.object({
@@ -213,9 +215,9 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
}
],
querystring: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
path: z.string().trim().default("/").transform(removeTrailingSlash)
workspaceId: z.string().trim().describe(SECRET_IMPORTS.LIST.workspaceId),
environment: z.string().trim().describe(SECRET_IMPORTS.LIST.environment),
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(SECRET_IMPORTS.LIST.path)
}),
response: {
200: z.object({

View File

@@ -1,6 +1,7 @@
import { z } from "zod";
import { IdentitiesSchema, IdentityOrgMembershipsSchema, OrgRolesSchema } from "@app/db/schemas";
import { ORGANIZATIONS } from "@app/lib/api-docs";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
@@ -18,7 +19,7 @@ export const registerIdentityOrgRouter = async (server: FastifyZodProvider) => {
}
],
params: z.object({
orgId: z.string().trim()
orgId: z.string().trim().describe(ORGANIZATIONS.LIST_IDENTITY_MEMBERSHIPS.orgId)
}),
response: {
200: z.object({

View File

@@ -7,6 +7,7 @@ import {
ProjectMembershipRole,
ProjectUserMembershipRolesSchema
} from "@app/db/schemas";
import { PROJECTS } from "@app/lib/api-docs";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
import { ProjectUserMembershipTemporaryMode } from "@app/services/project-membership/project-membership-types";
@@ -56,8 +57,8 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
}
],
params: z.object({
projectId: z.string().trim(),
identityId: z.string().trim()
projectId: z.string().trim().describe(PROJECTS.UPDATE_IDENTITY_MEMBERSHIP.projectId),
identityId: z.string().trim().describe(PROJECTS.UPDATE_IDENTITY_MEMBERSHIP.identityId)
}),
body: z.object({
roles: z
@@ -77,6 +78,7 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
])
)
.min(1)
.describe(PROJECTS.UPDATE_IDENTITY_MEMBERSHIP.roles)
}),
response: {
200: z.object({
@@ -110,8 +112,8 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
}
],
params: z.object({
projectId: z.string().trim(),
identityId: z.string().trim()
projectId: z.string().trim().describe(PROJECTS.DELETE_IDENTITY_MEMBERSHIP.projectId),
identityId: z.string().trim().describe(PROJECTS.DELETE_IDENTITY_MEMBERSHIP.identityId)
}),
response: {
200: z.object({
@@ -144,7 +146,7 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
}
],
params: z.object({
projectId: z.string().trim()
projectId: z.string().trim().describe(PROJECTS.LIST_IDENTITY_MEMBERSHIPS.projectId)
}),
response: {
200: z.object({

View File

@@ -1,6 +1,7 @@
import { z } from "zod";
import { OrganizationsSchema, OrgMembershipsSchema, UserEncryptionKeysSchema, UsersSchema } from "@app/db/schemas";
import { ORGANIZATIONS } from "@app/lib/api-docs";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { ActorType, AuthMode } from "@app/services/auth/auth-type";
@@ -17,7 +18,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
}
],
params: z.object({
organizationId: z.string().trim()
organizationId: z.string().trim().describe(ORGANIZATIONS.LIST_USER_MEMBERSHIPS.organizationId)
}),
response: {
200: z.object({
@@ -63,7 +64,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
}
],
params: z.object({
organizationId: z.string().trim()
organizationId: z.string().trim().describe(ORGANIZATIONS.GET_PROJECTS.organizationId)
}),
response: {
200: z.object({
@@ -108,9 +109,12 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
apiKeyAuth: []
}
],
params: z.object({ organizationId: z.string().trim(), membershipId: z.string().trim() }),
params: z.object({
organizationId: z.string().trim().describe(ORGANIZATIONS.UPDATE_USER_MEMBERSHIP.organizationId),
membershipId: z.string().trim().describe(ORGANIZATIONS.UPDATE_USER_MEMBERSHIP.membershipId)
}),
body: z.object({
role: z.string().trim()
role: z.string().trim().describe(ORGANIZATIONS.UPDATE_USER_MEMBERSHIP.role)
}),
response: {
200: z.object({
@@ -145,7 +149,10 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
apiKeyAuth: []
}
],
params: z.object({ organizationId: z.string().trim(), membershipId: z.string().trim() }),
params: z.object({
organizationId: z.string().trim().describe(ORGANIZATIONS.DELETE_USER_MEMBERSHIP.organizationId),
membershipId: z.string().trim().describe(ORGANIZATIONS.DELETE_USER_MEMBERSHIP.membershipId)
}),
response: {
200: z.object({
membership: OrgMembershipsSchema

View File

@@ -2,6 +2,7 @@ import { z } from "zod";
import { ProjectMembershipsSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { PROJECTS } from "@app/lib/api-docs";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
@@ -11,11 +12,11 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
url: "/:projectId/memberships",
schema: {
params: z.object({
projectId: z.string().describe("The ID of the project.")
projectId: z.string().describe(PROJECTS.INVITE_MEMBER.projectId)
}),
body: z.object({
emails: z.string().email().array().default([]).describe("Emails of the users to add to the project."),
usernames: z.string().array().default([]).describe("Usernames of the users to add to the project.")
emails: z.string().email().array().default([]).describe(PROJECTS.INVITE_MEMBER.emails),
usernames: z.string().array().default([]).describe(PROJECTS.INVITE_MEMBER.usernames)
}),
response: {
200: z.object({
@@ -57,12 +58,12 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
url: "/:projectId/memberships",
schema: {
params: z.object({
projectId: z.string().describe("The ID of the project.")
projectId: z.string().describe(PROJECTS.REMOVE_MEMBER.projectId)
}),
body: z.object({
emails: z.string().email().array().default([]).describe("Emails of the users to remove from the project."),
usernames: z.string().array().default([]).describe("Usernames of the users to remove from the project.")
emails: z.string().email().array().default([]).describe(PROJECTS.REMOVE_MEMBER.emails),
usernames: z.string().array().default([]).describe(PROJECTS.REMOVE_MEMBER.usernames)
}),
response: {
200: z.object({

View File

@@ -3,6 +3,7 @@ import { z } from "zod";
import { ProjectKeysSchema, ProjectsSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { PROJECTS } from "@app/lib/api-docs";
import { authRateLimit } from "@app/server/config/rateLimiter";
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
@@ -38,7 +39,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
}
],
params: z.object({
workspaceId: z.string().trim()
workspaceId: z.string().trim().describe(PROJECTS.GET_KEY.workspaceId)
}),
response: {
200: ProjectKeysSchema.merge(
@@ -140,11 +141,16 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
},
schema: {
body: z.object({
projectName: z.string().trim().describe("Name of the project you're creating"),
slug: slugSchema
projectName: z.string().trim().describe(PROJECTS.CREATE.projectName),
slug: z
.string()
.min(5)
.max(36)
.refine((v) => slugify(v) === v, {
message: "Slug must be a valid slug"
})
.optional()
.describe("An optional slug for the project. If not provided, it will be auto-generated"),
organizationSlug: z.string().trim().describe("The slug of the organization to create the project in")
.describe(PROJECTS.CREATE.slug)
}),
response: {
200: z.object({
@@ -159,7 +165,6 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
actor: req.permission.type,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
orgSlug: req.body.organizationSlug,
workspaceName: req.body.projectName,
slug: req.body.slug
});

View File

@@ -10,6 +10,7 @@ import {
} from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { CommitType } from "@app/ee/services/secret-approval-request/secret-approval-request-types";
import { RAW_SECRETS } from "@app/lib/api-docs";
import { BadRequestError } from "@app/lib/errors";
import { removeTrailingSlash } from "@app/lib/fn";
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
@@ -34,20 +35,15 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
}
],
querystring: z.object({
workspaceId: z.string().trim().optional(),
workspaceSlug: z
.string()
.trim()
.optional()
.describe(
"The slug of the workspace. This is only supported when authenticating Machine Identity's. Either the project ID or project slug has to be present in the request."
),
environment: z.string().trim().optional(),
secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
workspaceId: z.string().trim().optional().describe(RAW_SECRETS.LIST.workspaceId),
workspaceSlug: z.string().trim().optional().describe(RAW_SECRETS.LIST.workspaceSlug),
environment: z.string().trim().optional().describe(RAW_SECRETS.LIST.environment),
secretPath: z.string().trim().default("/").transform(removeTrailingSlash).describe(RAW_SECRETS.LIST.secretPath),
include_imports: z
.enum(["true", "false"])
.default("false")
.transform((value) => value === "true")
.describe(RAW_SECRETS.LIST.includeImports)
}),
response: {
200: z.object({
@@ -148,18 +144,19 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
}
],
params: z.object({
secretName: z.string().trim()
secretName: z.string().trim().describe(RAW_SECRETS.GET.secretName)
}),
querystring: z.object({
workspaceId: z.string().trim().optional(),
environment: z.string().trim().optional(),
secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
version: z.coerce.number().optional(),
type: z.nativeEnum(SecretType).default(SecretType.Shared),
workspaceId: z.string().trim().optional().describe(RAW_SECRETS.GET.workspaceId),
environment: z.string().trim().optional().describe(RAW_SECRETS.GET.environment),
secretPath: z.string().trim().default("/").transform(removeTrailingSlash).describe(RAW_SECRETS.GET.secretPath),
version: z.coerce.number().optional().describe(RAW_SECRETS.GET.version),
type: z.nativeEnum(SecretType).default(SecretType.Shared).describe(RAW_SECRETS.GET.type),
include_imports: z
.enum(["true", "false"])
.default("false")
.transform((value) => value === "true")
.describe(RAW_SECRETS.GET.includeImports)
}),
response: {
200: z.object({
@@ -239,16 +236,24 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
}
],
params: z.object({
secretName: z.string().trim()
secretName: z.string().trim().describe(RAW_SECRETS.CREATE.secretName)
}),
body: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
secretValue: z.string().transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim())),
secretComment: z.string().trim().optional().default(""),
skipMultilineEncoding: z.boolean().optional(),
type: z.nativeEnum(SecretType).default(SecretType.Shared)
workspaceId: z.string().trim().describe(RAW_SECRETS.CREATE.workspaceId),
environment: z.string().trim().describe(RAW_SECRETS.CREATE.environment),
secretPath: z
.string()
.trim()
.default("/")
.transform(removeTrailingSlash)
.describe(RAW_SECRETS.CREATE.secretPath),
secretValue: z
.string()
.transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim()))
.describe(RAW_SECRETS.CREATE.secretValue),
secretComment: z.string().trim().optional().default("").describe(RAW_SECRETS.CREATE.secretComment),
skipMultilineEncoding: z.boolean().optional().describe(RAW_SECRETS.CREATE.skipMultilineEncoding),
type: z.nativeEnum(SecretType).default(SecretType.Shared).describe(RAW_SECRETS.CREATE.type)
}),
response: {
200: z.object({
@@ -317,15 +322,23 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
}
],
params: z.object({
secretName: z.string().trim()
secretName: z.string().trim().describe(RAW_SECRETS.UPDATE.secretName)
}),
body: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
secretValue: z.string().transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim())),
secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
skipMultilineEncoding: z.boolean().optional(),
type: z.nativeEnum(SecretType).default(SecretType.Shared)
workspaceId: z.string().trim().describe(RAW_SECRETS.UPDATE.workspaceId),
environment: z.string().trim().describe(RAW_SECRETS.UPDATE.environment),
secretValue: z
.string()
.transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim()))
.describe(RAW_SECRETS.UPDATE.secretValue),
secretPath: z
.string()
.trim()
.default("/")
.transform(removeTrailingSlash)
.describe(RAW_SECRETS.UPDATE.secretPath),
skipMultilineEncoding: z.boolean().optional().describe(RAW_SECRETS.UPDATE.skipMultilineEncoding),
type: z.nativeEnum(SecretType).default(SecretType.Shared).describe(RAW_SECRETS.UPDATE.type)
}),
response: {
200: z.object({
@@ -392,13 +405,18 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
}
],
params: z.object({
secretName: z.string().trim()
secretName: z.string().trim().describe(RAW_SECRETS.DELETE.secretName)
}),
body: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
type: z.nativeEnum(SecretType).default(SecretType.Shared)
workspaceId: z.string().trim().describe(RAW_SECRETS.DELETE.workspaceId),
environment: z.string().trim().describe(RAW_SECRETS.DELETE.environment),
secretPath: z
.string()
.trim()
.default("/")
.transform(removeTrailingSlash)
.describe(RAW_SECRETS.DELETE.secretPath),
type: z.nativeEnum(SecretType).default(SecretType.Shared).describe(RAW_SECRETS.DELETE.type)
}),
response: {
200: z.object({

View File

@@ -260,20 +260,44 @@ const getAppsGithub = async ({ accessToken }: { accessToken: string }) => {
* Return list of services for Render integration
*/
const getAppsRender = async ({ accessToken }: { accessToken: string }) => {
const res = (
await request.get<{ service: { name: string; id: string } }[]>(`${IntegrationUrls.RENDER_API_URL}/v1/services`, {
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: "application/json",
"Accept-Encoding": "application/json"
}
})
).data;
const apps: Array<{ name: string; appId: string }> = [];
let hasMorePages = true;
const perPage = 100;
let cursor;
const apps = res.map((a) => ({
name: a.service.name,
appId: a.service.id
}));
interface RenderService {
cursor: string;
service: { name: string; id: string };
}
while (hasMorePages) {
const res: RenderService[] = (
await request.get<RenderService[]>(`${IntegrationUrls.RENDER_API_URL}/v1/services`, {
params: new URLSearchParams({
...(cursor ? { cursor: String(cursor) } : {}),
limit: String(perPage)
}),
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: "application/json",
"Accept-Encoding": "application/json"
}
})
).data;
res.forEach((a) => {
apps.push({
name: a.service.name,
appId: a.service.id
});
});
if (res.length < perPage) {
hasMorePages = false;
} else {
cursor = res[res.length - 1].cursor;
}
}
return apps;
};

View File

@@ -1,4 +1,5 @@
import { ForbiddenError } from "@casl/ability";
import { Octokit } from "@octokit/rest";
import { SecretEncryptionAlgo, SecretKeyEncoding, TIntegrationAuths, TIntegrationAuthsInsert } from "@app/db/schemas";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
@@ -24,6 +25,8 @@ import {
TIntegrationAuthAppsDTO,
TIntegrationAuthBitbucketWorkspaceDTO,
TIntegrationAuthChecklyGroupsDTO,
TIntegrationAuthGithubEnvsDTO,
TIntegrationAuthGithubOrgsDTO,
TIntegrationAuthHerokuPipelinesDTO,
TIntegrationAuthNorthflankSecretGroupDTO,
TIntegrationAuthQoveryEnvironmentsDTO,
@@ -435,6 +438,75 @@ export const integrationAuthServiceFactory = ({
return [];
};
const getGithubOrgs = async ({ actorId, actor, actorOrgId, actorAuthMethod, id }: TIntegrationAuthGithubOrgsDTO) => {
const integrationAuth = await integrationAuthDAL.findById(id);
if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" });
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
integrationAuth.projectId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const botKey = await projectBotService.getBotKey(integrationAuth.projectId);
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey);
const octokit = new Octokit({
auth: accessToken
});
const { data } = await octokit.request("GET /user/orgs", {
headers: {
"X-GitHub-Api-Version": "2022-11-28"
}
});
if (!data) return [];
return data.map(({ login: name, id: orgId }) => ({ name, orgId: String(orgId) }));
};
const getGithubEnvs = async ({
actorId,
actor,
actorOrgId,
actorAuthMethod,
id,
repoOwner,
repoName
}: TIntegrationAuthGithubEnvsDTO) => {
const integrationAuth = await integrationAuthDAL.findById(id);
if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" });
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
integrationAuth.projectId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const botKey = await projectBotService.getBotKey(integrationAuth.projectId);
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey);
const octokit = new Octokit({
auth: accessToken
});
const {
data: { environments }
} = await octokit.request("GET /repos/{owner}/{repo}/environments", {
headers: {
"X-GitHub-Api-Version": "2022-11-28"
},
owner: repoOwner,
repo: repoName
});
if (!environments) return [];
return environments.map(({ id: envId, name }) => ({ name, envId: String(envId) }));
};
const getQoveryOrgs = async ({ actorId, actor, actorOrgId, actorAuthMethod, id }: TIntegrationAuthQoveryOrgsDTO) => {
const integrationAuth = await integrationAuthDAL.findById(id);
if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" });
@@ -1061,6 +1133,8 @@ export const integrationAuthServiceFactory = ({
getIntegrationApps,
getVercelBranches,
getApps,
getGithubOrgs,
getGithubEnvs,
getChecklyGroups,
getQoveryApps,
getQoveryEnvs,

View File

@@ -44,6 +44,16 @@ export type TIntegrationAuthChecklyGroupsDTO = {
accountId: string;
} & Omit<TProjectPermission, "projectId">;
export type TIntegrationAuthGithubOrgsDTO = {
id: string;
} & Omit<TProjectPermission, "projectId">;
export type TIntegrationAuthGithubEnvsDTO = {
id: string;
repoName: string;
repoOwner: string;
} & Omit<TProjectPermission, "projectId">;
export type TIntegrationAuthQoveryOrgsDTO = {
id: string;
} & Omit<TProjectPermission, "projectId">;

View File

@@ -459,7 +459,7 @@ const syncSecretsAWSParameterStore = async ({
const params = {
Path: integration.path as string,
Recursive: true,
Recursive: false,
WithDecryption: true
};
@@ -1110,98 +1110,176 @@ const syncSecretsGitHub = async ({
interface GitHubRepoKey {
key_id: string;
key: string;
id?: number | undefined;
url?: string | undefined;
title?: string | undefined;
created_at?: string | undefined;
}
interface GitHubSecret {
name: string;
created_at: string;
updated_at: string;
}
interface GitHubSecretRes {
[index: string]: GitHubSecret;
visibility?: "all" | "private" | "selected";
selected_repositories_url?: string | undefined;
}
const octokit = new Octokit({
auth: accessToken
});
// const user = (await octokit.request('GET /user', {})).data;
const repoPublicKey: GitHubRepoKey = (
await octokit.request("GET /repos/{owner}/{repo}/actions/secrets/public-key", {
owner: integration.owner as string,
repo: integration.app as string
})
).data;
enum GithubScope {
Repo = "github-repo",
Org = "github-org",
Env = "github-env"
}
let repoPublicKey: GitHubRepoKey;
switch (integration.scope) {
case GithubScope.Org: {
const { data } = await octokit.request("GET /orgs/{org}/actions/secrets/public-key", {
org: integration.owner as string
});
repoPublicKey = data;
break;
}
case GithubScope.Env: {
const { data } = await octokit.request(
"GET /repositories/{repository_id}/environments/{environment_name}/secrets/public-key",
{
repository_id: Number(integration.appId),
environment_name: integration.targetEnvironmentId as string
}
);
repoPublicKey = data;
break;
}
default: {
const { data } = await octokit.request("GET /repos/{owner}/{repo}/actions/secrets/public-key", {
owner: integration.owner as string,
repo: integration.app as string
});
repoPublicKey = data;
break;
}
}
// Get local copy of decrypted secrets. We cannot decrypt them as we dont have access to GH private key
let encryptedSecrets: GitHubSecretRes = (
await octokit.request("GET /repos/{owner}/{repo}/actions/secrets", {
owner: integration.owner as string,
repo: integration.app as string
})
).data.secrets.reduce(
(obj, secret) => ({
...obj,
[secret.name]: secret
}),
{}
);
let encryptedSecrets: GitHubSecret[];
encryptedSecrets = Object.keys(encryptedSecrets).reduce(
(
result: {
[key: string]: GitHubSecret;
},
key
) => {
if (
(appendices?.prefix !== undefined ? key.startsWith(appendices?.prefix) : true) &&
(appendices?.suffix !== undefined ? key.endsWith(appendices?.suffix) : true)
) {
result[key] = encryptedSecrets[key];
}
return result;
},
{}
);
await Promise.all(
Object.keys(encryptedSecrets).map(async (key) => {
if (!(key in secrets)) {
return octokit.request("DELETE /repos/{owner}/{repo}/actions/secrets/{secret_name}", {
switch (integration.scope) {
case GithubScope.Org: {
encryptedSecrets = (
await octokit.request("GET /orgs/{org}/actions/secrets", {
org: integration.owner as string
})
).data.secrets;
break;
}
case GithubScope.Env: {
encryptedSecrets = (
await octokit.request("GET /repositories/{repository_id}/environments/{environment_name}/secrets", {
repository_id: Number(integration.appId),
environment_name: integration.targetEnvironmentId as string
})
).data.secrets;
break;
}
default: {
encryptedSecrets = (
await octokit.request("GET /repos/{owner}/{repo}/actions/secrets", {
owner: integration.owner as string,
repo: integration.app as string,
secret_name: key
});
repo: integration.app as string
})
).data.secrets;
break;
}
}
for await (const encryptedSecret of encryptedSecrets) {
if (
!(encryptedSecret.name in secrets) &&
!(appendices?.prefix !== undefined && !encryptedSecret.name.startsWith(appendices?.prefix)) &&
!(appendices?.suffix !== undefined && !encryptedSecret.name.endsWith(appendices?.suffix))
) {
switch (integration.scope) {
case GithubScope.Org: {
await octokit.request("DELETE /orgs/{org}/actions/secrets/{secret_name}", {
org: integration.owner as string,
secret_name: encryptedSecret.name
});
break;
}
case GithubScope.Env: {
await octokit.request(
"DELETE /repositories/{repository_id}/environments/{environment_name}/secrets/{secret_name}",
{
repository_id: Number(integration.appId),
environment_name: integration.targetEnvironmentId as string,
secret_name: encryptedSecret.name
}
);
break;
}
default: {
await octokit.request("DELETE /repos/{owner}/{repo}/actions/secrets/{secret_name}", {
owner: integration.owner as string,
repo: integration.app as string,
secret_name: encryptedSecret.name
});
break;
}
}
})
);
}
}
await Promise.all(
Object.keys(secrets).map((key) => {
// let encryptedSecret;
return sodium.ready.then(async () => {
// convert secret & base64 key to Uint8Array.
const binkey = sodium.from_base64(repoPublicKey.key, sodium.base64_variants.ORIGINAL);
const binsec = sodium.from_string(secrets[key].value);
await sodium.ready.then(async () => {
for await (const key of Object.keys(secrets)) {
// convert secret & base64 key to Uint8Array.
const binkey = sodium.from_base64(repoPublicKey.key, sodium.base64_variants.ORIGINAL);
const binsec = sodium.from_string(secrets[key].value);
// encrypt secret using libsodium
const encBytes = sodium.crypto_box_seal(binsec, binkey);
// encrypt secret using libsodium
const encBytes = sodium.crypto_box_seal(binsec, binkey);
// convert encrypted Uint8Array to base64
const encryptedSecret = sodium.to_base64(encBytes, sodium.base64_variants.ORIGINAL);
// convert encrypted Uint8Array to base64
const encryptedSecret = sodium.to_base64(encBytes, sodium.base64_variants.ORIGINAL);
await octokit.request("PUT /repos/{owner}/{repo}/actions/secrets/{secret_name}", {
owner: integration.owner as string,
repo: integration.app as string,
secret_name: key,
encrypted_value: encryptedSecret,
key_id: repoPublicKey.key_id
});
});
})
);
switch (integration.scope) {
case GithubScope.Org:
await octokit.request("PUT /orgs/{org}/actions/secrets/{secret_name}", {
org: integration.owner as string,
secret_name: key,
visibility: "all",
encrypted_value: encryptedSecret,
key_id: repoPublicKey.key_id
});
break;
case GithubScope.Env:
await octokit.request(
"PUT /repositories/{repository_id}/environments/{environment_name}/secrets/{secret_name}",
{
repository_id: Number(integration.appId),
environment_name: integration.targetEnvironmentId as string,
secret_name: key,
encrypted_value: encryptedSecret,
key_id: repoPublicKey.key_id
}
);
break;
default:
await octokit.request("PUT /repos/{owner}/{repo}/actions/secrets/{secret_name}", {
owner: integration.owner as string,
repo: integration.app as string,
secret_name: key,
encrypted_value: encryptedSecret,
key_id: repoPublicKey.key_id
});
break;
}
}
});
};
/**
@@ -1229,6 +1307,22 @@ const syncSecretsRender = async ({
}
}
);
if (integration.metadata) {
const metadata = z.record(z.any()).parse(integration.metadata);
if (metadata.shouldAutoRedeploy === true) {
await request.post(
`${IntegrationUrls.RENDER_API_URL}/v1/services/${integration.appId}/deploys`,
{},
{
headers: {
Authorization: `Bearer ${accessToken}`,
"Accept-Encoding": "application/json"
}
}
);
}
}
};
/**

View File

@@ -92,7 +92,6 @@ export const projectServiceFactory = ({
* Create workspace. Make user the admin
* */
const createProject = async ({
orgSlug,
actor,
actorId,
actorOrgId,
@@ -100,13 +99,7 @@ export const projectServiceFactory = ({
workspaceName,
slug: projectSlug
}: TCreateProjectDTO) => {
if (!orgSlug) {
throw new BadRequestError({
message: "Must provide organization slug to create project"
});
}
const organization = await orgDAL.findOne({ slug: orgSlug });
const organization = await orgDAL.findOne({ id: actorOrgId });
const { permission, membership: orgMembership } = await permissionService.getOrgPermission(
actor,

View File

@@ -24,7 +24,6 @@ export type TCreateProjectDTO = {
actorAuthMethod: ActorAuthMethod;
actorId: string;
actorOrgId?: string;
orgSlug: string;
workspaceName: string;
slug?: string;
};

View File

@@ -232,6 +232,7 @@ export const secretFolderServiceFactory = ({
if (!parentFolder) return [];
const folders = await folderDAL.find({ envId: env.id, parentId: parentFolder.id });
return folders;
};

View File

@@ -23,7 +23,8 @@ export default defineConfig({
loader: {
".handlebars": "copy",
".md": "copy",
".txt": "copy"
".txt": "copy",
".pem": "copy"
},
external: ["../../../frontend/node_modules/next/dist/server/next-server.js"],
outDir: "dist",

View File

@@ -164,6 +164,28 @@ func CallGetAllOrganizations(httpClient *resty.Client) (GetOrganizationsResponse
return orgResponse, nil
}
func CallSelectOrganization(httpClient *resty.Client, request SelectOrganizationRequest) (SelectOrganizationResponse, error) {
var selectOrgResponse SelectOrganizationResponse
response, err := httpClient.
R().
SetBody(request).
SetResult(&selectOrgResponse).
SetHeader("User-Agent", USER_AGENT).
Post(fmt.Sprintf("%v/v3/auth/select-organization", config.INFISICAL_URL))
if err != nil {
return SelectOrganizationResponse{}, err
}
if response.IsError() {
return SelectOrganizationResponse{}, fmt.Errorf("CallSelectOrganization: Unsuccessful response: [response=%v]", response)
}
return selectOrgResponse, nil
}
func CallGetAllWorkSpacesUserBelongsTo(httpClient *resty.Client) (GetWorkSpacesResponse, error) {
var workSpacesResponse GetWorkSpacesResponse
response, err := httpClient.

View File

@@ -135,6 +135,14 @@ type GetOrganizationsResponse struct {
} `json:"organizations"`
}
type SelectOrganizationResponse struct {
Token string `json:"token"`
}
type SelectOrganizationRequest struct {
OrganizationId string `json:"organizationId"`
}
type Secret struct {
SecretKeyCiphertext string `json:"secretKeyCiphertext,omitempty"`
SecretKeyIV string `json:"secretKeyIV,omitempty"`

View File

@@ -74,6 +74,21 @@ var initCmd = &cobra.Command{
selectedOrganization := organizations[index]
tokenResponse, err := api.CallSelectOrganization(httpClient, api.SelectOrganizationRequest{OrganizationId: selectedOrganization.ID})
if err != nil {
util.HandleError(err, "Unable to select organization")
}
// set the config jwt token to the new token
userCreds.UserCredentials.JTWToken = tokenResponse.Token
err = util.StoreUserCredsInKeyRing(&userCreds.UserCredentials)
httpClient.SetAuthToken(tokenResponse.Token)
if err != nil {
util.HandleError(err, "Unable to store your user credentials")
}
workspaceResponse, err := api.CallGetAllWorkSpacesUserBelongsTo(httpClient)
if err != nil {
util.HandleError(err, "Unable to pull projects that belong to you")

View File

@@ -301,11 +301,13 @@ func cliDefaultLogin(userCredentialsToBeStored *models.UserCredentials) {
log.Debug().Msgf("[decryptedPrivateKey=%s] [email=%s] [loginTwoResponse.Token=%s]", string(decryptedPrivateKey), email, loginTwoResponse.Token)
util.PrintErrorMessageAndExit("We were unable to fetch required details to complete your login. Run with -d to see more info")
}
// Login is successful so ask user to choose organization
newJwtToken := GetJwtTokenWithOrganizationId(loginTwoResponse.Token)
//updating usercredentials
userCredentialsToBeStored.Email = email
userCredentialsToBeStored.PrivateKey = string(decryptedPrivateKey)
userCredentialsToBeStored.JTWToken = loginTwoResponse.Token
userCredentialsToBeStored.JTWToken = newJwtToken
}
func init() {
@@ -480,6 +482,44 @@ func getFreshUserCredentials(email string, password string) (*api.GetLoginOneV2R
return &loginOneResponseResult, &loginTwoResponseResult, nil
}
func GetJwtTokenWithOrganizationId(oldJwtToken string) string {
log.Debug().Msg(fmt.Sprint("GetJwtTokenWithOrganizationId: ", "oldJwtToken", oldJwtToken))
httpClient := resty.New()
httpClient.SetAuthToken(oldJwtToken)
organizationResponse, err := api.CallGetAllOrganizations(httpClient)
if err != nil {
util.HandleError(err, "Unable to pull organizations that belong to you")
}
organizations := organizationResponse.Organizations
organizationNames := util.GetOrganizationsNameList(organizationResponse)
prompt := promptui.Select{
Label: "Which Infisical organization would you like to log into?",
Items: organizationNames,
}
index, _, err := prompt.Run()
if err != nil {
util.HandleError(err)
}
selectedOrganization := organizations[index]
selectedOrgRes, err := api.CallSelectOrganization(httpClient, api.SelectOrganizationRequest{OrganizationId: selectedOrganization.ID})
if err != nil {
util.HandleError(err)
}
return selectedOrgRes.Token
}
func userLoginMenu(currentLoggedInUserEmail string) (bool, error) {
label := fmt.Sprintf("Current logged in user email: %s on domain: %s", currentLoggedInUserEmail, config.INFISICAL_URL)

View File

@@ -419,7 +419,7 @@ func getSecretsByNames(cmd *cobra.Command, args []string) {
util.HandleError(err, "Unable to parse path flag")
}
secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, SecretsPath: secretsPath}, "")
secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, SecretsPath: secretsPath, IncludeImport: true}, "")
if err != nil {
util.HandleError(err, "To fetch all secrets")
}
@@ -477,7 +477,7 @@ func generateExampleEnv(cmd *cobra.Command, args []string) {
util.HandleError(err, "Unable to parse flag")
}
secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, SecretsPath: secretsPath}, "")
secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, SecretsPath: secretsPath, IncludeImport: true}, "")
if err != nil {
util.HandleError(err, "To fetch all secrets")
}

View File

@@ -1,4 +1,4 @@
---
title: "Delete"
openapi: "DELETE /api/v1/folders/{folderId}"
openapi: "DELETE /api/v1/folders/{folderIdOrName}"
---

View File

@@ -18,4 +18,4 @@ Follow the instructions for your language use the SDK for it:
- [Java SDK](https://infisical.com/docs/sdks/languages/java)
- [.NET SDK](https://infisical.com/docs/sdks/languages/csharp)
Missing a language? [Throw in a request](https://github.com/Infisical/infisical/issues).
Missing a language? [Throw in a request here](https://github.com/Infisical/infisical/issues).

Binary file not shown.

Before

Width:  |  Height:  |  Size: 733 KiB

After

Width:  |  Height:  |  Size: 739 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 691 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 709 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 715 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 398 KiB

After

Width:  |  Height:  |  Size: 649 KiB

View File

@@ -3,17 +3,14 @@ title: "GitHub Actions"
description: "How to sync secrets from Infisical to GitHub Actions"
---
Infisical lets you sync secrets to GitHub at the organization-level, repository-level, and repository environment-level.
Prerequisites:
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
- Ensure that you have admin privileges to the repository you want to sync secrets to.
<Tabs>
<Tab title="Usage">
<Warning>
Infisical can sync secrets to GitHub repo secrets only. If your repo uses environment secrets, then stay tuned with this [issue](https://github.com/Infisical/infisical/issues/54).
</Warning>
Prerequisites:
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
- Ensure you have admin privileges to the repo you want to sync secrets to.
<Steps>
<Step title="Authorize Infisical for GitHub">
Navigate to your project's integrations tab in Infisical.
@@ -29,12 +26,27 @@ description: "How to sync secrets from Infisical to GitHub Actions"
Although this step breaks E2EE, it's necessary for Infisical to sync the environment variables to the cloud platform.
</Info>
</Step>
<Step title="Start integration">
Select which Infisical environment secrets you want to sync to which GitHub repo and press start integration to start syncing secrets to the repo.
<Step title="Configure Infisical GitHub integration">
Select which Infisical environment secrets you want to sync to which GitHub organization, repository, or repository environment.
<Tabs>
<Tab title="Repository">
![integrations github](../../images/integrations/github/integrations-github-scope-repo.png)
</Tab>
<Tab title="Organization">
![integrations github](../../images/integrations/github/integrations-github-scope-org.png)
</Tab>
<Tab title="Repository Environment">
![integrations github](../../images/integrations/github/integrations-github-scope-env.png)
</Tab>
</Tabs>
Finally, press create integration to start syncing secrets to GitHub.
![integrations github](../../images/integrations/github/integrations-github.png)
</Step>
</Steps>
</Tab>
<Tab title="Self-Hosted Setup">
Using the GitHub integration on a self-hosted instance of Infisical requires configuring an OAuth application in GitHub
@@ -45,13 +57,13 @@ description: "How to sync secrets from Infisical to GitHub Actions"
![integrations github config](../../images/integrations/github/integrations-github-config-settings.png)
![integrations github config](../../images/integrations/github/integrations-github-config-dev-settings.png)
![integrations github config](../../images/integrations/github/integrations-github-config-new-app.png)
![integrations github config](../../images/integrations/github/integrations-github-config-new-app.png)
Create the OAuth application. As part of the form, set the **Homepage URL** to your self-hosted domain `https://your-domain.com`
and the **Authorization callback URL** to `https://your-domain.com/integrations/github/oauth2/callback`.
![integrations github config](../../images/integrations/github/integrations-github-config-new-app-form.png)
![integrations github config](../../images/integrations/github/integrations-github-config-new-app-form.png)
<Note>
If you have a GitHub organization, you can create an OAuth application under it
in your organization Settings > Developer settings > OAuth Apps > New Org OAuth App.
@@ -59,17 +71,17 @@ description: "How to sync secrets from Infisical to GitHub Actions"
</Step>
<Step title="Add your OAuth application credentials to Infisical">
Obtain the **Client ID** and generate a new **Client Secret** for your GitHub OAuth application.
![integrations github config](../../images/integrations/github/integrations-github-config-credentials.png)
![integrations github config](../../images/integrations/github/integrations-github-config-credentials.png)
Back in your Infisical instance, add two new environment variables for the credentials of your GitHub OAuth application:
- `CLIENT_ID_GITHUB`: The **Client ID** of your GitHub OAuth application.
- `CLIENT_SECRET_GITHUB`: The **Client Secret** of your GitHub OAuth application.
Once added, restart your Infisical instance and use the GitHub integration.
</Step>
</Steps>
</Tab>
</Tabs>

View File

@@ -39,32 +39,32 @@
"name": "Start for Free",
"url": "https://app.infisical.com/signup"
},
"anchors": [
"tabs": [
{
"name": "Internals",
"icon": "sitemap",
"url": "internals"
},
{
"name": "SDKs",
"icon": "puzzle-piece",
"url": "sdks"
"name": "Changelog",
"url": "changelog"
},
{
"name": "API Reference",
"icon": "cloud",
"url": "api-reference"
},
{
"name": "Changelog",
"icon": "timer",
"url": "changelog"
"name": "SDKs",
"url": "sdks"
},
{
"name": "Contributing",
"url": "contributing"
}
],
"anchors": [
{
"name": "Contributing",
"icon": "code",
"url": "contributing"
},
{
"name": "Blog",
"icon": "newspaper",
@@ -79,6 +79,11 @@
"name": "GitHub",
"icon": "github",
"url": "https://github.com/Infisical/infisical"
},
{
"name": "Internals",
"icon": "sitemap",
"url": "internals"
}
],
"navigation": [
@@ -191,6 +196,7 @@
"self-hosting/guides/mongo-to-postgres"
]
},
"self-hosting/ee",
"self-hosting/faq"
]
},
@@ -356,7 +362,18 @@
},
{
"group": "Overview",
"pages": ["sdks/overview"]
"pages": [
"sdks/overview"
]
},
{
"group": "SDK's",
"pages": [
"sdks/languages/node",
"sdks/languages/python",
"sdks/languages/java",
"sdks/languages/csharp"
]
},
{
"group": "Overview",

View File

@@ -1,6 +1,7 @@
---
title: "Infisical .NET SDK"
icon: "C#"
sidebarTitle: ".NET"
icon: "bars"
---
If you're working with C#, the official [Infisical C# SDK](https://github.com/Infisical/sdk/tree/main/languages/csharp) package is the easiest way to fetch and work with secrets for your application.

View File

@@ -1,5 +1,6 @@
---
title: "Infisical Java SDK"
sidebarTitle: "Java"
icon: "java"
---

View File

@@ -1,5 +1,6 @@
---
title: "Infisical Node.js SDK"
sidebarTitle: "Node.js"
icon: "node"
---

View File

@@ -1,5 +1,6 @@
---
title: "Infisical Python SDK"
sidebarTitle: "Python"
icon: "python"
---

28
docs/self-hosting/ee.mdx Normal file
View File

@@ -0,0 +1,28 @@
---
title: "Using Infisical EE"
description: "How to activate Infisical Enterprise Edition (EE) features"
---
While most features in Infisical are free to use, others are paid and require purchasing an enterprise license to use them.
This guide walks through how you can use these paid features in Infisical.
<Steps>
<Step title="Purchase a license">
Start by either signing up for a free demo [here](https://infisical.com/schedule-demo) or contacting sales@infisical.com to purchase a license.
Once purchased, you will be issued a license key.
</Step>
<Step title="Activate the license">
Depending on whether or not the environment where Infisical is deployed has internet access, you may be issued a regular license or an offline license.
- If using a regular license, you should set the value of the environment variable `LICENSE_KEY` in Infisical to the issued license key.
- If using an offline license, you should set the value of the environment variable `LICENSE_KEY_OFFLINE` in Infisical to the issued license key.
Once your instance starts up, the license key will be validated and youll be able to use the paid features.
<Note>
Once the license expires, Infisical will continue to run, but EE features will be disabled until the license is renewed or a new one is purchased.
</Note>
</Step>
</Steps>

View File

@@ -29,6 +29,7 @@ module.exports = {
},
plugins: ["react", "prettier", "simple-import-sort", "import"],
rules: {
"@typescript-eslint/no-empty-function": "off",
quotes: ["error", "double", { avoidEscape: true }],
"comma-dangle": ["error", "only-multiline"],
"react/react-in-jsx-scope": "off",
@@ -72,7 +73,6 @@ module.exports = {
],
"@typescript-eslint/no-non-null-assertion": "off",
"simple-import-sort/exports": "warn",
"@typescript-eslint/no-empty-function": "off",
"simple-import-sort/imports": [
"warn",
{

View File

@@ -1,28 +1,28 @@
const path = require('path');
const path = require("path");
module.exports = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'storybook-dark-mode',
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions",
"storybook-dark-mode",
{
name: '@storybook/addon-styling',
name: "@storybook/addon-styling",
options: {
postCss: {
implementation: require('postcss')
implementation: require("postcss")
}
}
}
],
framework: {
name: '@storybook/nextjs',
name: "@storybook/nextjs",
options: {}
},
core: {
disableTelemetry: true
},
docs: {
autodocs: 'tag'
autodocs: "tag"
}
};

View File

@@ -6,7 +6,7 @@ import { ENV, POSTHOG_API_KEY, POSTHOG_HOST } from "../utilities/config";
export const initPostHog = () => {
// @ts-ignore
console.log("Hi there 👋")
console.log("Hi there 👋");
try {
if (typeof window !== "undefined") {
// @ts-ignore
@@ -19,7 +19,7 @@ export const initPostHog = () => {
return posthog;
} catch (e) {
console.log("posthog err", e)
console.log("posthog err", e);
}
return undefined;

View File

@@ -3,9 +3,9 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
const Error = ({ text }: { text: string }): JSX.Element => {
return (
<div className="relative flex flex-row justify-center m-auto items-center w-fit rounded-full">
<FontAwesomeIcon icon={faExclamationTriangle} className="text-red mt-1.5 mb-2 mx-2" />
{text && <p className="relative top-0 text-red mr-2 text-sm py-1">{text}</p>}
<div className="relative m-auto flex w-fit flex-row items-center justify-center rounded-full">
<FontAwesomeIcon icon={faExclamationTriangle} className="mx-2 mt-1.5 mb-2 text-red" />
{text && <p className="relative top-0 mr-2 py-1 text-sm text-red">{text}</p>}
</div>
);
};

View File

@@ -39,16 +39,16 @@ const InputField = ({
if (isStatic === true) {
return (
<div className="flex flex-col my-2 md:my-4 justify-center w-full max-w-md">
<p className="text-sm font-semibold text-gray-400 mb-0.5">{label}</p>
{text && <p className="text-xs text-gray-400 mb-2">{text}</p>}
<div className="my-2 flex w-full max-w-md flex-col justify-center md:my-4">
<p className="mb-0.5 text-sm font-semibold text-gray-400">{label}</p>
{text && <p className="mb-2 text-xs text-gray-400">{text}</p>}
<input
onChange={(e) => onChangeHandler(e.target.value)}
type={type}
placeholder={placeholder}
value={value}
required={isRequired}
className="bg-bunker-800 text-gray-400 border border-gray-600 rounded-md text-md p-2 w-full min-w-16 outline-none"
className="text-md min-w-16 w-full rounded-md border border-gray-600 bg-bunker-800 p-2 text-gray-400 outline-none"
name={name}
readOnly
autoComplete={autoComplete}
@@ -58,12 +58,12 @@ const InputField = ({
);
}
return (
<div className="flex-col w-full">
<div className="flex flex-row text-mineshaft-300 items-center mb-0.5">
<p className="text-sm font-semibold mr-1">{label}</p>
<div className="w-full flex-col">
<div className="mb-0.5 flex flex-row items-center text-mineshaft-300">
<p className="mr-1 text-sm font-semibold">{label}</p>
</div>
<div
className={`group relative flex flex-col justify-center w-full max-w-2xl border ${
className={`group relative flex w-full max-w-2xl flex-col justify-center border ${
error ? "border-red" : "border-mineshaft-500"
} rounded-md`}
>
@@ -75,11 +75,11 @@ const InputField = ({
required={isRequired}
className={`${
blurred
? "text-bunker-800 group-hover:text-gray-400 focus:text-gray-400 active:text-gray-400"
? "text-bunker-800 focus:text-gray-400 active:text-gray-400 group-hover:text-gray-400"
: ""
} ${
error ? "focus:ring-red/50" : "focus:ring-primary/50"
} relative peer bg-mineshaft-900 rounded-md text-gray-400 text-md p-2 w-full min-w-16 outline-none focus:ring-4 duration-200`}
} text-md min-w-16 peer relative w-full rounded-md bg-mineshaft-900 p-2 text-gray-400 outline-none duration-200 focus:ring-4`}
name={name}
spellCheck="false"
autoComplete={autoComplete}
@@ -91,7 +91,7 @@ const InputField = ({
onClick={() => {
setPasswordVisible(!passwordVisible);
}}
className="absolute self-end mr-3 text-gray-400 cursor-pointer"
className="absolute mr-3 cursor-pointer self-end text-gray-400"
>
{passwordVisible ? (
<FontAwesomeIcon icon={faEyeSlash} />
@@ -101,7 +101,7 @@ const InputField = ({
</button>
)}
{blurred && (
<div className="peer group-hover:hidden peer-hover:hidden peer-focus:hidden peer-active:invisible absolute h-10 w-fit max-w-xl rounded-md flex items-center text-gray-400/50 text-clip overflow-hidden">
<div className="peer absolute flex h-10 w-fit max-w-xl items-center overflow-hidden text-clip rounded-md text-gray-400/50 group-hover:hidden peer-hover:hidden peer-focus:hidden peer-active:invisible">
<p className="ml-2" />
{value
.split("")
@@ -109,7 +109,7 @@ const InputField = ({
.map(() => (
<FontAwesomeIcon
key={guidGenerator()}
className="text-xxs mx-0.5"
className="mx-0.5 text-xxs"
icon={faCircle}
/>
))}
@@ -121,7 +121,7 @@ const InputField = ({
</div>
)} */}
</div>
{error && <p className="text-red text-xs mt-0.5 mx-0 mb-2 max-w-xs">{errorText}</p>}
{error && <p className="mx-0 mt-0.5 mb-2 max-w-xs text-xs text-red">{errorText}</p>}
</div>
);
};

View File

@@ -34,19 +34,19 @@ const ListBox = ({
<Listbox value={isSelected} onChange={onChange}>
<div className="relative">
<Listbox.Button
className={`text-gray-400 relative ${
className={`relative text-gray-400 ${
isFull ? "w-full" : "w-52"
} cursor-default rounded-md bg-white/[0.07] hover:bg-white/[0.11] duration-200 py-2.5 pl-3 pr-10 text-left shadow-md focus:outline-none focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-orange-300 sm:text-sm`}
} focus-visible:ring-offset-orange-300 cursor-default rounded-md bg-white/[0.07] py-2.5 pl-3 pr-10 text-left shadow-md duration-200 hover:bg-white/[0.11] focus:outline-none focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 sm:text-sm`}
>
<div className="flex flex-row">
{text}
<span className="ml-1 cursor-pointer block truncate font-semibold text-gray-300">
<span className="ml-1 block cursor-pointer truncate font-semibold text-gray-300">
{" "}
{isSelected}
</span>
</div>
{data && (
<div className="cursor-pointer pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
<div className="pointer-events-none absolute inset-y-0 right-0 flex cursor-pointer items-center pr-2">
<FontAwesomeIcon icon={faAngleDown} className="text-md mr-1.5" />
</div>
)}
@@ -58,16 +58,16 @@ const ListBox = ({
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options className="border border-mineshaft-700 z-[70] p-2 absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-bunker text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm no-scrollbar no-scrollbar::-webkit-scrollbar">
<Listbox.Options className="no-scrollbar::-webkit-scrollbar absolute z-[70] mt-1 max-h-60 w-full overflow-auto rounded-md border border-mineshaft-700 bg-bunker p-2 text-base shadow-lg ring-1 ring-black ring-opacity-5 no-scrollbar focus:outline-none sm:text-sm">
{data.map((person, personIdx) => (
<Listbox.Option
key={`${person}.${personIdx + 1}`}
className={({ active, selected }) =>
`my-0.5 relative cursor-default select-none py-2 pl-10 pr-4 rounded-md ${
selected ? "bg-white/10 text-gray-400 font-bold" : ""
`relative my-0.5 cursor-default select-none rounded-md py-2 pl-10 pr-4 ${
selected ? "bg-white/10 font-bold text-gray-400" : ""
} ${
active && !selected
? "bg-white/5 text-mineshaft-200 cursor-pointer"
? "cursor-pointer bg-white/5 text-mineshaft-200"
: "text-gray-400"
} `
}
@@ -83,7 +83,7 @@ const ListBox = ({
{person}
</span>
{selected ? (
<span className="text-primary rounded-lg absolute inset-y-0 left-0 flex items-center pl-3">
<span className="absolute inset-y-0 left-0 flex items-center rounded-lg pl-3 text-primary">
<FontAwesomeIcon icon={faCheck} className="text-md ml-1" />
</span>
) : null}
@@ -92,9 +92,9 @@ const ListBox = ({
</Listbox.Option>
))}
{buttonAction && (
<button type="button" onClick={buttonAction} className="cursor-pointer w-full">
<div className="my-0.5 relative flex justify-start cursor-pointer select-none py-2 pl-10 pr-4 rounded-md text-gray-400 hover:bg-lime-300 duration-200 hover:text-black hover:font-semibold mt-2">
<span className="rounded-lg absolute inset-y-0 left-0 flex items-center pl-3 pr-4">
<button type="button" onClick={buttonAction} className="w-full cursor-pointer">
<div className="relative my-0.5 mt-2 flex cursor-pointer select-none justify-start rounded-md py-2 pl-10 pr-4 text-gray-400 duration-200 hover:bg-lime-300 hover:font-semibold hover:text-black">
<span className="absolute inset-y-0 left-0 flex items-center rounded-lg pl-3 pr-4">
<FontAwesomeIcon icon={faPlus} className="text-lg" />
</span>
Add Project

View File

@@ -43,7 +43,7 @@ const Button = ({
loading,
icon,
iconDisabled,
type = "button",
type = "button"
}: ButtonProps): JSX.Element => {
// Check if the button show always be 'active' - then true;
// or if it should switch between 'active' and 'disabled' - then give the status
@@ -53,9 +53,13 @@ const Button = ({
"group m-auto md:m-0 inline-block rounded-md duration-200",
// Setting background colors and hover modes
color === "mineshaft" && activityStatus && "bg-mineshaft-800 border border-mineshaft-600 hover:bg-primary/[0.15] hover:border-primary/60",
color === "mineshaft" &&
activityStatus &&
"bg-mineshaft-800 border border-mineshaft-600 hover:bg-primary/[0.15] hover:border-primary/60",
color === "mineshaft" && !activityStatus && "bg-mineshaft",
(color === "primary" || !color) && activityStatus && "bg-primary border border-primary-400 opacity-80 hover:opacity-100",
(color === "primary" || !color) &&
activityStatus &&
"bg-primary border border-primary-400 opacity-80 hover:opacity-100",
(color === "primary" || !color) && !activityStatus && "bg-primary",
color === "red" && "bg-red-800 border border-red",
@@ -78,7 +82,9 @@ const Button = ({
color !== "mineshaft" && color !== "red" && color !== "none" && "text-black",
color === "red" && "text-gray-200",
color === "none" && "text-gray-200 text-xl",
activityStatus && color !== "red" && color !== "mineshaft" && color !== "none" ? "group-hover:text-black" : "",
activityStatus && color !== "red" && color !== "mineshaft" && color !== "none"
? "group-hover:text-black"
: "",
size === "icon" && "flex items-center justify-center"
);
@@ -103,7 +109,7 @@ const Button = ({
<div
className={`${
loading === true ? "opacity-100" : "opacity-0"
} absolute flex items-center px-3 bg-primary duration-200 w-full`}
} absolute flex w-full items-center bg-primary px-3 duration-200`}
>
<Image
src="/images/loading/loadingblack.gif"
@@ -116,7 +122,7 @@ const Button = ({
{icon && (
<FontAwesomeIcon
icon={icon}
className={`flex my-auto font-extrabold ${size === "icon-sm" ? "text-sm" : "text-sm"} ${
className={`my-auto flex font-extrabold ${size === "icon-sm" ? "text-sm" : "text-sm"} ${
(text || textDisabled) && "mr-2"
}`}
/>
@@ -124,7 +130,7 @@ const Button = ({
{iconDisabled && (
<FontAwesomeIcon
icon={iconDisabled as IconProp}
className={`flex my-auto font-extrabold ${size === "icon-sm" ? "text-sm" : "text-md"} ${
className={`my-auto flex font-extrabold ${size === "icon-sm" ? "text-sm" : "text-md"} ${
(text || textDisabled) && "mr-2"
}`}
/>

View File

@@ -64,7 +64,7 @@ const AddProjectMemberDialog = ({
) : (
<Dialog.Title
as="h3"
className="z-50 text-lg font-medium text-mineshaft-300 mb-4"
className="z-50 mb-4 text-lg font-medium text-mineshaft-300"
>
{t("section.members.add-dialog.already-all-invited")}
</Dialog.Title>
@@ -127,7 +127,9 @@ const AddProjectMemberDialog = ({
</div>
) : (
<Button
onButtonPressed={() => router.push(`/org/${localStorage.getItem("orgData.id")}/members`)}
onButtonPressed={() =>
router.push(`/org/${localStorage.getItem("orgData.id")}/members`)
}
color="mineshaft"
text={t("section.members.add-dialog.add-user-to-org") as string}
size="md"

View File

@@ -28,11 +28,11 @@ export const AddUpdateEnvironmentDialog = ({
onCreateSubmit,
onEditSubmit,
initialValues,
isEditMode,
isEditMode
}: Props) => {
const [formInput, setFormInput] = useState<FormFields>({
name: "",
slug: "",
slug: ""
});
// This use effect can be removed when the unmount is happening from outside the component
@@ -50,7 +50,7 @@ export const AddUpdateEnvironmentDialog = ({
e.preventDefault();
const data = {
name: formInput.name,
slug: formInput.slug.toLowerCase(),
slug: formInput.slug.toLowerCase()
};
if (isEditMode) {
onEditSubmit(data);
@@ -62,75 +62,70 @@ export const AddUpdateEnvironmentDialog = ({
return (
<div>
<Transition appear show={isOpen} as={Fragment}>
<Dialog as='div' className='relative z-20' onClose={onClose}>
<Dialog as="div" className="relative z-20" onClose={onClose}>
<Transition.Child
as={Fragment}
enter='ease-out duration-300'
enterFrom='opacity-0'
enterTo='opacity-100'
leave='ease-out duration-150'
leaveFrom='opacity-100'
leaveTo='opacity-0'
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-out duration-150"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className='fixed inset-0 bg-black bg-opacity-70' />
<div className="fixed inset-0 bg-black bg-opacity-70" />
</Transition.Child>
<div className='fixed inset-0 overflow-y-auto z-50'>
<div className='flex min-h-full items-center justify-center p-4 text-center'>
<div className="fixed inset-0 z-50 overflow-y-auto">
<div className="flex min-h-full items-center justify-center p-4 text-center">
<Transition.Child
as={Fragment}
enter='ease-out duration-300'
enterFrom='opacity-0 scale-95'
enterTo='opacity-100 scale-100'
leave='ease-in duration-200'
leaveFrom='opacity-100 scale-100'
leaveTo='opacity-0 scale-95'
enter="ease-out duration-300"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<Dialog.Panel className='w-full max-w-md transform overflow-hidden rounded-md bg-bunker-800 border border-gray-700 p-6 text-left align-middle shadow-xl transition-all'>
<Dialog.Title
as='h3'
className='text-lg font-medium leading-6 text-gray-400'
>
{isEditMode
? "Update environment"
: "Create a new environment"}
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-md border border-gray-700 bg-bunker-800 p-6 text-left align-middle shadow-xl transition-all">
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-gray-400">
{isEditMode ? "Update environment" : "Create a new environment"}
</Dialog.Title>
<form onSubmit={onFormSubmit}>
<div className='max-h-28 mt-4'>
<div className="mt-4 max-h-28">
<InputField
label='Environment Name'
label="Environment Name"
onChangeHandler={(val) => onInputChange("name", val)}
type='varName'
type="varName"
value={formInput.name}
placeholder=''
placeholder=""
isRequired
// error={error.length > 0}
// errorText={error}
/>
</div>
<div className='max-h-28 mt-4'>
<div className="mt-4 max-h-28">
<InputField
label='Environment Slug'
label="Environment Slug"
onChangeHandler={(val) => onInputChange("slug", val)}
type='varName'
type="varName"
value={formInput.slug}
placeholder=''
placeholder=""
isRequired
// error={error.length > 0}
// errorText={error}
/>
</div>
<p className='text-xs text-gray-500 mt-2'>
<p className="mt-2 text-xs text-gray-500">
Slugs are shorthands used in cli to access environment
</p>
<div className='mt-4 max-w-min'>
<div className="mt-4 max-w-min">
<Button
onButtonPressed={() => null}
type='submit'
color='mineshaft'
type="submit"
color="mineshaft"
text={isEditMode ? "Update" : "Create"}
active={formInput.name !== "" && formInput.slug !== ""}
size='md'
size="md"
/>
</div>
</form>

View File

@@ -13,76 +13,63 @@ type Props = {
orgName: string;
};
const AddUserDialog = ({
isOpen,
closeModal,
submitModal,
email,
setEmail,
orgName,
}: Props) => {
const AddUserDialog = ({ isOpen, closeModal, submitModal, email, setEmail, orgName }: Props) => {
const submit = () => {
submitModal(email);
};
return (
<div className='z-50'>
<div className="z-50">
<Transition appear show={isOpen} as={Fragment}>
<Dialog as='div' className='relative' onClose={closeModal}>
<Dialog as="div" className="relative" onClose={closeModal}>
<Transition.Child
as={Fragment}
enter='ease-out duration-300'
enterFrom='opacity-0'
enterTo='opacity-100'
leave='ease-in duration-200'
leaveFrom='opacity-100'
leaveTo='opacity-0'
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className='fixed inset-0 bg-black bg-opacity-70' />
<div className="fixed inset-0 bg-black bg-opacity-70" />
</Transition.Child>
<div className='fixed inset-0 overflow-y-auto'>
<div className='flex min-h-full items-center justify-center p-4 text-center'>
<div className="fixed inset-0 overflow-y-auto">
<div className="flex min-h-full items-center justify-center p-4 text-center">
<Transition.Child
as={Fragment}
enter='ease-out duration-300'
enterFrom='opacity-0 scale-95'
enterTo='opacity-100 scale-100'
leave='ease-in duration-200'
leaveFrom='opacity-100 scale-100'
leaveTo='opacity-0 scale-95'
enter="ease-out duration-300"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<Dialog.Panel className='w-full max-w-lg transform overflow-hidden rounded-md bg-bunker-800 border border-gray-700 p-6 text-left align-middle shadow-xl transition-all'>
<Dialog.Panel className="w-full max-w-lg transform overflow-hidden rounded-md border border-gray-700 bg-bunker-800 p-6 text-left align-middle shadow-xl transition-all">
<Dialog.Title
as='h3'
className='text-lg font-medium leading-6 text-gray-400 z-50'
as="h3"
className="z-50 text-lg font-medium leading-6 text-gray-400"
>
Invite others to {orgName}
</Dialog.Title>
<div className='mt-2 mb-4'>
<p className='text-sm text-gray-500'>
An invite is specific to an email address and expires
after 1 day. For security reasons, you will need to
separately add members to projects.
<div className="mt-2 mb-4">
<p className="text-sm text-gray-500">
An invite is specific to an email address and expires after 1 day. For
security reasons, you will need to separately add members to projects.
</p>
</div>
<div className='max-h-28'>
<div className="max-h-28">
<InputField
label='Email'
label="Email"
onChangeHandler={setEmail}
type='varName'
type="varName"
value={email}
placeholder=''
placeholder=""
isRequired
/>
</div>
<div className='mt-4 max-w-max'>
<Button
onButtonPressed={submit}
color='mineshaft'
text='Invite'
size='md'
/>
<div className="mt-4 max-w-max">
<Button onButtonPressed={submit} color="mineshaft" text="Invite" size="md" />
</div>
</Dialog.Panel>
{/* <Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-md bg-bunker-800 border border-gray-700 p-6 text-left align-middle shadow-xl transition-all">

View File

@@ -5,7 +5,6 @@ import Button from "../buttons/Button";
import InputField from "../InputField";
import { Checkbox } from "../table/Checkbox";
type Props = {
isOpen: boolean;
closeModal: () => void;
@@ -26,8 +25,8 @@ const AddWorkspaceDialog = ({
workspaceName,
setWorkspaceName,
error,
loading,
}:Props) => {
loading
}: Props) => {
const [addAllUsers, setAddAllUsers] = useState(true);
const submit = () => {
submitModal(workspaceName, addAllUsers);
@@ -60,11 +59,8 @@ const AddWorkspaceDialog = ({
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-md bg-bunker-800 border border-gray-700 p-6 text-left align-middle shadow-xl transition-all">
<Dialog.Title
as="h3"
className="text-lg font-medium leading-6 text-gray-400"
>
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-md border border-gray-700 bg-bunker-800 p-6 text-left align-middle shadow-xl transition-all">
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-gray-400">
Create a new project
</Dialog.Title>
<div className="mt-2">
@@ -72,7 +68,7 @@ const AddWorkspaceDialog = ({
This project will contain your secrets and configs.
</p>
</div>
<div className="max-h-28 mt-4">
<div className="mt-4 max-h-28">
<InputField
label="Project Name"
onChangeHandler={setWorkspaceName}
@@ -84,10 +80,7 @@ const AddWorkspaceDialog = ({
/>
</div>
<div className="mt-4 ml-1">
<Checkbox
addAllUsers={addAllUsers}
setAddAllUsers={setAddAllUsers}
/>
<Checkbox addAllUsers={addAllUsers} setAddAllUsers={setAddAllUsers} />
</div>
<div className="mt-4 max-w-min">
<Button

View File

@@ -3,89 +3,76 @@ import { Dialog, Transition } from "@headlessui/react";
import InputField from "../InputField";
// REFACTOR: Move all these modals into one reusable one
// REFACTOR: Move all these modals into one reusable one
type Props = {
isOpen?: boolean;
onClose: ()=>void;
onClose: () => void;
title: string;
onSubmit:()=>void;
deleteKey?:string;
}
onSubmit: () => void;
deleteKey?: string;
};
const DeleteActionModal = ({
isOpen,
onClose,
title,
onSubmit,
deleteKey
}:Props) => {
const [deleteInputField, setDeleteInputField] = useState("")
const DeleteActionModal = ({ isOpen, onClose, title, onSubmit, deleteKey }: Props) => {
const [deleteInputField, setDeleteInputField] = useState("");
useEffect(() => {
setDeleteInputField("");
}, [isOpen]);
useEffect(() => {
setDeleteInputField("");
}, [isOpen]);
return (
<div>
<Transition appear show={isOpen} as={Fragment}>
<Dialog as='div' className='relative z-10' onClose={onClose}>
<Dialog as="div" className="relative z-10" onClose={onClose}>
<Transition.Child
as={Fragment}
enter='ease-out duration-300'
enterFrom='opacity-0'
enterTo='opacity-100'
leave='ease-in duration-150'
leaveFrom='opacity-100'
leaveTo='opacity-0'
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-150"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className='fixed inset-0 bg-black bg-opacity-70' />
<div className="fixed inset-0 bg-black bg-opacity-70" />
</Transition.Child>
<div className='fixed inset-0 overflow-y-auto'>
<div className='flex min-h-full items-center justify-center p-4 text-center'>
<div className="fixed inset-0 overflow-y-auto">
<div className="flex min-h-full items-center justify-center p-4 text-center">
<Transition.Child
as={Fragment}
enter='ease-out duration-300'
enterFrom='opacity-0 scale-95'
enterTo='opacity-100 scale-100'
leave='ease-in duration-200'
leaveFrom='opacity-100 scale-100'
leaveTo='opacity-0 scale-95'
enter="ease-out duration-300"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<Dialog.Panel className='w-full max-w-md transform overflow-hidden rounded-md bg-grey border border-gray-700 p-6 text-left align-middle shadow-xl transition-all'>
<Dialog.Title
as='h3'
className='text-lg font-medium leading-6 text-gray-400'
>
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-md border border-gray-700 bg-grey p-6 text-left align-middle shadow-xl transition-all">
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-gray-400">
{title}
</Dialog.Title>
<div className='mt-2'>
<p className='text-sm text-gray-500'>
This action is irrevertible.
</p>
<div className="mt-2">
<p className="text-sm text-gray-500">This action is irrevertible.</p>
</div>
<div className='mt-2'>
<div className="mt-2">
<InputField
isRequired
label={`Type ${deleteKey} to delete the resource`}
onChangeHandler={(val) => setDeleteInputField(val)}
value={deleteInputField}
type='text'
type="text"
/>
</div>
<div className='mt-6'>
<div className="mt-6">
<button
type='button'
className='inline-flex justify-center rounded-md border border-transparent bg-gray-800 px-4 py-2 text-sm font-medium text-gray-400 hover:bg-alizarin hover:text-white hover:text-semibold duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2'
type="button"
className="hover:bg-alizarin hover:text-semibold inline-flex justify-center rounded-md border border-transparent bg-gray-800 px-4 py-2 text-sm font-medium text-gray-400 duration-200 hover:text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
onClick={onSubmit}
disabled={
Boolean(deleteKey) && deleteInputField !== deleteKey
}
disabled={Boolean(deleteKey) && deleteInputField !== deleteKey}
>
Delete
</button>
<button
type='button'
className='ml-2 inline-flex justify-center rounded-md border border-transparent bg-gray-800 px-4 py-2 text-sm font-medium text-gray-400 hover:border-white hover:text-white hover:text-semibold duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2'
type="button"
className="hover:text-semibold ml-2 inline-flex justify-center rounded-md border border-transparent bg-gray-800 px-4 py-2 text-sm font-medium text-gray-400 duration-200 hover:border-white hover:text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
onClick={onClose}
>
Cancel

View File

@@ -5,13 +5,13 @@ import { Dialog, Transition } from "@headlessui/react";
// #TODO: USE THIS. Currently it's not. Kinda complicated to set up because of state.
type Props = {
isOpen: boolean
onClose: () => void
onSubmit: () => void
}
isOpen: boolean;
onClose: () => void;
onSubmit: () => void;
};
export const DeleteEnvVar = ({ isOpen, onClose, onSubmit }: Props) => {
const { t } = useTranslation()
const { t } = useTranslation();
return (
<div>
<Transition appear show={isOpen} as={Fragment}>
@@ -45,7 +45,7 @@ export const DeleteEnvVar = ({ isOpen, onClose, onSubmit }: Props) => {
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-md bg-bunker border border-mineshaft-600 p-6 text-left align-middle shadow-xl transition-all">
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-md border border-mineshaft-600 bg-bunker p-6 text-left align-middle shadow-xl transition-all">
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-bunker-200">
{t("dashboard:sidebar.delete-key-dialog.title")}
</Dialog.Title>
@@ -57,14 +57,14 @@ export const DeleteEnvVar = ({ isOpen, onClose, onSubmit }: Props) => {
<div className="mt-6 flex justify-start">
<button
type="button"
className="inline-flex justify-center rounded-md border border-transparent bg-red-500 opacity-80 hover:opacity-100 px-4 py-2 text-sm font-medium text-bunker-100 text-semibold duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
className="text-semibold inline-flex justify-center rounded-md border border-transparent bg-red-500 px-4 py-2 text-sm font-medium text-bunker-100 opacity-80 duration-200 hover:opacity-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
onClick={onSubmit}
>
Delete
</button>
<button
type="button"
className="ml-2 inline-flex justify-center rounded-md border border-transparent bg-bunker-500 px-4 py-2 text-sm font-medium text-gray-400 hover:bg-mineshaft-500 hover:text-white hover:text-semibold duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
className="hover:text-semibold ml-2 inline-flex justify-center rounded-md border border-transparent bg-bunker-500 px-4 py-2 text-sm font-medium text-gray-400 duration-200 hover:bg-mineshaft-500 hover:text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
onClick={onClose}
>
Cancel

View File

@@ -10,66 +10,55 @@ type Props = {
userIdToBeDeleted: string;
};
const DeleteUserDialog = ({
isOpen,
closeModal,
submitModal,
userIdToBeDeleted,
}: Props) => {
const DeleteUserDialog = ({ isOpen, closeModal, submitModal, userIdToBeDeleted }: Props) => {
const submit = () => {
submitModal(userIdToBeDeleted);
};
return (
<div>
<Transition appear show={isOpen} as={Fragment}>
<Dialog as='div' className='relative z-10' onClose={closeModal}>
<Dialog as="div" className="relative z-10" onClose={closeModal}>
<Transition.Child
as={Fragment}
enter='ease-out duration-300'
enterFrom='opacity-0'
enterTo='opacity-100'
leave='ease-in duration-200'
leaveFrom='opacity-100'
leaveTo='opacity-0'
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className='fixed inset-0 bg-black bg-opacity-25' />
<div className="fixed inset-0 bg-black bg-opacity-25" />
</Transition.Child>
<div className='fixed inset-0 overflow-y-auto'>
<div className='flex min-h-full items-center justify-center p-4 text-center'>
<div className="fixed inset-0 overflow-y-auto">
<div className="flex min-h-full items-center justify-center p-4 text-center">
<Transition.Child
as={Fragment}
enter='ease-out duration-300'
enterFrom='opacity-0 scale-95'
enterTo='opacity-100 scale-100'
leave='ease-in duration-200'
leaveFrom='opacity-100 scale-100'
leaveTo='opacity-0 scale-95'
enter="ease-out duration-300"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<Dialog.Panel className='w-full max-w-md transform overflow-hidden rounded-2xl bg-grey border border-gray-700 p-6 text-left align-middle shadow-xl transition-all'>
<Dialog.Title
as='h3'
className='text-lg font-medium leading-6 text-gray-400'
>
Are you sure you want to remove this user from the
workspace?
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-2xl border border-gray-700 bg-grey p-6 text-left align-middle shadow-xl transition-all">
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-gray-400">
Are you sure you want to remove this user from the workspace?
</Dialog.Title>
<div className='mt-2'>
<p className='text-sm text-gray-500'>
This action is irrevertible.
</p>
<div className="mt-2">
<p className="text-sm text-gray-500">This action is irrevertible.</p>
</div>
<div className='mt-6'>
<div className="mt-6">
<button
type='button'
className='inline-flex justify-center rounded-md border border-transparent bg-gray-800 px-4 py-2 text-sm font-medium text-gray-400 hover:bg-alizarin hover:text-white hover:text-semibold duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2'
type="button"
className="hover:bg-alizarin hover:text-semibold inline-flex justify-center rounded-md border border-transparent bg-gray-800 px-4 py-2 text-sm font-medium text-gray-400 duration-200 hover:text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
onClick={submit}
>
Delete
</button>
<button
type='button'
className='ml-2 inline-flex justify-center rounded-md border border-transparent bg-gray-800 px-4 py-2 text-sm font-medium text-gray-400 hover:border-white hover:text-white hover:text-semibold duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2'
type="button"
className="hover:text-semibold ml-2 inline-flex justify-center rounded-md border border-transparent bg-gray-800 px-4 py-2 text-sm font-medium text-gray-400 duration-200 hover:border-white hover:text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
onClick={submit}
>
Cancel

View File

@@ -34,27 +34,27 @@ const BottonRightPopup = ({
}: PopupProps): JSX.Element => {
return (
<div
className="z-[100] drop-shadow-xl border-gray-600/50 border flex flex-col items-start bg-bunker max-w-xl text-gray-200 pt-3 pb-4 rounded-md absolute bottom-0 right-0 mr-6 mb-6"
className="absolute bottom-0 right-0 z-[100] mr-6 mb-6 flex max-w-xl flex-col items-start rounded-md border border-gray-600/50 bg-bunker pt-3 pb-4 text-gray-200 drop-shadow-xl"
role="alert"
>
<div className="flex flex-row items-center justify-between w-full border-b border-gray-600/70 pb-3 px-6">
<div className="font-bold text-xl mr-2 mt-0.5 flex flex-row">
<div className="flex w-full flex-row items-center justify-between border-b border-gray-600/70 px-6 pb-3">
<div className="mr-2 mt-0.5 flex flex-row text-xl font-bold">
<div>{titleText}</div>
<div className="ml-2.5">{emoji}</div>
</div>
<button className="mt-1" onClick={() => setCheckDocsPopUpVisible(false)} type="button">
<FontAwesomeIcon
icon={faXmark}
className="text-gray-400 text-2xl hover:text-red duration-200 cursor-pointer"
className="cursor-pointer text-2xl text-gray-400 duration-200 hover:text-red"
/>
</button>
</div>
<div className="block sm:inline px-6 mt-4 mb-0.5 text-gray-300">{textLine1}</div>
<div className="block sm:inline mb-4 px-6">{textLine2}</div>
<div className="flex flex-row px-6 w-full">
<div className="mt-4 mb-0.5 block px-6 text-gray-300 sm:inline">{textLine1}</div>
<div className="mb-4 block px-6 sm:inline">{textLine2}</div>
<div className="flex w-full flex-row px-6">
{/* eslint-disable-next-line react/jsx-no-target-blank */}
<a
className="font-bold p-2 bg-white/10 rounded-md w-full hover:bg-primary duration-200 hover:text-black flex justify-center"
className="flex w-full justify-center rounded-md bg-white/10 p-2 font-bold duration-200 hover:bg-primary hover:text-black"
href={buttonLink}
target="_blank"
rel="noopener"

View File

@@ -9,7 +9,7 @@ export const Checkbox = ({ addAllUsers, setAddAllUsers }: Props) => (
{addAllUsers === true ? (
<input
type="checkbox"
className="accent-primary h-4 w-4"
className="h-4 w-4 accent-primary"
checked
readOnly
onClick={() => setAddAllUsers(!addAllUsers)}
@@ -20,12 +20,12 @@ export const Checkbox = ({ addAllUsers, setAddAllUsers }: Props) => (
role="button"
tabIndex={0}
aria-label="add all users"
className="h-4 w-4 bg-bunker border border-gray-600 rounded-sm"
className="h-4 w-4 rounded-sm border border-gray-600 bg-bunker"
onClick={() => setAddAllUsers(!addAllUsers)}
/>
)}
<label className="ml-2 text-gray-500 text-sm">
<label className="ml-2 text-sm text-gray-500">
Add all members of my organization to this project.
</label>
</div>

View File

@@ -36,25 +36,28 @@ const Notification = ({ notification, clearNotification }: NotificationProps) =>
return (
<div
className="relative w-full flex items-center justify-between px-6 py-4 rounded-md border border-bunker-500 pointer-events-auto bg-mineshaft-700 mb-3 right-3"
className="pointer-events-auto relative right-3 mb-3 flex w-full items-center justify-between rounded-md border border-bunker-500 bg-mineshaft-700 px-6 py-4"
role="alert"
>
{notification.type === "error" && (
<div className="absolute w-full h-1 bg-red top-0 left-0 rounded-t-md" />
<div className="absolute top-0 left-0 h-1 w-full rounded-t-md bg-red" />
)}
{notification.type === "success" && (
<div className="absolute w-full h-1 bg-green top-0 left-0 rounded-t-md" />
<div className="absolute top-0 left-0 h-1 w-full rounded-t-md bg-green" />
)}
{notification.type === "info" && (
<div className="absolute w-full h-1 bg-yellow top-0 left-0 rounded-t-md" />
<div className="absolute top-0 left-0 h-1 w-full rounded-t-md bg-yellow" />
)}
<p className="text-bunker-200 text-md font-base mt-0.5">{notification.text}</p>
<p className="text-md font-base mt-0.5 text-bunker-200">{notification.text}</p>
<button
type="button"
className="rounded-lg"
onClick={() => clearNotification(notification.text)}
>
<FontAwesomeIcon className="absolute right-2 top-3 text-bunker-300 pl-2 w-4 h-4 hover:text-white" icon={faXmark} />
<FontAwesomeIcon
className="absolute right-2 top-3 h-4 w-4 pl-2 text-bunker-300 hover:text-white"
icon={faXmark}
/>
</button>
</div>
);

View File

@@ -11,7 +11,7 @@ const Notifications = ({ notifications, clearNotification }: NoticationsProps) =
}
return (
<div className="hidden fixed z-50 md:flex md:flex-col-reverse gap-y-2 w-96 h-full right-2 bottom-2 pointer-events-none">
<div className="pointer-events-none fixed right-2 bottom-2 z-[100] hidden h-full w-96 gap-y-2 md:flex md:flex-col-reverse">
{notifications.map((notif) => (
<Notification key={notif.text} notification={notif} clearNotification={clearNotification} />
))}

View File

@@ -30,9 +30,9 @@ const ConfirmEnvOverwriteModal = ({
onClose={onClose}
>
<div className="flex flex-col gap-2">
<p className='text-gray-400'>Your file contains the following duplicate secrets:</p>
<p className="text-gray-400">Your file contains the following duplicate secrets:</p>
<p className="text-sm text-gray-500">{duplicateKeys.join(", ")}</p>
<p className='text-md text-gray-400'>Are you sure you want to overwrite these secrets?</p>
<p className="text-md text-gray-400">Are you sure you want to overwrite these secrets?</p>
</div>
</ModalContent>
</Modal>

View File

@@ -1,5 +1,10 @@
import { memo, SyntheticEvent, useRef } from "react";
import { faCircle, faCodeBranch, faExclamationCircle, faEye } from "@fortawesome/free-solid-svg-icons";
import {
faCircle,
faCodeBranch,
faExclamationCircle,
faEye
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import guidGenerator from "../utilities/randomId";
@@ -31,8 +36,8 @@ interface DashboardInputFieldProps {
* @param {boolean} obj.blurred - whether the input field should be blurred (behind the gray dots) or not; this can be turned on/off in the dashboard
* @param {boolean} obj.isDuplicate - if the key name is duplicated
* @param {boolean} obj.override - whether a secret/row should be displalyed as overriden
*
*
*
*
* @returns
*/
@@ -61,29 +66,31 @@ const DashboardInputField = ({
const error = startsWithNumber || isDuplicate;
return (
<div className={`relative flex-col w-full h-10 ${
error && value !== "" ? "bg-red/[0.15]" : ""
} ${
isSideBarOpen && "bg-mineshaft-700 duration-200"
}`}>
<div
className={`relative h-10 w-full flex-col ${error && value !== "" ? "bg-red/[0.15]" : ""} ${
isSideBarOpen && "bg-mineshaft-700 duration-200"
}`}
>
<div
className={`group relative flex flex-col justify-center items-center h-full ${
className={`group relative flex h-full flex-col items-center justify-center ${
error ? "w-max" : "w-full"
}`}
>
<input
onChange={(e) => onChangeHandler(isCapitalized ? e.target.value.toUpperCase() : e.target.value, id)}
onChange={(e) =>
onChangeHandler(isCapitalized ? e.target.value.toUpperCase() : e.target.value, id)
}
type={type}
value={value}
className={`z-10 peer font-mono ph-no-capture bg-transparent h-full caret-bunker-200 text-sm px-2 w-full min-w-16 outline-none ${
className={`ph-no-capture min-w-16 peer z-10 h-full w-full bg-transparent px-2 font-mono text-sm caret-bunker-200 outline-none ${
error ? "text-red-600 focus:text-red-500" : "text-bunker-300 focus:text-bunker-100"
} duration-200`}
spellCheck="false"
/>
</div>
{startsWithNumber && (
<div className='absolute right-2 top-2 text-red z-50'>
<HoverObject
<div className="absolute right-2 top-2 z-50 text-red">
<HoverObject
text="Secret names should not start with a number"
icon={faExclamationCircle}
color="red"
@@ -91,33 +98,44 @@ const DashboardInputField = ({
</div>
)}
{isDuplicate && value !== "" && !startsWithNumber && (
<div className='absolute right-2 top-2 text-red z-50'>
<HoverObject
<div className="absolute right-2 top-2 z-50 text-red">
<HoverObject
text="Secret names should be unique"
icon={faExclamationCircle}
color="red"
/>
</div>
)}
{!error && <div className={`absolute right-0 top-0 text-red z-50 bg-mineshaft-800 group-hover:bg-mineshaft-700 ${
overrideEnabled ? "visible" : "invisible group-hover:visible"
} cursor-pointer duration-0 h-10 flex items-center px-2`}>
<button type="button" onClick={() => {
if (modifyValueOverride) {
if (overrideEnabled === false) {
modifyValueOverride("", id);
} else {
modifyValueOverride(undefined, id);
}
}
}}>
<HoverObject
text={overrideEnabled ? "This secret is overriden with your personal value" : "You can override this secret with a personal value"}
icon={faCodeBranch}
color={overrideEnabled ? "primary" : "bunker-400"}
/>
</button>
</div>}
{!error && (
<div
className={`absolute right-0 top-0 z-50 bg-mineshaft-800 text-red group-hover:bg-mineshaft-700 ${
overrideEnabled ? "visible" : "invisible group-hover:visible"
} duration-0 flex h-10 cursor-pointer items-center px-2`}
>
<button
type="button"
onClick={() => {
if (modifyValueOverride) {
if (overrideEnabled === false) {
modifyValueOverride("", id);
} else {
modifyValueOverride(undefined, id);
}
}
}}
>
<HoverObject
text={
overrideEnabled
? "This secret is overriden with your personal value"
: "You can override this secret with a personal value"
}
icon={faCodeBranch}
color={overrideEnabled ? "primary" : "bunker-400"}
/>
</button>
</div>
)}
</div>
);
}
@@ -127,20 +145,29 @@ const DashboardInputField = ({
return (
<PopoverObject text={value || ""} onChangeHandler={onChangeHandler} id={id}>
<div title={value} className={`relative flex-col w-full h-10 overflow-hidden ${
isSideBarOpen && "bg-mineshaft-700 duration-200"
}`}>
<div
title={value}
className={`relative h-10 w-full flex-col overflow-hidden ${
isSideBarOpen && "bg-mineshaft-700 duration-200"
}`}
>
<div
className={`group relative flex flex-col justify-center items-center h-full ${
className={`group relative flex h-full flex-col items-center justify-center ${
error ? "w-max" : "w-full"
}`}
>
{value?.split("\n")[0] ? <span className='ph-no-capture truncate break-all bg-transparent leading-tight text-xs px-2 w-full min-w-16 outline-none text-bunker-300 focus:text-bunker-100 placeholder:text-bunker-400 placeholder:focus:text-transparent placeholder duration-200'>
{value?.split("\n")[0]}
</span> : <span className='text-bunker-400'>-</span> }
{value?.split("\n")[1] && <span className='ph-no-capture truncate break-all bg-transparent leading-tight text-xs px-2 w-full min-w-16 outline-none text-bunker-300 focus:text-bunker-100 placeholder:text-bunker-400 placeholder:focus:text-transparent placeholder duration-200'>
{value?.split("\n")[1]}
</span>}
{value?.split("\n")[0] ? (
<span className="ph-no-capture min-w-16 placeholder w-full truncate break-all bg-transparent px-2 text-xs leading-tight text-bunker-300 outline-none duration-200 placeholder:text-bunker-400 focus:text-bunker-100 placeholder:focus:text-transparent">
{value?.split("\n")[0]}
</span>
) : (
<span className="text-bunker-400">-</span>
)}
{value?.split("\n")[1] && (
<span className="ph-no-capture min-w-16 placeholder w-full truncate break-all bg-transparent px-2 text-xs leading-tight text-bunker-300 outline-none duration-200 placeholder:text-bunker-400 focus:text-bunker-100 placeholder:focus:text-transparent">
{value?.split("\n")[1]}
</span>
)}
</div>
</div>
</PopoverObject>
@@ -148,10 +175,10 @@ const DashboardInputField = ({
}
if (type === "value") {
return (
<div className="flex-col w-full">
<div className="group relative whitespace-pre flex flex-col justify-center w-full">
<div className="w-full flex-col">
<div className="group relative flex w-full flex-col justify-center whitespace-pre">
{overrideEnabled === true && (
<div className="bg-primary-500 rounded-sm absolute top-[0.1rem] right-[0.1rem] z-0 w-min text-xxs px-1 text-black opacity-80">
<div className="absolute top-[0.1rem] right-[0.1rem] z-0 w-min rounded-sm bg-primary-500 px-1 text-xxs text-black opacity-80">
Override enabled
</div>
)}
@@ -160,20 +187,20 @@ const DashboardInputField = ({
onChange={(e) => onChangeHandler(e.target.value, id)}
onScroll={syncScroll}
className={`${
blurred
? "text-transparent focus:text-transparent active:text-transparent"
: ""
} z-10 peer font-mono ph-no-capture bg-transparent caret-white text-transparent text-sm px-2 py-2 w-full min-w-16 outline-none duration-200 no-scrollbar no-scrollbar::-webkit-scrollbar`}
blurred ? "text-transparent focus:text-transparent active:text-transparent" : ""
} ph-no-capture min-w-16 no-scrollbar::-webkit-scrollbar peer z-10 w-full bg-transparent px-2 py-2 font-mono text-sm text-transparent caret-white outline-none duration-200 no-scrollbar`}
spellCheck="false"
/>
<div
ref={ref}
className={`${
blurred && !overrideEnabled
? "text-bunker-800 group-hover:text-gray-400 peer-focus:text-gray-100 peer-active:text-gray-400 duration-200"
? "text-bunker-800 duration-200 group-hover:text-gray-400 peer-focus:text-gray-100 peer-active:text-gray-400"
: ""
} ${overrideEnabled ? "text-primary-300" : "text-gray-400"}
absolute flex flex-row whitespace-pre font-mono z-0 ${blurred ? "invisible" : "visible"} peer-focus:visible mt-0.5 ph-no-capture overflow-x-scroll bg-transparent h-10 text-sm px-2 py-2 w-full min-w-16 outline-none duration-100 no-scrollbar no-scrollbar::-webkit-scrollbar`}
absolute z-0 flex flex-row whitespace-pre font-mono ${
blurred ? "invisible" : "visible"
} ph-no-capture min-w-16 no-scrollbar::-webkit-scrollbar mt-0.5 h-10 w-full overflow-x-scroll bg-transparent px-2 py-2 text-sm outline-none duration-100 no-scrollbar peer-focus:visible`}
>
{value?.split(REGEX).map((word) => {
if (word.match(REGEX) !== null) {
@@ -203,20 +230,24 @@ const DashboardInputField = ({
})}
</div>
{blurred && (
<div className={`absolute flex flex-row justify-between items-center z-0 peer pr-2 ${
isSideBarOpen ? "bg-mineshaft-700 duration-200" : "bg-mineshaft-800"
} peer-active:hidden peer-focus:hidden group-hover:bg-white/[0.00] duration-100 h-10 w-full text-bunker-400 text-clip`}>
<div className="px-2 flex flex-row items-center overflow-x-scroll no-scrollbar no-scrollbar::-webkit-scrollbar">
<div
className={`peer absolute z-0 flex flex-row items-center justify-between pr-2 ${
isSideBarOpen ? "bg-mineshaft-700 duration-200" : "bg-mineshaft-800"
} h-10 w-full text-clip text-bunker-400 duration-100 group-hover:bg-white/[0.00] peer-focus:hidden peer-active:hidden`}
>
<div className="no-scrollbar::-webkit-scrollbar flex flex-row items-center overflow-x-scroll px-2 no-scrollbar">
{value?.split("").map(() => (
<FontAwesomeIcon
key={guidGenerator()}
className="text-xxs mr-0.5"
className="mr-0.5 text-xxs"
icon={faCircle}
/>
))}
{value?.split("").length === 0 && <span className='text-bunker-400/80'>EMPTY</span>}
{value?.split("").length === 0 && <span className="text-bunker-400/80">EMPTY</span>}
</div>
<div className="invisible z-[100] cursor-default group-hover:visible">
<FontAwesomeIcon icon={faEye} />
</div>
<div className='invisible group-hover:visible cursor-default z-[100]'><FontAwesomeIcon icon={faEye} /></div>
</div>
)}
</div>

View File

@@ -1,4 +1,4 @@
import React from "react"
import React from "react";
import { useTranslation } from "react-i18next";
import { faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
@@ -8,32 +8,35 @@ import Button from "../basic/buttons/Button";
type Props = {
onSubmit: () => void;
isPlain?: boolean;
}
};
export const DeleteActionButton = ({ onSubmit, isPlain }: Props) => {
const { t } = useTranslation();
return (
<div className={`${
!isPlain
? "bg-[#9B3535] opacity-70 hover:opacity-100 w-[4.5rem] h-[2.5rem] rounded-md duration-200 ml-2"
: "cursor-pointer w-[1.5rem] h-[2.35rem] mr-2 flex items-center justfy-center"}`}>
{isPlain
? <div
onKeyDown={() => null}
role="button"
tabIndex={0}
onClick={onSubmit}
className="invisible group-hover:visible"
>
<FontAwesomeIcon className="text-bunker-300 hover:text-red pl-2 pr-6 text-lg mt-0.5" icon={faXmark} />
</div>
: <Button
text={String(t("Delete"))}
color="red"
size="md"
onButtonPressed={onSubmit}
/>}
<div
className={`${
!isPlain
? "ml-2 h-[2.5rem] w-[4.5rem] rounded-md bg-[#9B3535] opacity-70 duration-200 hover:opacity-100"
: "justfy-center mr-2 flex h-[2.35rem] w-[1.5rem] cursor-pointer items-center"
}`}
>
{isPlain ? (
<div
onKeyDown={() => null}
role="button"
tabIndex={0}
onClick={onSubmit}
className="invisible group-hover:visible"
>
<FontAwesomeIcon
className="mt-0.5 pl-2 pr-6 text-lg text-bunker-300 hover:text-red"
icon={faXmark}
/>
</div>
) : (
<Button text={String(t("Delete"))} color="red" size="md" onButtonPressed={onSubmit} />
)}
</div>
)
}
);
};

View File

@@ -18,7 +18,7 @@ const DownloadSecretMenu = ({ data, env }: { data: SecretDataProps[]; env: strin
<Menu as="div" className="relative inline-block text-left">
<Menu.Button
as="div"
className="inline-flex w-full justify-center text-sm font-medium text-gray-200 rounded-md hover:bg-white/10 duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75"
className="inline-flex w-full justify-center rounded-md text-sm font-medium text-gray-200 duration-200 hover:bg-white/10 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75"
>
<Button color="mineshaft" size="icon-md" icon={faDownload} onButtonPressed={() => {}} />
</Menu.Button>
@@ -31,7 +31,7 @@ const DownloadSecretMenu = ({ data, env }: { data: SecretDataProps[]; env: strin
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items className="absolute z-[90] drop-shadow-xl right-0 mt-0.5 w-[12rem] origin-top-right rounded-md bg-bunker border border-mineshaft-500 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none p-2 space-y-2">
<Menu.Items className="absolute right-0 z-[90] mt-0.5 w-[12rem] origin-top-right space-y-2 rounded-md border border-mineshaft-500 bg-bunker p-2 shadow-lg ring-1 ring-black ring-opacity-5 drop-shadow-xl focus:outline-none">
<Menu.Item>
<Button
color="mineshaft"

View File

@@ -56,7 +56,7 @@ export default function NavHeader({
return (
<div className="flex flex-row items-center pt-6">
<div className="mr-2 flex h-5 w-5 items-center justify-center rounded-md bg-primary text-sm text-black min-w-[1.25rem]">
<div className="mr-2 flex h-5 w-5 min-w-[1.25rem] items-center justify-center rounded-md bg-primary text-sm text-black">
{currentOrg?.name?.charAt(0)}
</div>
<Link passHref legacyBehavior href={`/org/${currentOrg?.id}/overview`}>

View File

@@ -6,7 +6,6 @@ import { useOrganization, useWorkspace } from "@app/context";
import { Select, SelectItem, Tooltip } from "../v2";
/**
* This is the component at the top of almost every page.
* It shows how to navigate to a certain page.
@@ -39,10 +38,12 @@ export default function NavHeaderSecrets({
}): JSX.Element {
const { currentWorkspace } = useWorkspace();
const { currentOrg } = useOrganization();
const router = useRouter()
const router = useRouter();
return (
<div className={`${!isSnapshot && "absolute"} ml-6 flex flex-row items-center pt-6 cursor-default`}>
<div
className={`${!isSnapshot && "absolute"} ml-6 flex cursor-default flex-row items-center pt-6`}
>
<div className="mr-3 flex h-6 w-6 items-center justify-center rounded-md bg-primary-900 text-mineshaft-100">
{currentOrg?.name?.charAt(0)}
</div>
@@ -60,31 +61,39 @@ export default function NavHeaderSecrets({
</>
)}
<FontAwesomeIcon icon={faAngleRight} className="ml-3 mr-3 text-sm text-gray-400" />
{pageName === "Secrets"
? <a className="text-md font-medium text-primary/80 hover:text-primary" href={`${router.asPath.split("?")[0]}`}>{pageName}</a>
: <div className="text-md text-gray-400">{pageName}</div>}
{currentEnv &&
<>
<FontAwesomeIcon icon={faAngleRight} className="ml-3 mr-1.5 text-sm text-gray-400" />
<div className='pl-3 rounded-md hover:bg-bunker-100/10'>
<Tooltip content="Select environment">
<Select
value={userAvailableEnvs?.filter(uae => uae.name === currentEnv)[0]?.slug}
onValueChange={(value) => {
if (value && onEnvChange) onEnvChange(value);
}}
className="text-md pl-0 font-medium text-primary/80 hover:text-primary bg-transparent"
dropdownContainerClassName="text-bunker-200 bg-mineshaft-800 border border-mineshaft-600 drop-shadow-2xl"
>
{userAvailableEnvs?.map(({ name, slug }) => (
<SelectItem value={slug} key={slug}>
{name}
</SelectItem>
))}
</Select>
</Tooltip>
</div>
</>}
{pageName === "Secrets" ? (
<a
className="text-md font-medium text-primary/80 hover:text-primary"
href={`${router.asPath.split("?")[0]}`}
>
{pageName}
</a>
) : (
<div className="text-md text-gray-400">{pageName}</div>
)}
{currentEnv && (
<>
<FontAwesomeIcon icon={faAngleRight} className="ml-3 mr-1.5 text-sm text-gray-400" />
<div className="rounded-md pl-3 hover:bg-bunker-100/10">
<Tooltip content="Select environment">
<Select
value={userAvailableEnvs?.filter((uae) => uae.name === currentEnv)[0]?.slug}
onValueChange={(value) => {
if (value && onEnvChange) onEnvChange(value);
}}
className="text-md bg-transparent pl-0 font-medium text-primary/80 hover:text-primary"
dropdownContainerClassName="text-bunker-200 bg-mineshaft-800 border border-mineshaft-600 drop-shadow-2xl"
>
{userAvailableEnvs?.map(({ name, slug }) => (
<SelectItem value={slug} key={slug}>
{name}
</SelectItem>
))}
</Select>
</Tooltip>
</div>
</>
)}
</div>
);
}

View File

@@ -3,9 +3,7 @@ import React, { useState } from "react";
import ReactCodeInput from "react-code-input";
import { useTranslation } from "react-i18next";
import {
useSendVerificationEmail
} from "@app/hooks/api";
import { useSendVerificationEmail } from "@app/hooks/api";
import Error from "../basic/Error";
import { Button } from "../v2";
@@ -90,8 +88,8 @@ export default function CodeInputStep({
return (
<div className="mx-auto h-full w-full pb-4 md:px-8">
<p className="text-md flex justify-center text-bunker-200">{t("signup.step2-message")}</p>
<p className="text-md flex justify-center font-semibold my-1 text-bunker-200">{email} </p>
<div className="hidden md:block w-max min-w-[20rem] mx-auto">
<p className="text-md my-1 flex justify-center font-semibold text-bunker-200">{email} </p>
<div className="mx-auto hidden w-max min-w-[20rem] md:block">
<ReactCodeInput
name=""
inputMode="tel"
@@ -102,7 +100,7 @@ export default function CodeInputStep({
className="mt-6 mb-2"
/>
</div>
<div className="block md:hidden w-max mt-4 mx-auto">
<div className="mx-auto mt-4 block w-max md:hidden">
<ReactCodeInput
name=""
inputMode="tel"
@@ -114,26 +112,29 @@ export default function CodeInputStep({
/>
</div>
{codeError && <Error text={t("signup.step2-code-error")} />}
<div className="flex flex-col items-center justify-center lg:w-[19%] w-1/4 min-w-[20rem] mt-2 max-w-xs md:max-w-md mx-auto text-sm text-center md:text-left">
<div className="text-l py-1 text-lg w-full">
<div className="mx-auto mt-2 flex w-1/4 min-w-[20rem] max-w-xs flex-col items-center justify-center text-center text-sm md:max-w-md md:text-left lg:w-[19%]">
<div className="text-l w-full py-1 text-lg">
<Button
type="submit"
onClick={incrementStep}
size="sm"
isFullWidth
className='h-14'
className="h-14"
colorSchema="primary"
variant="outline_bg"
isLoading={isCodeInputCheckLoading}
> {String(t("signup.verify"))} </Button>
>
{" "}
{String(t("signup.verify"))}{" "}
</Button>
</div>
</div>
<div className="flex flex-col items-center justify-center w-full max-h-24 max-w-md mx-auto pt-2">
<div className="mx-auto flex max-h-24 w-full max-w-md flex-col items-center justify-center pt-2">
<div className="flex flex-row items-baseline gap-1 text-sm">
<span className="text-bunker-400">{t("signup.step2-resend-alert")}</span>
<div className="mt-2 text-bunker-400 text-md flex flex-row">
<div className="text-md mt-2 flex flex-row text-bunker-400">
<button disabled={isLoading} onClick={resendVerificationEmail} type="button">
<span className='hover:underline hover:underline-offset-4 hover:decoration-primary-700 hover:text-bunker-200 duration-200 cursor-pointer'>
<span className="cursor-pointer duration-200 hover:text-bunker-200 hover:underline hover:decoration-primary-700 hover:underline-offset-4">
{isResendingVerificationEmail
? t("signup.step2-resend-progress")
: t("signup.step2-resend-submit")}
@@ -141,7 +142,7 @@ export default function CodeInputStep({
</button>
</div>
</div>
<p className="text-sm text-bunker-400 pb-2">{t("signup.step2-spam-alert")}</p>
<p className="pb-2 text-sm text-bunker-400">{t("signup.step2-spam-alert")}</p>
</div>
</div>
);

View File

@@ -57,19 +57,22 @@ export default function DonwloadBackupPDFStep({
};
return (
<div className="flex flex-col items-center w-full h-full md:px-6 mx-auto mb-36 md:mb-16">
<p className="text-xl text-center font-medium flex flex-col justify-center items-center text-transparent bg-clip-text bg-gradient-to-b from-white to-bunker-200">
<FontAwesomeIcon icon={faWarning} className="ml-2 mr-3 pt-1 mb-6 text-6xl text-bunker-200" />
<div className="mx-auto mb-36 flex h-full w-full flex-col items-center md:mb-16 md:px-6">
<p className="flex flex-col items-center justify-center bg-gradient-to-b from-white to-bunker-200 bg-clip-text text-center text-xl font-medium text-transparent">
<FontAwesomeIcon
icon={faWarning}
className="ml-2 mr-3 mb-6 pt-1 text-6xl text-bunker-200"
/>
{t("signup.step4-message")}
</p>
<div className="flex flex-col pb-2 bg-mineshaft-800 border border-mineshaft-600 items-center justify-center text-center lg:w-1/6 w-full md:min-w-[24rem] mt-8 max-w-md text-bunker-300 text-md rounded-md">
<div className="w-full mt-4 md:mt-8 flex flex-row text-center items-center m-2 text-bunker-300 rounded-md lg:w-1/6 lg:w-1/6 w-full md:min-w-[23rem] px-3 mx-auto">
<div className="text-md mt-8 flex w-full max-w-md flex-col items-center justify-center rounded-md border border-mineshaft-600 bg-mineshaft-800 pb-2 text-center text-bunker-300 md:min-w-[24rem] lg:w-1/6">
<div className="m-2 mx-auto mt-4 flex w-full w-full flex-row items-center rounded-md px-3 text-center text-bunker-300 md:mt-8 md:min-w-[23rem] lg:w-1/6 lg:w-1/6">
<span className="mb-2">
{t("signup.step4-description1")} {t("signup.step4-description3")}
</span>
</div>
<div className="flex flex-col items-center px-3 justify-center mt-0 md:mt-4 mb-2 md:mb-4 lg:w-1/6 w-full md:min-w-[20rem] mt-2 md:max-w-md mx-auto text-sm text-center md:text-left">
<div className="text-l py-1 text-lg w-full">
<div className="mx-auto mt-0 mb-2 mt-2 flex w-full flex-col items-center justify-center px-3 text-center text-sm md:mt-4 md:mb-4 md:min-w-[20rem] md:max-w-md md:text-left lg:w-1/6">
<div className="text-l w-full py-1 text-lg">
<Button
onClick={handleBackupKeyGenerate}
size="sm"

View File

@@ -52,13 +52,13 @@ export default function EnterEmailStep({
try {
await mutateAsync({ email });
incrementStep();
} catch(e) {
} catch (e) {
if (axios.isAxiosError(e)) {
const { message = "Something went wrong" } = e.response?.data as { message: string};
const { message = "Something went wrong" } = e.response?.data as { message: string };
createNotification({
type: "error",
text: message
})
text: message
});
}
}
}
@@ -66,11 +66,11 @@ export default function EnterEmailStep({
return (
<div>
<div className="w-full md:px-6 mx-auto">
<p className="text-xl font-medium flex justify-center text-transparent bg-clip-text bg-gradient-to-b from-white to-bunker-200">
<div className="mx-auto w-full md:px-6">
<p className="flex justify-center bg-gradient-to-b from-white to-bunker-200 bg-clip-text text-xl font-medium text-transparent">
{t("signup.step1-start")}
</p>
<div className="flex flex-col items-center justify-center lg:w-1/6 w-1/4 min-w-[20rem] m-auto rounded-lg mt-8">
<div className="m-auto mt-8 flex w-1/4 min-w-[20rem] flex-col items-center justify-center rounded-lg lg:w-1/6">
<Input
placeholder="Enter your email address..."
onChange={(e) => setEmail(e.target.value)}
@@ -79,28 +79,35 @@ export default function EnterEmailStep({
autoComplete="username"
className="h-12"
/>
{emailError && <p className="text-red-600 text-xs text-left w-full ml-1.5 mt-1.5">Please enter a valid email.</p>}
{emailError && (
<p className="ml-1.5 mt-1.5 w-full text-left text-xs text-red-600">
Please enter a valid email.
</p>
)}
</div>
<div className="flex flex-col items-center justify-center lg:w-1/6 w-1/4 min-w-[20rem] mt-2 max-w-xs md:max-w-md mx-auto text-sm text-center md:text-left">
<div className="text-l py-1 text-lg w-full">
<div className="mx-auto mt-2 flex w-1/4 min-w-[20rem] max-w-xs flex-col items-center justify-center text-center text-sm md:max-w-md md:text-left lg:w-1/6">
<div className="text-l w-full py-1 text-lg">
<Button
type="submit"
onClick={emailCheck}
size="sm"
isFullWidth
className='h-14'
className="h-14"
colorSchema="primary"
variant="outline_bg"
isLoading={isLoading}
isDisabled={isLoading}
> {String(t("signup.step1-submit"))} </Button>
>
{" "}
{String(t("signup.step1-submit"))}{" "}
</Button>
</div>
</div>
</div>
<div className="mx-auto mb-48 mt-2 flex w-full max-w-md flex-col items-center justify-center pt-2 md:mb-16 md:pb-2">
<Link href="/login">
<button type="button" className="w-max pb-3 duration-200 hover:opacity-90">
<span className="text-sm text-mineshaft-400 hover:underline hover:underline-offset-4 hover:decoration-primary-700 hover:text-bunker-200 duration-200 cursor-pointer">
<span className="cursor-pointer text-sm text-mineshaft-400 duration-200 hover:text-bunker-200 hover:underline hover:decoration-primary-700 hover:underline-offset-4">
{t("signup.already-have-account")}
</span>
</button>

View File

@@ -16,7 +16,7 @@ export default function TeamInviteStep(): JSX.Element {
const router = useRouter();
const [emails, setEmails] = useState("");
const { data: serverDetails } = useFetchServerStatus();
const { mutateAsync } = useAddUserToOrg();
const { handlePopUpToggle, popUp, handlePopUpOpen } = usePopUp(["setUpEmail"] as const);
@@ -40,55 +40,61 @@ export default function TeamInviteStep(): JSX.Element {
};
return (
<div className="w-max mx-auto min-w-lg h-full pb-4 px-8 mb-64 md:mb-32">
<p className="text-2xl font-semibold flex justify-center text-transparent bg-clip-text bg-gradient-to-b from-white to-bunker-200">
<div className="min-w-lg mx-auto mb-64 h-full w-max px-8 pb-4 md:mb-32">
<p className="flex justify-center bg-gradient-to-b from-white to-bunker-200 bg-clip-text text-2xl font-semibold text-transparent">
{t("signup.step5-invite-team")}
</p>
<p className="text-center flex justify-center text-bunker-400 md:mx-8 mb-6 mt-4">
<p className="mb-6 mt-4 flex justify-center text-center text-bunker-400 md:mx-8">
{t("signup.step5-subtitle")}
</p>
<div className="bg-mineshaft-800 border border-mineshaft-500 w-max mx-auto pt-6 pb-4 px-8 rounded-xl drop-shadow-xl mb-6">
<div className="mx-auto mb-6 w-max rounded-xl border border-mineshaft-500 bg-mineshaft-800 px-8 pt-6 pb-4 drop-shadow-xl">
<div>
<div className="text-bunker-300 font-medium pl-1 pb-1 text-sm">
<div className="pl-1 pb-1 text-sm font-medium text-bunker-300">
<span>Emails</span>
</div>
<textarea
className="bg-mineshaft-900/70 min-w-[30rem] h-20 w-full placeholder:text-bunker-400 py-1 px-2 rounded-md border border-mineshaft-500 text-sm text-bunker-300 outline-none focus:ring-2 ring-primary-800 ring-opacity-70"
className="h-20 w-full min-w-[30rem] rounded-md border border-mineshaft-500 bg-mineshaft-900/70 py-1 px-2 text-sm text-bunker-300 outline-none ring-primary-800 ring-opacity-70 placeholder:text-bunker-400 focus:ring-2"
value={emails}
onChange={(e) => setEmails(e.target.value)}
placeholder="email@example.com, email2@example.com..."
/>
</div>
<div className="flex flex-row items-end justify-end mt-0 md:mt-4 md:mb-2 w-full md:min-w-[30rem] mt-2 md:max-w-md mx-auto text-sm">
<div className="mx-auto mt-0 mt-2 flex w-full flex-row items-end justify-end text-sm md:mt-4 md:mb-2 md:min-w-[30rem] md:max-w-md">
<Button
onClick={() => {
if (serverDetails?.emailConfigured) {
inviteUsers({ emails })
inviteUsers({ emails });
} else {
handlePopUpOpen("setUpEmail");
}
}}
size="sm"
// isFullWidth
className='h-10'
className="h-10"
colorSchema="primary"
variant="solid"
> {t("signup.step5-send-invites") ?? ""} </Button>
>
{" "}
{t("signup.step5-send-invites") ?? ""}{" "}
</Button>
</div>
<EmailServiceSetupModal
isOpen={popUp.setUpEmail?.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("setUpEmail", isOpen)}
/>
</div>
<div className="flex flex-row max-w-max min-w-28 items-center justify-center md:p-2 min-w-[20rem] max-h-24 mx-auto text-lg px-4 mt-4 mb-2">
<div className="min-w-28 mx-auto mt-4 mb-2 flex max-h-24 min-w-[20rem] max-w-max flex-row items-center justify-center px-4 text-lg md:p-2">
<Button
onClick={redirectToHome}
size="sm"
isFullWidth
className='h-12'
className="h-12"
colorSchema="secondary"
variant="outline"
> {t("signup.step5-skip") ?? "Skip"} </Button>
>
{" "}
{t("signup.step5-skip") ?? "Skip"}{" "}
</Button>
</div>
</div>
);

View File

@@ -196,10 +196,8 @@ export default function UserInfoStep({
const userOrgs = await fetchOrganizations();
const orgSlug = userOrgs[0]?.slug;
const orgId = userOrgs[0]?.id;
const project = await ProjectService.initProject({
organizationSlug: orgSlug,
projectName: "Example Project"
});

View File

@@ -1,17 +1,11 @@
import {
getAuthToken,
setAuthToken,
setMfaTempToken,
setSignupTempToken} from "@app/reactQuery";
import { getAuthToken, setAuthToken, setMfaTempToken, setSignupTempToken } from "@app/reactQuery";
export const PROVIDER_AUTH_TOKEN_KEY = "infisical__provider-auth-token";
// depreciated: go for apiRequest module in config/api
export default class SecurityClient {
static setProviderAuthToken(tokenStr: string) {
localStorage.setItem(PROVIDER_AUTH_TOKEN_KEY, tokenStr || "")
localStorage.setItem(PROVIDER_AUTH_TOKEN_KEY, tokenStr || "");
}
static getProviderAuthToken() {

View File

@@ -3,9 +3,7 @@ import crypto from "crypto";
import jsrp from "jsrp";
import {
changePassword,
srp1} from "@app/hooks/api/auth/queries";
import { changePassword, srp1 } from "@app/hooks/api/auth/queries";
import Aes256Gcm from "./cryptography/aes-256-gcm";
import { deriveArgonKey } from "./cryptography/crypto";
@@ -15,95 +13,95 @@ const clientOldPassword = new jsrp.client();
const clientNewPassword = new jsrp.client();
type Params = {
email: string;
currentPassword: string;
newPassword: string;
}
email: string;
currentPassword: string;
newPassword: string;
};
const attemptChangePassword = ({ email, currentPassword, newPassword }: Params): Promise<void> => {
return new Promise((resolve, reject) => {
clientOldPassword.init({ username: email, password: currentPassword }, async () => {
let serverPublicKey; let salt;
return new Promise((resolve, reject) => {
clientOldPassword.init({ username: email, password: currentPassword }, async () => {
let serverPublicKey;
let salt;
try {
const clientPublicKey = clientOldPassword.getPublicKey();
const res = await srp1({ clientPublicKey });
serverPublicKey = res.serverPublicKey;
salt = res.salt;
clientOldPassword.setSalt(salt);
clientOldPassword.setServerPublicKey(serverPublicKey);
const clientProof = clientOldPassword.getProof();
clientNewPassword.init({ username: email, password: newPassword }, async () => {
clientNewPassword.createVerifier(async (err, result) => {
try {
const clientPublicKey = clientOldPassword.getPublicKey();
const derivedKey = await deriveArgonKey({
password: newPassword,
salt: result.salt,
mem: 65536,
time: 3,
parallelism: 1,
hashLen: 32
});
const res = await srp1({ clientPublicKey });
if (!derivedKey) throw new Error("Failed to derive key from password");
serverPublicKey = res.serverPublicKey;
salt = res.salt;
const key = crypto.randomBytes(32);
clientOldPassword.setSalt(salt);
clientOldPassword.setServerPublicKey(serverPublicKey);
const {
ciphertext: encryptedPrivateKey,
iv: encryptedPrivateKeyIV,
tag: encryptedPrivateKeyTag
} = Aes256Gcm.encrypt({
text: localStorage.getItem("PRIVATE_KEY") as string,
secret: key
});
const clientProof = clientOldPassword.getProof();
const {
ciphertext: protectedKey,
iv: protectedKeyIV,
tag: protectedKeyTag
} = Aes256Gcm.encrypt({
text: key.toString("hex"),
secret: Buffer.from(derivedKey.hash)
});
clientNewPassword.init({ username: email, password: newPassword }, async () => {
clientNewPassword.createVerifier(async (err, result) => {
try {
const derivedKey = await deriveArgonKey({
password: newPassword,
salt: result.salt,
mem: 65536,
time: 3,
parallelism: 1,
hashLen: 32
});
await changePassword({
clientProof,
protectedKey,
protectedKeyIV,
protectedKeyTag,
encryptedPrivateKey,
encryptedPrivateKeyIV,
encryptedPrivateKeyTag,
salt: result.salt,
verifier: result.verifier
});
if (!derivedKey) throw new Error("Failed to derive key from password");
saveTokenToLocalStorage({
encryptedPrivateKey,
iv: encryptedPrivateKeyIV,
tag: encryptedPrivateKeyTag
});
const key = crypto.randomBytes(32);
const {
ciphertext: encryptedPrivateKey,
iv: encryptedPrivateKeyIV,
tag: encryptedPrivateKeyTag
} = Aes256Gcm.encrypt({
text: localStorage.getItem("PRIVATE_KEY") as string,
secret: key
});
const {
ciphertext: protectedKey,
iv: protectedKeyIV,
tag: protectedKeyTag
} = Aes256Gcm.encrypt({
text: key.toString("hex"),
secret: Buffer.from(derivedKey.hash)
});
await changePassword({
clientProof,
protectedKey,
protectedKeyIV,
protectedKeyTag,
encryptedPrivateKey,
encryptedPrivateKeyIV,
encryptedPrivateKeyTag,
salt: result.salt,
verifier: result.verifier
});
saveTokenToLocalStorage({
encryptedPrivateKey,
iv: encryptedPrivateKeyIV,
tag: encryptedPrivateKeyTag
});
resolve();
} catch (err2) {
console.error(err2);
reject(err2);
}
});
});
} catch (err) {
console.error(err);
reject(err);
resolve();
} catch (err2) {
console.error(err2);
reject(err2);
}
});
});
} catch (err) {
console.error(err);
reject(err);
}
});
}
});
};
export default attemptChangePassword;
export default attemptChangePassword;

View File

@@ -1,7 +1,7 @@
/* eslint-disable prefer-destructuring */
import jsrp from "jsrp";
import { login1 , verifyMfaToken } from "@app/hooks/api/auth/queries";
import { login1, verifyMfaToken } from "@app/hooks/api/auth/queries";
import KeyService from "@app/services/KeyService";
import { saveTokenToLocalStorage } from "./saveTokenToLocalStorage";
@@ -11,11 +11,11 @@ import SecurityClient from "./SecurityClient";
const client = new jsrp.client();
interface IsMfaLoginSuccessful {
success: boolean;
loginResponse:{
privateKey: string;
JTWToken: string;
}
success: boolean;
loginResponse: {
privateKey: string;
JTWToken: string;
};
}
/**
@@ -26,81 +26,84 @@ interface IsMfaLoginSuccessful {
* @param {String} obj.mfaToken - MFA code/token
*/
const attemptLoginMfa = async ({
email,
password,
providerAuthToken,
mfaToken
email,
password,
providerAuthToken,
mfaToken
}: {
email: string;
password: string;
providerAuthToken?: string,
mfaToken: string;
email: string;
password: string;
providerAuthToken?: string;
mfaToken: string;
}): Promise<IsMfaLoginSuccessful> => {
return new Promise((resolve, reject) => {
client.init({
username: email,
password
}, async () => {
try {
const clientPublicKey = client.getPublicKey();
const { salt } = await login1({
email,
clientPublicKey,
providerAuthToken,
});
return new Promise((resolve, reject) => {
client.init(
{
username: email,
password
},
async () => {
try {
const clientPublicKey = client.getPublicKey();
const { salt } = await login1({
email,
clientPublicKey,
providerAuthToken
});
const {
encryptionVersion,
protectedKey,
protectedKeyIV,
protectedKeyTag,
token,
publicKey,
encryptedPrivateKey,
iv,
tag
} = await verifyMfaToken({
email,
mfaCode: mfaToken
});
const {
encryptionVersion,
protectedKey,
protectedKeyIV,
protectedKeyTag,
token,
publicKey,
encryptedPrivateKey,
iv,
tag
} = await verifyMfaToken({
email,
mfaCode: mfaToken
});
// unset temporary (MFA) JWT token and set JWT token
SecurityClient.setMfaToken("");
SecurityClient.setToken(token);
SecurityClient.setProviderAuthToken("");
// unset temporary (MFA) JWT token and set JWT token
SecurityClient.setMfaToken("");
SecurityClient.setToken(token);
SecurityClient.setProviderAuthToken("");
const privateKey = await KeyService.decryptPrivateKey({
encryptionVersion,
encryptedPrivateKey,
iv,
tag,
password,
salt,
protectedKey,
protectedKeyIV,
protectedKeyTag
});
const privateKey = await KeyService.decryptPrivateKey({
encryptionVersion,
encryptedPrivateKey,
iv,
tag,
password,
salt,
protectedKey,
protectedKeyIV,
protectedKeyTag
});
saveTokenToLocalStorage({
publicKey,
encryptedPrivateKey,
iv,
tag,
privateKey
});
saveTokenToLocalStorage({
publicKey,
encryptedPrivateKey,
iv,
tag,
privateKey
});
resolve({
success: true,
loginResponse:{
privateKey,
JTWToken: token
}
});
} catch (err) {
reject(err);
resolve({
success: true,
loginResponse: {
privateKey,
JTWToken: token
}
});
});
}
});
} catch (err) {
reject(err);
}
}
);
});
};
export default attemptLoginMfa;
export default attemptLoginMfa;

View File

@@ -1,7 +1,7 @@
/* eslint-disable prefer-destructuring */
import jsrp from "jsrp";
import { login1 , verifyMfaToken } from "@app/hooks/api/auth/queries";
import { login1, verifyMfaToken } from "@app/hooks/api/auth/queries";
import KeyService from "@app/services/KeyService";
import { saveTokenToLocalStorage } from "./saveTokenToLocalStorage";
@@ -18,75 +18,78 @@ const client = new jsrp.client();
* @param {String} obj.mfaToken - MFA code/token
*/
const attemptLoginMfa = async ({
email,
password,
providerAuthToken,
mfaToken
email,
password,
providerAuthToken,
mfaToken
}: {
email: string;
password: string;
providerAuthToken?: string,
mfaToken: string;
email: string;
password: string;
providerAuthToken?: string;
mfaToken: string;
}): Promise<Boolean> => {
return new Promise((resolve, reject) => {
client.init({
username: email,
password
}, async () => {
try {
const clientPublicKey = client.getPublicKey();
const { salt } = await login1({
email,
clientPublicKey,
providerAuthToken,
});
const {
encryptionVersion,
protectedKey,
protectedKeyIV,
protectedKeyTag,
token,
publicKey,
encryptedPrivateKey,
iv,
tag
} = await verifyMfaToken({
email,
mfaCode: mfaToken
});
return new Promise((resolve, reject) => {
client.init(
{
username: email,
password
},
async () => {
try {
const clientPublicKey = client.getPublicKey();
const { salt } = await login1({
email,
clientPublicKey,
providerAuthToken
});
// unset temporary (MFA) JWT token and set JWT token
SecurityClient.setMfaToken("");
SecurityClient.setToken(token);
SecurityClient.setProviderAuthToken("");
const {
encryptionVersion,
protectedKey,
protectedKeyIV,
protectedKeyTag,
token,
publicKey,
encryptedPrivateKey,
iv,
tag
} = await verifyMfaToken({
email,
mfaCode: mfaToken
});
const privateKey = await KeyService.decryptPrivateKey({
encryptionVersion,
encryptedPrivateKey,
iv,
tag,
password,
salt,
protectedKey,
protectedKeyIV,
protectedKeyTag
});
saveTokenToLocalStorage({
publicKey,
encryptedPrivateKey,
iv,
tag,
privateKey
});
// unset temporary (MFA) JWT token and set JWT token
SecurityClient.setMfaToken("");
SecurityClient.setToken(token);
SecurityClient.setProviderAuthToken("");
resolve(true);
} catch (err) {
reject(err);
}
});
});
}
const privateKey = await KeyService.decryptPrivateKey({
encryptionVersion,
encryptedPrivateKey,
iv,
tag,
password,
salt,
protectedKey,
protectedKeyIV,
protectedKeyTag
});
export default attemptLoginMfa;
saveTokenToLocalStorage({
publicKey,
encryptedPrivateKey,
iv,
tag,
privateKey
});
resolve(true);
} catch (err) {
reject(err);
}
}
);
});
};
export default attemptLoginMfa;

View File

@@ -1,5 +1,11 @@
import { checkIsPasswordBreached } from "./checkIsPasswordBreached";
import { escapeCharRegex, letterCharRegex, lowEntropyRegexes,numAndSpecialCharRegex, repeatedCharRegex } from "./passwordRegexes";
import {
escapeCharRegex,
letterCharRegex,
lowEntropyRegexes,
numAndSpecialCharRegex,
repeatedCharRegex
} from "./passwordRegexes";
interface PasswordCheckProps {
password: string;
@@ -29,40 +35,38 @@ const passwordCheck = async ({
{
name: "tooShort",
validator: (pwd: string) => pwd.length >= 14,
setError: setPasswordErrorTooShort,
setError: setPasswordErrorTooShort
},
{
name: "tooLong",
validator: (pwd: string) => pwd.length < 101,
setError: setPasswordErrorTooLong,
setError: setPasswordErrorTooLong
},
{
name: "noLetterChar",
validator: (pwd: string) => letterCharRegex.test(pwd),
setError: setPasswordErrorNoLetterChar,
setError: setPasswordErrorNoLetterChar
},
{
name: "noNumOrSpecialChar",
validator: (pwd: string) => numAndSpecialCharRegex.test(pwd),
setError: setPasswordErrorNoNumOrSpecialChar,
setError: setPasswordErrorNoNumOrSpecialChar
},
{
name: "repeatedChar",
validator: (pwd: string) => !repeatedCharRegex.test(pwd),
setError: setPasswordErrorRepeatedChar,
setError: setPasswordErrorRepeatedChar
},
{
name: "escapeChar",
validator: (pwd: string) => !escapeCharRegex.test(pwd),
setError: setPasswordErrorEscapeChar,
setError: setPasswordErrorEscapeChar
},
{
name: "lowEntropy",
validator: (pwd: string) => (
!lowEntropyRegexes.some(regex => regex.test(pwd))
),
setError: setPasswordErrorLowEntropy,
},
validator: (pwd: string) => !lowEntropyRegexes.some((regex) => regex.test(pwd)),
setError: setPasswordErrorLowEntropy
}
];
const isBreached = await checkIsPasswordBreached(password);
@@ -73,7 +77,7 @@ const passwordCheck = async ({
} else {
setPasswordErrorBreached(false);
}
tests.forEach((test) => {
if (!test.validator(password)) {
errorCheck = true;
@@ -81,7 +85,7 @@ const passwordCheck = async ({
} else {
test.setError(false);
}
})
});
return errorCheck;
};

View File

@@ -17,25 +17,25 @@ function bufferToHex(buffer: ArrayBuffer): string {
return hexParts.join("");
}
// see API details here: https://haveibeenpwned.com/API/v3#SearchingPwnedPasswordsByRange
// in short, the pending password is hashed (SHA-1), the first 5 chars are sliced and compared against a ranged hash table
// this hash table is formed from the 5 char hash prefix (ie. 00000-FFFFF) so 16^5 results
// returns a hash table of 800-1000 results
// padding has been added to prevent MitM attacker determining which hash table was called by the response size
// the last 35 chars of the password hash are compared client-side against the table
// if there is a match, that password has been involved in a password breach (ie. pwnd) and should NOT be accepted
// the database consists of ~700 mln breached passwords and is continuously updated, including with law enforcement ingestion
// https://www.troyhunt.com/open-source-pwned-passwords-with-fbi-feed-and-225m-new-nca-passwords-is-now-live/
// see API details here: https://haveibeenpwned.com/API/v3#SearchingPwnedPasswordsByRange
// in short, the pending password is hashed (SHA-1), the first 5 chars are sliced and compared against a ranged hash table
// this hash table is formed from the 5 char hash prefix (ie. 00000-FFFFF) so 16^5 results
// returns a hash table of 800-1000 results
// padding has been added to prevent MitM attacker determining which hash table was called by the response size
// the last 35 chars of the password hash are compared client-side against the table
// if there is a match, that password has been involved in a password breach (ie. pwnd) and should NOT be accepted
// the database consists of ~700 mln breached passwords and is continuously updated, including with law enforcement ingestion
// https://www.troyhunt.com/open-source-pwned-passwords-with-fbi-feed-and-225m-new-nca-passwords-is-now-live/
// The HIBP API follows NIST guidance (pg.14) https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-63b.pdf
// "When processing requests to establish and change memorized secrets, verifiers SHALL compare
// the prospective secrets against a list that contains values known to be commonly-used, expected,
// or compromised. For example, the list MAY include, but is not limited to:
// • Passwords obtained from previous breach corpuses.
// • Dictionary words.
// • Repetitive or sequential characters (e.g. aaaaaa, 1234abcd).
// • Context-specific words, such as the name of the service, the username, and derivatives
// thereof."
// The HIBP API follows NIST guidance (pg.14) https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-63b.pdf
// "When processing requests to establish and change memorized secrets, verifiers SHALL compare
// the prospective secrets against a list that contains values known to be commonly-used, expected,
// or compromised. For example, the list MAY include, but is not limited to:
// • Passwords obtained from previous breach corpuses.
// • Dictionary words.
// • Repetitive or sequential characters (e.g. aaaaaa, 1234abcd).
// • Context-specific words, such as the name of the service, the username, and derivatives
// thereof."
export const checkIsPasswordBreached = async (password: string): Promise<boolean> => {
const HAVE_I_BEEN_PWNED_API_URL = "https://api.pwnedpasswords.com";
@@ -66,8 +66,8 @@ export const checkIsPasswordBreached = async (password: string): Promise<boolean
response = await axios.get(rangedHashTableUri, {
headers: {
"Add-Padding": "true", // see https://www.troyhunt.com/enhancing-pwned-passwords-privacy-with-padding/
"Content-Type": "text/plain",
},
"Content-Type": "text/plain"
}
});
if (response.status === 200) {
@@ -76,9 +76,8 @@ export const checkIsPasswordBreached = async (password: string): Promise<boolean
// check the last 35 hash chars to see if there's a match
const isBreachedPassword: boolean = responseData.includes(hashedPwd.slice(5, 40));
return isBreachedPassword;
}
retryAttempt += 1;
}
retryAttempt += 1;
} catch (err) {
if (!axios.isAxiosError(err)) {
throw err;
@@ -88,14 +87,15 @@ export const checkIsPasswordBreached = async (password: string): Promise<boolean
}
console.error(
`Received a non-200 response (${response ? response.status : "unknown"}) from the Pwnd Passwords API`
`Received a non-200 response (${
response ? response.status : "unknown"
}) from the Pwnd Passwords API`
);
return false;
} catch (err: any) {
console.error("An unexpected error has occurred:", err.message);
return false;
} finally {
// Clear the UTF-8 encoded password from memory
if (encodedPwd) {

View File

@@ -1,5 +1,11 @@
import { checkIsPasswordBreached } from "./checkIsPasswordBreached";
import { escapeCharRegex, letterCharRegex, lowEntropyRegexes,numAndSpecialCharRegex, repeatedCharRegex } from "./passwordRegexes";
import {
escapeCharRegex,
letterCharRegex,
lowEntropyRegexes,
numAndSpecialCharRegex,
repeatedCharRegex
} from "./passwordRegexes";
type Errors = {
tooShort?: string;
@@ -44,40 +50,38 @@ const checkPassword = async ({ password, setErrors }: CheckPasswordParams): Prom
{
name: "tooShort",
validator: (pwd: string) => pwd.length >= 14,
errorText: "at least 14 characters",
errorText: "at least 14 characters"
},
{
name: "tooLong",
validator: (pwd: string) => pwd.length < 101,
errorText: "at most 100 characters",
errorText: "at most 100 characters"
},
{
name: "noLetterChar",
validator: (pwd: string) => letterCharRegex.test(pwd),
errorText: "at least 1 letter character",
errorText: "at least 1 letter character"
},
{
name: "noNumOrSpecialChar",
validator: (pwd: string) => numAndSpecialCharRegex.test(pwd),
errorText: "at least 1 number or special character",
errorText: "at least 1 number or special character"
},
{
name: "repeatedChar",
validator: (pwd: string) => !repeatedCharRegex.test(pwd),
errorText: "at most 3 repeated, consecutive characters",
errorText: "at most 3 repeated, consecutive characters"
},
{
name: "escapeChar",
validator: (pwd: string) => !escapeCharRegex.test(pwd),
errorText: "No escape characters allowed.",
errorText: "No escape characters allowed."
},
{
name: "lowEntropy",
validator: (pwd: string) => (
!lowEntropyRegexes.some(regex => regex.test(pwd))
),
errorText: "Password contains personal info.",
},
validator: (pwd: string) => !lowEntropyRegexes.some((regex) => regex.test(pwd)),
errorText: "Password contains personal info."
}
];
const isBreached = await checkIsPasswordBreached(password);
@@ -96,4 +100,4 @@ const checkPassword = async ({ password, setErrors }: CheckPasswordParams): Prom
return Object.keys(errors).length > 0;
};
export default checkPassword;
export default checkPassword;

View File

@@ -1,6 +1,7 @@
// This regex covers letters (case insensitive) for the top 50 most spoken languages
/* eslint-disable no-misleading-character-class */
export const letterCharRegex = /[A-Za-z\u00C0-\u00D6\u00D8-\u00DE\u00DF-\u00F6\u00F8-\u00FF\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF\u0600-\u06FF\u0400-\u04FF\u0500-\u052F\u2DE0-\u2DFF\uA640-\uA69F\u05B0-\u05FF\u0980-\u09FF\u1F00-\u1FFF\u0130\u015E\u011E\u00C7\u00FC\u00FB\u00EB\u00E7]/u;
export const letterCharRegex =
/[A-Za-z\u00C0-\u00D6\u00D8-\u00DE\u00DF-\u00F6\u00F8-\u00FF\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF\u0600-\u06FF\u0400-\u04FF\u0500-\u052F\u2DE0-\u2DFF\uA640-\uA69F\u05B0-\u05FF\u0980-\u09FF\u1F00-\u1FFF\u0130\u015E\u011E\u00C7\u00FC\u00FB\u00EB\u00E7]/u;
// This regex covers digits, special characters, symbols, and emojis.
export const numAndSpecialCharRegex = /[\d!@#$%^&*(),.?":{}|<>]|[^\p{L}\p{N}\s]/u;
@@ -32,5 +33,5 @@ export const lowEntropyRegexes = [
/\b(?:[A-Z0-9]{7,10}|[A-Z0-9]{10,11}|[A-Z0-9]{7,10})\b/,
// US social security number
/\b\d{3}[-\s]?\d{2}[-\s]?\d{4}\b/,
];
/\b\d{3}[-\s]?\d{2}[-\s]?\d{4}\b/
];

View File

@@ -3,9 +3,7 @@ import crypto from "crypto";
import jsrp from "jsrp";
import { issueBackupPrivateKey ,
srp1
} from "@app/hooks/api/auth/queries";
import { issueBackupPrivateKey, srp1 } from "@app/hooks/api/auth/queries";
import generateBackupPDF from "../generateBackupPDF";
import Aes256Gcm from "./aes-256-gcm";
@@ -97,7 +95,6 @@ const issueBackupKey = async ({
generatedKey
});
setBackupKeyIssued(true);
} catch {
setBackupKeyError(true);
}

View File

@@ -7,6 +7,7 @@
/* eslint-disable vars-on-top */
/* eslint-disable no-var */
/* eslint-disable func-names */
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-nocheck
import { INTERCOMid as APPid } from "@app/components/utilities/config";

Some files were not shown because too many files have changed in this diff Show More