Compare commits

...

59 Commits

Author SHA1 Message Date
Lakhan Samani
6d541cbfb9 fix: use normal mutex for cache 2023-04-10 15:33:59 +05:30
Lakhan Samani
1ebba7f2b7 Merge pull request #343 from authorizerdev/fix/session-storage
fix: session storage
2023-04-08 18:07:52 +05:30
Lakhan Samani
428a0be3db feat: add cache clear 2023-04-08 18:02:53 +05:30
Lakhan Samani
02c0ebb9c4 fix: session storage 2023-04-08 13:06:15 +05:30
Lakhan Samani
9a284c03ca fix: redis session 2023-04-03 10:26:27 +05:30
Lakhan Samani
c8fe05eabc Merge pull request #342 from authorizerdev/feat/default-oauth-configs
feat: add support for default response mode & type env
2023-04-01 17:42:02 +05:30
Lakhan Samani
48344ffd4c feat: add support for default response mode & type env
Resolves #341
2023-04-01 17:36:07 +05:30
Lakhan Samani
77f34e1149 Merge pull request #339 from authorizerdev/fix/webhooks
fix: allow multiple hooks for same event
2023-03-29 07:31:46 +05:30
Lakhan Samani
16136931a9 fix: add event description to webhook res 2023-03-29 07:31:07 +05:30
Lakhan Samani
c908ac94da fix: continue in case of error for register events 2023-03-29 07:29:44 +05:30
Lakhan Samani
6604b6bbdd fix: update dashboard ui for webhooks 2023-03-29 07:27:56 +05:30
Lakhan Samani
2c227b5518 chore: delete couchbase container after tests 2023-03-29 07:10:36 +05:30
Lakhan Samani
e822b6f31a fix: queries for webhooks + improve tests 2023-03-29 07:06:33 +05:30
Lakhan Samani
a38e9d4e6c fix: rename title -> event_description 2023-03-26 07:48:06 +05:30
Lakhan Samani
deaf1e2ff7 fix: allow multiple hooks for same event 2023-03-26 07:20:45 +05:30
Lakhan Samani
f324976801 Merge pull request #338 from authorizerdev/fix/accessibility
fix: accessibility
2023-03-25 10:48:49 +05:30
Lakhan Samani
fad90ce1a8 fix: accessibility
Resolves #337
2023-03-25 10:48:09 +05:30
Lakhan Samani
df406ba053 Merge pull request #332 from authorizerdev/fix/open-id
fix: add missing info for openid config
2023-03-07 08:44:26 +05:30
Lakhan Samani
4a7877a21b fix: remove duplicate files 2023-03-04 16:13:31 +05:30
Lakhan Samani
79089cc009 Merge pull request #330 from productdevbook/patch-1
feat: github sponsor
2023-03-04 16:12:52 +05:30
Lakhan Samani
149d0cac7a fix: add missing info for openid config
Resolves #304
2023-03-04 16:11:37 +05:30
Lakhan Samani
8863140e75 Create FUNDING.yaml 2023-03-03 08:11:38 +05:30
Mehmet
b8ffadd36c Create FUNDING.yml 2023-03-02 13:07:05 +03:00
Lakhan Samani
7dd20128af Merge pull request #329 from authorizerdev/fix/add-sub-user-info
[server][fix]: add sub to userinfo
2023-02-28 12:52:21 +05:30
Lakhan Samani
19f5ff61c0 [server][fix]: add sub to userinfo
Resolves: #327
2023-02-28 12:51:11 +05:30
Lakhan Samani
146707d062 Merge pull request #328 from authorizerdev/feat/add-microsoft-login
feat: add microsoft login
2023-02-26 06:05:42 +05:30
Lakhan Samani
0810c4a201 chore: update app authorizer-react 1.1.8 2023-02-26 06:05:15 +05:30
Lakhan Samani
3603af9f84 feat: add microsoft login 2023-02-26 05:23:02 +05:30
Lakhan Samani
1ac8ba4ce0 Merge pull request #324 from authorizerdev/fix/neon-db-support
[server]: fix support for neondb
2023-02-10 18:08:22 +05:30
Lakhan Samani
cdcdc444b2 [server]: fix support for neondb
Update gorm/postgres driver version 1.4.7
2023-02-10 10:39:53 +05:30
Lakhan Samani
330f35f2fc Merge pull request #322 from authorizerdev/fix/use-scopes-as-string
[server] use scope string instead of string array in tokens
2023-02-08 09:41:17 +05:30
Lakhan Samani
70242debe1 [server] fix scope response type + add extra claims to access token 2023-02-08 09:39:08 +05:30
Lakhan Samani
4018da6697 [server] use scope string instead of string array in tokens 2023-02-07 01:13:03 +05:30
Lakhan Samani
a73c6ee49e Merge pull request #319 from authorizerdev/fix/arangodb-connection 2023-02-06 21:32:32 +05:30
Lakhan Samani
c23fb1bb32 [server] update arangodb version for test 3.10.3 2023-02-06 20:39:22 +05:30
Lakhan Samani
270853a6a3 [server] add support for arangodb cert, username, password
Adding support for db username, password and ca cert will
enable users to connect arangodb cloud platform
2023-02-06 18:14:19 +05:30
Lakhan Samani
2d0346ff23 [server] remove unused error condition for couchbase 2023-02-05 11:03:26 +05:30
Lakhan Samani
4b26e1ce85 [server] fix bucket creation for couchbase
Run create bucket query only if bucket is not found.
Required while running couchbase cloud version
2023-02-05 11:01:20 +05:30
Lakhan Samani
8212e81023 [server] common util for couchbase syntax 2023-02-02 13:06:18 +05:30
Lakhan Samani
642581eefd [server] Add COUCHBASE_BUCKET_RAM_QUOTA
Resolves #317
2023-02-02 12:43:17 +05:30
Lakhan Samani
b7357dde21 [server] fix primary index creation for couchbase 2023-02-02 12:28:52 +05:30
Lakhan Samani
a1df2ce31f [server] use encryption_key for couchbase env as hash is reserved keyword 2023-01-31 11:18:20 +05:30
Lakhan Samani
748761926d [server] fix make command 2023-01-25 05:19:01 +05:30
Lakhan Samani
d632195ba5 Merge pull request #315 from authorizerdev/feat/couchbase
feat: add couchbase provider
2023-01-19 23:32:20 +05:30
Lakhan Samani
25970f80e1 [app] update authorizer-react 2023-01-19 23:30:54 +05:30
Lakhan Samani
a52b7c86e7 [server] add couchbase to all db test 2023-01-19 23:29:48 +05:30
Lakhan Samani
504d0f34d7 [server] add couchbase envs 2023-01-19 23:28:21 +05:30
Lakhan Samani
44879f1a8f feat: add couchbase provider 2023-01-18 01:38:00 +05:30
Lakhan Samani
b39f0b87fd Merge pull request #313 from authorizerdev/feat/add-get-user
Add get user api for admin
2023-01-05 20:17:48 +05:30
Lakhan Samani
b2423140e2 Add get user api for admin 2023-01-05 20:16:41 +05:30
Lakhan Samani
4f810d2f8b Merge pull request #307 from authorizerdev/feat/mobile-basic-auth
feat: add mobile based basic auth
2022-12-25 03:28:55 +05:30
Lakhan Samani
313b510ba1 feat: add signup + login using mobile 2022-12-25 03:22:42 +05:30
Lakhan Samani
105d9be685 Merge pull request #310 from Lentech-AS/main
Rnd warning fixes
2022-12-24 09:18:07 +05:30
Leander Gangsø
bc68b61879 fix: oauth2.NoContext is deprecated
using context.TODO instead
2022-12-23 21:12:11 +01:00
Leander Gangsø
2847300bf6 fix: update deprecated func since go 1.16
might want to update the docs if you accept this, as it states go > 1.15 is required
2022-12-23 21:12:11 +01:00
Leander Gangsø
d438480f37 fix: remove unused formatting directive 2022-12-23 21:12:11 +01:00
Lakhan Samani
da29f9d055 Merge pull request #308 from Lentech-AS/main
change toast position to top-right
2022-12-23 07:10:11 +05:30
Leander Gangsø
f29256a8f5 change toast position to top-right 2022-12-22 19:53:18 +01:00
Lakhan Samani
1eb8965f98 feat: add mobile based basic auth 2022-12-21 23:14:24 +05:30
143 changed files with 8493 additions and 1559 deletions

3
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,3 @@
# These are supported funding model platforms
github: authorizerdev

View File

@@ -1,4 +1,4 @@
FROM golang:1.19.1-alpine as go-builder
FROM golang:1.19.5-alpine as go-builder
WORKDIR /authorizer
COPY server server
COPY Makefile .

View File

@@ -26,23 +26,31 @@ test-scylladb:
cd server && go clean --testcache && TEST_DBS="scylladb" go test -p 1 -v ./test
docker rm -vf authorizer_scylla_db
test-arangodb:
docker run -d --name authorizer_arangodb -p 8529:8529 -e ARANGO_NO_AUTH=1 arangodb/arangodb:3.8.4
docker run -d --name authorizer_arangodb -p 8529:8529 -e ARANGO_NO_AUTH=1 arangodb/arangodb:3.10.3
cd server && go clean --testcache && TEST_DBS="arangodb" go test -p 1 -v ./test
docker rm -vf authorizer_arangodb
test-dynamodb:
docker run -d --name dynamodb-local-test -p 8000:8000 amazon/dynamodb-local:latest
cd server && go clean --testcache && TEST_DBS="dynamodb" go test -p 1 -v ./test
docker rm -vf dynamodb-local-test
test-couchbase:
docker run -d --name couchbase-local-test -p 8091-8097:8091-8097 -p 11210:11210 -p 11207:11207 -p 18091-18095:18091-18095 -p 18096:18096 -p 18097:18097 couchbase:latest
sh scripts/couchbase-test.sh
cd server && go clean --testcache && TEST_DBS="couchbase" go test -p 1 -v ./test
docker rm -vf couchbase-local-test
test-all-db:
rm -rf server/test/test.db server/test/test.db-shm server/test/test.db-wal && rm -rf test.db test.db-shm test.db-wal
docker run -d --name authorizer_scylla_db -p 9042:9042 scylladb/scylla
docker run -d --name authorizer_mongodb_db -p 27017:27017 mongo:4.4.15
docker run -d --name authorizer_arangodb -p 8529:8529 -e ARANGO_NO_AUTH=1 arangodb/arangodb:3.8.4
docker run -d --name authorizer_arangodb -p 8529:8529 -e ARANGO_NO_AUTH=1 arangodb/arangodb:3.10.3
docker run -d --name dynamodb-local-test -p 8000:8000 amazon/dynamodb-local:latest
docker run -d --name couchbase-local-test -p 8091-8097:8091-8097 -p 11210:11210 -p 11207:11207 -p 18091-18095:18091-18095 -p 18096:18096 -p 18097:18097 couchbase:latest
sh scripts/couchbase-test.sh
cd server && go clean --testcache && TEST_DBS="sqlite,mongodb,arangodb,scylladb,dynamodb" go test -p 1 -v ./test
docker rm -vf authorizer_scylla_db
docker rm -vf authorizer_mongodb_db
docker rm -vf authorizer_arangodb
docker rm -vf dynamodb-local-test
docker rm -vf couchbase-local-test
generate:
cd server && go run github.com/99designs/gqlgen generate && go mod tidy

30
app/package-lock.json generated
View File

@@ -9,7 +9,7 @@
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"@authorizerdev/authorizer-react": "^1.1.4",
"@authorizerdev/authorizer-react": "^1.1.8",
"@types/react": "^17.0.15",
"@types/react-dom": "^17.0.9",
"esbuild": "^0.12.17",
@@ -27,9 +27,9 @@
}
},
"node_modules/@authorizerdev/authorizer-js": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-1.1.2.tgz",
"integrity": "sha512-22qoqBaCNMn3QRWdJXmwAZeb5X9lwhZF3y23loY0eO3xUUzBaJiltENjHynbLGCg8LGgn7UaJEKDqGfL6Rzwvg==",
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-1.1.4.tgz",
"integrity": "sha512-oLp3XMYU9xHlg5urpAOaw6NrKQ07SaI8oAi1BAMHZWivv5qi2V+6roey0y9MdvCUmsMULBBcZg87kuoMTXRJOw==",
"dependencies": {
"cross-fetch": "^3.1.5"
},
@@ -38,11 +38,11 @@
}
},
"node_modules/@authorizerdev/authorizer-react": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.1.4.tgz",
"integrity": "sha512-FBH2igXFM8+TdA2hl1S/HMzt1+OL5wWUow3+Zyiq+IkG9nIjWFlM7ebo4D0zJd875IJiabYFnXqstRABo0ysIQ==",
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.1.8.tgz",
"integrity": "sha512-5v9Zc7ZxiEQEHT1Fwj1jvEemdckcrn7mZPkdmJ/z0YOWNbF20ZpM4WsfrtJB+d293m7Rb1QKwzdFPkYINMh5tw==",
"dependencies": {
"@authorizerdev/authorizer-js": "^1.1.2"
"@authorizerdev/authorizer-js": "^1.1.4"
},
"engines": {
"node": ">=10"
@@ -834,19 +834,19 @@
},
"dependencies": {
"@authorizerdev/authorizer-js": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-1.1.2.tgz",
"integrity": "sha512-22qoqBaCNMn3QRWdJXmwAZeb5X9lwhZF3y23loY0eO3xUUzBaJiltENjHynbLGCg8LGgn7UaJEKDqGfL6Rzwvg==",
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-1.1.4.tgz",
"integrity": "sha512-oLp3XMYU9xHlg5urpAOaw6NrKQ07SaI8oAi1BAMHZWivv5qi2V+6roey0y9MdvCUmsMULBBcZg87kuoMTXRJOw==",
"requires": {
"cross-fetch": "^3.1.5"
}
},
"@authorizerdev/authorizer-react": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.1.4.tgz",
"integrity": "sha512-FBH2igXFM8+TdA2hl1S/HMzt1+OL5wWUow3+Zyiq+IkG9nIjWFlM7ebo4D0zJd875IJiabYFnXqstRABo0ysIQ==",
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.1.8.tgz",
"integrity": "sha512-5v9Zc7ZxiEQEHT1Fwj1jvEemdckcrn7mZPkdmJ/z0YOWNbF20ZpM4WsfrtJB+d293m7Rb1QKwzdFPkYINMh5tw==",
"requires": {
"@authorizerdev/authorizer-js": "^1.1.2"
"@authorizerdev/authorizer-js": "^1.1.4"
}
},
"@babel/code-frame": {

View File

@@ -12,7 +12,7 @@
"author": "Lakhan Samani",
"license": "ISC",
"dependencies": {
"@authorizerdev/authorizer-react": "^1.1.4",
"@authorizerdev/authorizer-react": "^1.1.9",
"@types/react": "^17.0.15",
"@types/react-dom": "^17.0.9",
"esbuild": "^0.12.17",

626
app/pnpm-lock.yaml generated Normal file
View File

@@ -0,0 +1,626 @@
lockfileVersion: 5.4
specifiers:
'@authorizerdev/authorizer-react': ^1.1.9
'@types/react': ^17.0.15
'@types/react-dom': ^17.0.9
'@types/react-router-dom': ^5.1.8
'@types/styled-components': ^5.1.11
esbuild: ^0.12.17
prettier: 2.7.1
react: ^17.0.2
react-dom: ^17.0.2
react-is: ^17.0.2
react-router-dom: ^5.2.0
styled-components: ^5.3.0
typescript: ^4.3.5
dependencies:
'@authorizerdev/authorizer-react': 1.1.9_react@17.0.2
'@types/react': 17.0.53
'@types/react-dom': 17.0.19
esbuild: 0.12.29
react: 17.0.2
react-dom: 17.0.2_react@17.0.2
react-is: 17.0.2
react-router-dom: 5.3.4_react@17.0.2
styled-components: 5.3.9_fane7jikarojcev26y27hpbhu4
typescript: 4.9.5
devDependencies:
'@types/react-router-dom': 5.3.3
'@types/styled-components': 5.1.26
prettier: 2.7.1
packages:
/@authorizerdev/authorizer-js/1.2.1:
resolution: {integrity: sha512-/nFARvsHyZUsGFKrcYi8hgpnbThYR/NMJ2BJdQpWy/x7QsBnfLeCChBYWncbYHSIjFCa5PPKKfvhXM56HqVqsw==}
engines: {node: '>=10'}
dependencies:
cross-fetch: 3.1.5
transitivePeerDependencies:
- encoding
dev: false
/@authorizerdev/authorizer-react/1.1.9_react@17.0.2:
resolution: {integrity: sha512-BlB4ixEm9nf+yjZ9OqIWbx5fMTmzeByEsNDAd5iYkt6HB+3Sk53DGiO5h6SgJznzPyqAwl8yg6y/QgbZreDTFA==}
engines: {node: '>=10'}
peerDependencies:
react: '>=16'
dependencies:
'@authorizerdev/authorizer-js': 1.2.1
react: 17.0.2
transitivePeerDependencies:
- encoding
dev: false
/@babel/code-frame/7.18.6:
resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/highlight': 7.18.6
dev: false
/@babel/generator/7.21.3:
resolution: {integrity: sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.21.3
'@jridgewell/gen-mapping': 0.3.2
'@jridgewell/trace-mapping': 0.3.17
jsesc: 2.5.2
dev: false
/@babel/helper-annotate-as-pure/7.18.6:
resolution: {integrity: sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.21.3
dev: false
/@babel/helper-environment-visitor/7.18.9:
resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==}
engines: {node: '>=6.9.0'}
dev: false
/@babel/helper-function-name/7.21.0:
resolution: {integrity: sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/template': 7.20.7
'@babel/types': 7.21.3
dev: false
/@babel/helper-hoist-variables/7.18.6:
resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.21.3
dev: false
/@babel/helper-module-imports/7.18.6:
resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.21.3
dev: false
/@babel/helper-split-export-declaration/7.18.6:
resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.21.3
dev: false
/@babel/helper-string-parser/7.19.4:
resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==}
engines: {node: '>=6.9.0'}
dev: false
/@babel/helper-validator-identifier/7.19.1:
resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==}
engines: {node: '>=6.9.0'}
dev: false
/@babel/highlight/7.18.6:
resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/helper-validator-identifier': 7.19.1
chalk: 2.4.2
js-tokens: 4.0.0
dev: false
/@babel/parser/7.21.3:
resolution: {integrity: sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==}
engines: {node: '>=6.0.0'}
hasBin: true
dependencies:
'@babel/types': 7.21.3
dev: false
/@babel/runtime/7.21.0:
resolution: {integrity: sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==}
engines: {node: '>=6.9.0'}
dependencies:
regenerator-runtime: 0.13.11
dev: false
/@babel/template/7.20.7:
resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/code-frame': 7.18.6
'@babel/parser': 7.21.3
'@babel/types': 7.21.3
dev: false
/@babel/traverse/7.21.3_supports-color@5.5.0:
resolution: {integrity: sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/code-frame': 7.18.6
'@babel/generator': 7.21.3
'@babel/helper-environment-visitor': 7.18.9
'@babel/helper-function-name': 7.21.0
'@babel/helper-hoist-variables': 7.18.6
'@babel/helper-split-export-declaration': 7.18.6
'@babel/parser': 7.21.3
'@babel/types': 7.21.3
debug: 4.3.4_supports-color@5.5.0
globals: 11.12.0
transitivePeerDependencies:
- supports-color
dev: false
/@babel/types/7.21.3:
resolution: {integrity: sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/helper-string-parser': 7.19.4
'@babel/helper-validator-identifier': 7.19.1
to-fast-properties: 2.0.0
dev: false
/@emotion/is-prop-valid/1.2.0:
resolution: {integrity: sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==}
dependencies:
'@emotion/memoize': 0.8.0
dev: false
/@emotion/memoize/0.8.0:
resolution: {integrity: sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==}
dev: false
/@emotion/stylis/0.8.5:
resolution: {integrity: sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==}
dev: false
/@emotion/unitless/0.7.5:
resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==}
dev: false
/@jridgewell/gen-mapping/0.3.2:
resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==}
engines: {node: '>=6.0.0'}
dependencies:
'@jridgewell/set-array': 1.1.2
'@jridgewell/sourcemap-codec': 1.4.14
'@jridgewell/trace-mapping': 0.3.17
dev: false
/@jridgewell/resolve-uri/3.1.0:
resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==}
engines: {node: '>=6.0.0'}
dev: false
/@jridgewell/set-array/1.1.2:
resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==}
engines: {node: '>=6.0.0'}
dev: false
/@jridgewell/sourcemap-codec/1.4.14:
resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==}
dev: false
/@jridgewell/trace-mapping/0.3.17:
resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==}
dependencies:
'@jridgewell/resolve-uri': 3.1.0
'@jridgewell/sourcemap-codec': 1.4.14
dev: false
/@types/history/4.7.11:
resolution: {integrity: sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==}
dev: true
/@types/hoist-non-react-statics/3.3.1:
resolution: {integrity: sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==}
dependencies:
'@types/react': 17.0.53
hoist-non-react-statics: 3.3.2
dev: true
/@types/prop-types/15.7.5:
resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==}
/@types/react-dom/17.0.19:
resolution: {integrity: sha512-PiYG40pnQRdPHnlf7tZnp0aQ6q9tspYr72vD61saO6zFCybLfMqwUCN0va1/P+86DXn18ZWeW30Bk7xlC5eEAQ==}
dependencies:
'@types/react': 17.0.53
dev: false
/@types/react-router-dom/5.3.3:
resolution: {integrity: sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==}
dependencies:
'@types/history': 4.7.11
'@types/react': 17.0.53
'@types/react-router': 5.1.20
dev: true
/@types/react-router/5.1.20:
resolution: {integrity: sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==}
dependencies:
'@types/history': 4.7.11
'@types/react': 17.0.53
dev: true
/@types/react/17.0.53:
resolution: {integrity: sha512-1yIpQR2zdYu1Z/dc1OxC+MA6GR240u3gcnP4l6mvj/PJiVaqHsQPmWttsvHsfnhfPbU2FuGmo0wSITPygjBmsw==}
dependencies:
'@types/prop-types': 15.7.5
'@types/scheduler': 0.16.3
csstype: 3.1.1
/@types/scheduler/0.16.3:
resolution: {integrity: sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==}
/@types/styled-components/5.1.26:
resolution: {integrity: sha512-KuKJ9Z6xb93uJiIyxo/+ksS7yLjS1KzG6iv5i78dhVg/X3u5t1H7juRWqVmodIdz6wGVaIApo1u01kmFRdJHVw==}
dependencies:
'@types/hoist-non-react-statics': 3.3.1
'@types/react': 17.0.53
csstype: 3.1.1
dev: true
/ansi-styles/3.2.1:
resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
engines: {node: '>=4'}
dependencies:
color-convert: 1.9.3
dev: false
/babel-plugin-styled-components/2.0.7_styled-components@5.3.9:
resolution: {integrity: sha512-i7YhvPgVqRKfoQ66toiZ06jPNA3p6ierpfUuEWxNF+fV27Uv5gxBkf8KZLHUCc1nFA9j6+80pYoIpqCeyW3/bA==}
peerDependencies:
styled-components: '>= 2'
dependencies:
'@babel/helper-annotate-as-pure': 7.18.6
'@babel/helper-module-imports': 7.18.6
babel-plugin-syntax-jsx: 6.18.0
lodash: 4.17.21
picomatch: 2.3.1
styled-components: 5.3.9_fane7jikarojcev26y27hpbhu4
dev: false
/babel-plugin-syntax-jsx/6.18.0:
resolution: {integrity: sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw==}
dev: false
/camelize/1.0.1:
resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==}
dev: false
/chalk/2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
engines: {node: '>=4'}
dependencies:
ansi-styles: 3.2.1
escape-string-regexp: 1.0.5
supports-color: 5.5.0
dev: false
/color-convert/1.9.3:
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
dependencies:
color-name: 1.1.3
dev: false
/color-name/1.1.3:
resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
dev: false
/cross-fetch/3.1.5:
resolution: {integrity: sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==}
dependencies:
node-fetch: 2.6.7
transitivePeerDependencies:
- encoding
dev: false
/css-color-keywords/1.0.0:
resolution: {integrity: sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==}
engines: {node: '>=4'}
dev: false
/css-to-react-native/3.2.0:
resolution: {integrity: sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==}
dependencies:
camelize: 1.0.1
css-color-keywords: 1.0.0
postcss-value-parser: 4.2.0
dev: false
/csstype/3.1.1:
resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==}
/debug/4.3.4_supports-color@5.5.0:
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
dependencies:
ms: 2.1.2
supports-color: 5.5.0
dev: false
/esbuild/0.12.29:
resolution: {integrity: sha512-w/XuoBCSwepyiZtIRsKsetiLDUVGPVw1E/R3VTFSecIy8UR7Cq3SOtwKHJMFoVqqVG36aGkzh4e8BvpO1Fdc7g==}
hasBin: true
requiresBuild: true
dev: false
/escape-string-regexp/1.0.5:
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
engines: {node: '>=0.8.0'}
dev: false
/globals/11.12.0:
resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
engines: {node: '>=4'}
dev: false
/has-flag/3.0.0:
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
engines: {node: '>=4'}
dev: false
/history/4.10.1:
resolution: {integrity: sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==}
dependencies:
'@babel/runtime': 7.21.0
loose-envify: 1.4.0
resolve-pathname: 3.0.0
tiny-invariant: 1.3.1
tiny-warning: 1.0.3
value-equal: 1.0.1
dev: false
/hoist-non-react-statics/3.3.2:
resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==}
dependencies:
react-is: 16.13.1
/isarray/0.0.1:
resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==}
dev: false
/js-tokens/4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
dev: false
/jsesc/2.5.2:
resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==}
engines: {node: '>=4'}
hasBin: true
dev: false
/lodash/4.17.21:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
dev: false
/loose-envify/1.4.0:
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
hasBin: true
dependencies:
js-tokens: 4.0.0
dev: false
/ms/2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
dev: false
/node-fetch/2.6.7:
resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==}
engines: {node: 4.x || >=6.0.0}
peerDependencies:
encoding: ^0.1.0
peerDependenciesMeta:
encoding:
optional: true
dependencies:
whatwg-url: 5.0.0
dev: false
/object-assign/4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
dev: false
/path-to-regexp/1.8.0:
resolution: {integrity: sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==}
dependencies:
isarray: 0.0.1
dev: false
/picomatch/2.3.1:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
dev: false
/postcss-value-parser/4.2.0:
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
dev: false
/prettier/2.7.1:
resolution: {integrity: sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==}
engines: {node: '>=10.13.0'}
hasBin: true
dev: true
/prop-types/15.8.1:
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
dependencies:
loose-envify: 1.4.0
object-assign: 4.1.1
react-is: 16.13.1
dev: false
/react-dom/17.0.2_react@17.0.2:
resolution: {integrity: sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==}
peerDependencies:
react: 17.0.2
dependencies:
loose-envify: 1.4.0
object-assign: 4.1.1
react: 17.0.2
scheduler: 0.20.2
dev: false
/react-is/16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
/react-is/17.0.2:
resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
dev: false
/react-router-dom/5.3.4_react@17.0.2:
resolution: {integrity: sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ==}
peerDependencies:
react: '>=15'
dependencies:
'@babel/runtime': 7.21.0
history: 4.10.1
loose-envify: 1.4.0
prop-types: 15.8.1
react: 17.0.2
react-router: 5.3.4_react@17.0.2
tiny-invariant: 1.3.1
tiny-warning: 1.0.3
dev: false
/react-router/5.3.4_react@17.0.2:
resolution: {integrity: sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==}
peerDependencies:
react: '>=15'
dependencies:
'@babel/runtime': 7.21.0
history: 4.10.1
hoist-non-react-statics: 3.3.2
loose-envify: 1.4.0
path-to-regexp: 1.8.0
prop-types: 15.8.1
react: 17.0.2
react-is: 16.13.1
tiny-invariant: 1.3.1
tiny-warning: 1.0.3
dev: false
/react/17.0.2:
resolution: {integrity: sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==}
engines: {node: '>=0.10.0'}
dependencies:
loose-envify: 1.4.0
object-assign: 4.1.1
dev: false
/regenerator-runtime/0.13.11:
resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
dev: false
/resolve-pathname/3.0.0:
resolution: {integrity: sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==}
dev: false
/scheduler/0.20.2:
resolution: {integrity: sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==}
dependencies:
loose-envify: 1.4.0
object-assign: 4.1.1
dev: false
/shallowequal/1.1.0:
resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==}
dev: false
/styled-components/5.3.9_fane7jikarojcev26y27hpbhu4:
resolution: {integrity: sha512-Aj3kb13B75DQBo2oRwRa/APdB5rSmwUfN5exyarpX+x/tlM/rwZA2vVk2vQgVSP6WKaZJHWwiFrzgHt+CLtB4A==}
engines: {node: '>=10'}
peerDependencies:
react: '>= 16.8.0'
react-dom: '>= 16.8.0'
react-is: '>= 16.8.0'
dependencies:
'@babel/helper-module-imports': 7.18.6
'@babel/traverse': 7.21.3_supports-color@5.5.0
'@emotion/is-prop-valid': 1.2.0
'@emotion/stylis': 0.8.5
'@emotion/unitless': 0.7.5
babel-plugin-styled-components: 2.0.7_styled-components@5.3.9
css-to-react-native: 3.2.0
hoist-non-react-statics: 3.3.2
react: 17.0.2
react-dom: 17.0.2_react@17.0.2
react-is: 17.0.2
shallowequal: 1.1.0
supports-color: 5.5.0
dev: false
/supports-color/5.5.0:
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
engines: {node: '>=4'}
dependencies:
has-flag: 3.0.0
dev: false
/tiny-invariant/1.3.1:
resolution: {integrity: sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==}
dev: false
/tiny-warning/1.0.3:
resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==}
dev: false
/to-fast-properties/2.0.0:
resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
engines: {node: '>=4'}
dev: false
/tr46/0.0.3:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
dev: false
/typescript/4.9.5:
resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==}
engines: {node: '>=4.2.0'}
hasBin: true
dev: false
/value-equal/1.0.1:
resolution: {integrity: sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==}
dev: false
/webidl-conversions/3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
dev: false
/whatwg-url/5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
dependencies:
tr46: 0.0.3
webidl-conversions: 3.0.1
dev: false

588
app/yarn.lock Normal file
View File

@@ -0,0 +1,588 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@authorizerdev/authorizer-js@^1.1.4":
version "1.1.4"
resolved "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-1.1.4.tgz"
integrity sha512-oLp3XMYU9xHlg5urpAOaw6NrKQ07SaI8oAi1BAMHZWivv5qi2V+6roey0y9MdvCUmsMULBBcZg87kuoMTXRJOw==
dependencies:
cross-fetch "^3.1.5"
"@authorizerdev/authorizer-react@^1.1.8":
version "1.1.8"
resolved "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.1.8.tgz"
integrity sha512-5v9Zc7ZxiEQEHT1Fwj1jvEemdckcrn7mZPkdmJ/z0YOWNbF20ZpM4WsfrtJB+d293m7Rb1QKwzdFPkYINMh5tw==
dependencies:
"@authorizerdev/authorizer-js" "^1.1.4"
"@babel/code-frame@^7.16.7":
version "7.16.7"
resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz"
integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==
dependencies:
"@babel/highlight" "^7.16.7"
"@babel/generator@^7.16.8":
version "7.16.8"
resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz"
integrity sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw==
dependencies:
"@babel/types" "^7.16.8"
jsesc "^2.5.1"
source-map "^0.5.0"
"@babel/helper-annotate-as-pure@^7.16.0":
version "7.16.7"
resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz"
integrity sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==
dependencies:
"@babel/types" "^7.16.7"
"@babel/helper-environment-visitor@^7.16.7":
version "7.16.7"
resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz"
integrity sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==
dependencies:
"@babel/types" "^7.16.7"
"@babel/helper-function-name@^7.16.7":
version "7.16.7"
resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz"
integrity sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==
dependencies:
"@babel/helper-get-function-arity" "^7.16.7"
"@babel/template" "^7.16.7"
"@babel/types" "^7.16.7"
"@babel/helper-get-function-arity@^7.16.7":
version "7.16.7"
resolved "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz"
integrity sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==
dependencies:
"@babel/types" "^7.16.7"
"@babel/helper-hoist-variables@^7.16.7":
version "7.16.7"
resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz"
integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==
dependencies:
"@babel/types" "^7.16.7"
"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.16.0":
version "7.16.7"
resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz"
integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==
dependencies:
"@babel/types" "^7.16.7"
"@babel/helper-split-export-declaration@^7.16.7":
version "7.16.7"
resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz"
integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==
dependencies:
"@babel/types" "^7.16.7"
"@babel/helper-validator-identifier@^7.16.7":
version "7.16.7"
resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz"
integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==
"@babel/highlight@^7.16.7":
version "7.16.10"
resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz"
integrity sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==
dependencies:
"@babel/helper-validator-identifier" "^7.16.7"
chalk "^2.0.0"
js-tokens "^4.0.0"
"@babel/parser@^7.16.10", "@babel/parser@^7.16.7":
version "7.16.12"
resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.16.12.tgz"
integrity sha512-VfaV15po8RiZssrkPweyvbGVSe4x2y+aciFCgn0n0/SJMR22cwofRV1mtnJQYcSB1wUTaA/X1LnA3es66MCO5A==
"@babel/runtime@^7.1.2", "@babel/runtime@^7.12.1":
version "7.14.8"
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz"
integrity sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/template@^7.16.7":
version "7.16.7"
resolved "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz"
integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==
dependencies:
"@babel/code-frame" "^7.16.7"
"@babel/parser" "^7.16.7"
"@babel/types" "^7.16.7"
"@babel/traverse@^7.4.5":
version "7.16.10"
resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.10.tgz"
integrity sha512-yzuaYXoRJBGMlBhsMJoUW7G1UmSb/eXr/JHYM/MsOJgavJibLwASijW7oXBdw3NQ6T0bW7Ty5P/VarOs9cHmqw==
dependencies:
"@babel/code-frame" "^7.16.7"
"@babel/generator" "^7.16.8"
"@babel/helper-environment-visitor" "^7.16.7"
"@babel/helper-function-name" "^7.16.7"
"@babel/helper-hoist-variables" "^7.16.7"
"@babel/helper-split-export-declaration" "^7.16.7"
"@babel/parser" "^7.16.10"
"@babel/types" "^7.16.8"
debug "^4.1.0"
globals "^11.1.0"
"@babel/types@^7.16.7", "@babel/types@^7.16.8":
version "7.16.8"
resolved "https://registry.npmjs.org/@babel/types/-/types-7.16.8.tgz"
integrity sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg==
dependencies:
"@babel/helper-validator-identifier" "^7.16.7"
to-fast-properties "^2.0.0"
"@emotion/is-prop-valid@^0.8.8":
version "0.8.8"
resolved "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz"
integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==
dependencies:
"@emotion/memoize" "0.7.4"
"@emotion/memoize@0.7.4":
version "0.7.4"
resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz"
integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==
"@emotion/stylis@^0.8.4":
version "0.8.5"
resolved "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz"
integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==
"@emotion/unitless@^0.7.4":
version "0.7.5"
resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz"
integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==
"@types/history@*":
version "4.7.9"
resolved "https://registry.npmjs.org/@types/history/-/history-4.7.9.tgz"
integrity sha512-MUc6zSmU3tEVnkQ78q0peeEjKWPUADMlC/t++2bI8WnAG2tvYRPIgHG8lWkXwqc8MsUF6Z2MOf+Mh5sazOmhiQ==
"@types/hoist-non-react-statics@*":
version "3.3.1"
resolved "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz"
integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
dependencies:
"@types/react" "*"
hoist-non-react-statics "^3.3.0"
"@types/prop-types@*":
version "15.7.4"
resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz"
integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==
"@types/react-dom@^17.0.9":
version "17.0.9"
resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.9.tgz"
integrity sha512-wIvGxLfgpVDSAMH5utdL9Ngm5Owu0VsGmldro3ORLXV8CShrL8awVj06NuEXFQ5xyaYfdca7Sgbk/50Ri1GdPg==
dependencies:
"@types/react" "*"
"@types/react-router-dom@^5.1.8":
version "5.1.8"
resolved "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.8.tgz"
integrity sha512-03xHyncBzG0PmDmf8pf3rehtjY0NpUj7TIN46FrT5n1ZWHPZvXz32gUyNboJ+xsL8cpg8bQVLcllptcQHvocrw==
dependencies:
"@types/history" "*"
"@types/react" "*"
"@types/react-router" "*"
"@types/react-router@*":
version "5.1.16"
resolved "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.16.tgz"
integrity sha512-8d7nR/fNSqlTFGHti0R3F9WwIertOaaA1UEB8/jr5l5mDMOs4CidEgvvYMw4ivqrBK+vtVLxyTj2P+Pr/dtgzg==
dependencies:
"@types/history" "*"
"@types/react" "*"
"@types/react@*", "@types/react@^17.0.15":
version "17.0.15"
resolved "https://registry.npmjs.org/@types/react/-/react-17.0.15.tgz"
integrity sha512-uTKHDK9STXFHLaKv6IMnwp52fm0hwU+N89w/p9grdUqcFA6WuqDyPhaWopbNyE1k/VhgzmHl8pu1L4wITtmlLw==
dependencies:
"@types/prop-types" "*"
"@types/scheduler" "*"
csstype "^3.0.2"
"@types/scheduler@*":
version "0.16.2"
resolved "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz"
integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==
"@types/styled-components@^5.1.11":
version "5.1.25"
resolved "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.25.tgz"
integrity sha512-fgwl+0Pa8pdkwXRoVPP9JbqF0Ivo9llnmsm+7TCI330kbPIFd9qv1Lrhr37shf4tnxCOSu+/IgqM7uJXLWZZNQ==
dependencies:
"@types/hoist-non-react-statics" "*"
"@types/react" "*"
csstype "^3.0.2"
ansi-styles@^3.2.1:
version "3.2.1"
resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz"
integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
dependencies:
color-convert "^1.9.0"
"babel-plugin-styled-components@>= 1.12.0":
version "2.0.2"
resolved "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.0.2.tgz"
integrity sha512-7eG5NE8rChnNTDxa6LQfynwgHTVOYYaHJbUYSlOhk8QBXIQiMBKq4gyfHBBKPrxUcVBXVJL61ihduCpCQbuNbw==
dependencies:
"@babel/helper-annotate-as-pure" "^7.16.0"
"@babel/helper-module-imports" "^7.16.0"
babel-plugin-syntax-jsx "^6.18.0"
lodash "^4.17.11"
babel-plugin-syntax-jsx@^6.18.0:
version "6.18.0"
resolved "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz"
integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=
camelize@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz"
integrity sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=
chalk@^2.0.0:
version "2.4.2"
resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
dependencies:
ansi-styles "^3.2.1"
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
color-convert@^1.9.0:
version "1.9.3"
resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz"
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
dependencies:
color-name "1.1.3"
color-name@1.1.3:
version "1.1.3"
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz"
integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
cross-fetch@^3.1.5:
version "3.1.5"
resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz"
integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==
dependencies:
node-fetch "2.6.7"
css-color-keywords@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz"
integrity sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU=
css-to-react-native@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.0.0.tgz"
integrity sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ==
dependencies:
camelize "^1.0.0"
css-color-keywords "^1.0.0"
postcss-value-parser "^4.0.2"
csstype@^3.0.2:
version "3.0.8"
resolved "https://registry.npmjs.org/csstype/-/csstype-3.0.8.tgz"
integrity sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==
debug@^4.1.0:
version "4.3.3"
resolved "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz"
integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==
dependencies:
ms "2.1.2"
esbuild@^0.12.17:
version "0.12.17"
resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.12.17.tgz"
integrity sha512-GshKJyVYUnlSXIZj/NheC2O0Kblh42CS7P1wJyTbbIHevTG4jYMS9NNw8EOd8dDWD0dzydYHS01MpZoUcQXB4g==
escape-string-regexp@^1.0.5:
version "1.0.5"
resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz"
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
globals@^11.1.0:
version "11.12.0"
resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz"
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
has-flag@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz"
integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
history@^4.9.0:
version "4.10.1"
resolved "https://registry.npmjs.org/history/-/history-4.10.1.tgz"
integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==
dependencies:
"@babel/runtime" "^7.1.2"
loose-envify "^1.2.0"
resolve-pathname "^3.0.0"
tiny-invariant "^1.0.2"
tiny-warning "^1.0.0"
value-equal "^1.0.1"
hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0:
version "3.3.2"
resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz"
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
dependencies:
react-is "^16.7.0"
isarray@0.0.1:
version "0.0.1"
resolved "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
jsesc@^2.5.1:
version "2.5.2"
resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz"
integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
lodash@^4.17.11:
version "4.17.21"
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
version "1.4.0"
resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
dependencies:
js-tokens "^3.0.0 || ^4.0.0"
mini-create-react-context@^0.4.0:
version "0.4.1"
resolved "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz"
integrity sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==
dependencies:
"@babel/runtime" "^7.12.1"
tiny-warning "^1.0.3"
ms@2.1.2:
version "2.1.2"
resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
node-fetch@2.6.7:
version "2.6.7"
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
dependencies:
whatwg-url "^5.0.0"
object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
path-to-regexp@^1.7.0:
version "1.8.0"
resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz"
integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==
dependencies:
isarray "0.0.1"
postcss-value-parser@^4.0.2:
version "4.2.0"
resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz"
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
prettier@2.7.1:
version "2.7.1"
resolved "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz"
integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==
prop-types@^15.0.0, prop-types@^15.6.2:
version "15.7.2"
resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
dependencies:
loose-envify "^1.4.0"
object-assign "^4.1.1"
react-is "^16.8.1"
react-dom@^17.0.2, "react-dom@>= 16.8.0":
version "17.0.2"
resolved "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz"
integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
scheduler "^0.20.2"
react-is@^16.6.0:
version "16.13.1"
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-is@^16.7.0:
version "16.13.1"
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-is@^16.8.1:
version "16.13.1"
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-is@^17.0.2, "react-is@>= 16.8.0":
version "17.0.2"
resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
react-router-dom@^5.2.0:
version "5.2.0"
resolved "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz"
integrity sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==
dependencies:
"@babel/runtime" "^7.1.2"
history "^4.9.0"
loose-envify "^1.3.1"
prop-types "^15.6.2"
react-router "5.2.0"
tiny-invariant "^1.0.2"
tiny-warning "^1.0.0"
react-router@5.2.0:
version "5.2.0"
resolved "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz"
integrity sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==
dependencies:
"@babel/runtime" "^7.1.2"
history "^4.9.0"
hoist-non-react-statics "^3.1.0"
loose-envify "^1.3.1"
mini-create-react-context "^0.4.0"
path-to-regexp "^1.7.0"
prop-types "^15.6.2"
react-is "^16.6.0"
tiny-invariant "^1.0.2"
tiny-warning "^1.0.0"
"react@^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0", react@^17.0.2, "react@>= 16.8.0", react@>=15, react@>=16, react@17.0.2:
version "17.0.2"
resolved "https://registry.npmjs.org/react/-/react-17.0.2.tgz"
integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
regenerator-runtime@^0.13.4:
version "0.13.9"
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz"
integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
resolve-pathname@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz"
integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==
scheduler@^0.20.2:
version "0.20.2"
resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz"
integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
shallowequal@^1.1.0:
version "1.1.0"
resolved "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz"
integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==
source-map@^0.5.0:
version "0.5.7"
resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
styled-components@^5.3.0, "styled-components@>= 2":
version "5.3.3"
resolved "https://registry.npmjs.org/styled-components/-/styled-components-5.3.3.tgz"
integrity sha512-++4iHwBM7ZN+x6DtPPWkCI4vdtwumQ+inA/DdAsqYd4SVgUKJie5vXyzotA00ttcFdQkCng7zc6grwlfIfw+lw==
dependencies:
"@babel/helper-module-imports" "^7.0.0"
"@babel/traverse" "^7.4.5"
"@emotion/is-prop-valid" "^0.8.8"
"@emotion/stylis" "^0.8.4"
"@emotion/unitless" "^0.7.4"
babel-plugin-styled-components ">= 1.12.0"
css-to-react-native "^3.0.0"
hoist-non-react-statics "^3.0.0"
shallowequal "^1.1.0"
supports-color "^5.5.0"
supports-color@^5.3.0, supports-color@^5.5.0:
version "5.5.0"
resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz"
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
dependencies:
has-flag "^3.0.0"
tiny-invariant@^1.0.2:
version "1.1.0"
resolved "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz"
integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==
tiny-warning@^1.0.0, tiny-warning@^1.0.3:
version "1.0.3"
resolved "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz"
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
to-fast-properties@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz"
integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
tr46@~0.0.3:
version "0.0.3"
resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz"
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
typescript@^4.3.5:
version "4.3.5"
resolved "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz"
integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==
value-equal@^1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz"
integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz"
integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
whatwg-url@^5.0.0:
version "5.0.0"
resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz"
integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
dependencies:
tr46 "~0.0.3"
webidl-conversions "^3.0.0"

View File

@@ -44,7 +44,7 @@ const DeleteEmailTemplateModal = ({
title: capitalizeFirstLetter(res.error.message),
isClosable: true,
status: 'error',
position: 'bottom-right',
position: 'top-right',
});
return;
@@ -53,7 +53,7 @@ const DeleteEmailTemplateModal = ({
title: capitalizeFirstLetter(res.data?._delete_email_template.message),
isClosable: true,
status: 'success',
position: 'bottom-right',
position: 'top-right',
});
}
onClose();

View File

@@ -51,7 +51,7 @@ const DeleteUserModal = ({
title: capitalizeFirstLetter(res.error.message),
isClosable: true,
status: 'error',
position: 'bottom-right',
position: 'top-right',
});
return;
@@ -60,7 +60,7 @@ const DeleteUserModal = ({
title: capitalizeFirstLetter(res.data?._delete_user.message),
isClosable: true,
status: 'success',
position: 'bottom-right',
position: 'top-right',
});
}
onClose();

View File

@@ -44,7 +44,7 @@ const DeleteWebhookModal = ({
title: capitalizeFirstLetter(res.error.message),
isClosable: true,
status: 'error',
position: 'bottom-right',
position: 'top-right',
});
return;
@@ -53,7 +53,7 @@ const DeleteWebhookModal = ({
title: capitalizeFirstLetter(res.data?._delete_webhook.message),
isClosable: true,
status: 'success',
position: 'bottom-right',
position: 'top-right',
});
}
onClose();

View File

@@ -104,14 +104,14 @@ const EditUserModal = ({
title: 'User data update failed',
isClosable: true,
status: 'error',
position: 'bottom-right',
position: 'top-right',
});
} else if (res.data?._update_user?.id) {
toast({
title: 'User data update successful',
isClosable: true,
status: 'success',
position: 'bottom-right',
position: 'top-right',
});
}
onClose();

View File

@@ -43,7 +43,7 @@ const JSTConfigurations = ({
title: `JWT config copied successfully`,
isClosable: true,
status: 'success',
position: 'bottom-right',
position: 'top-right',
});
} catch (err) {
console.error({
@@ -54,14 +54,13 @@ const JSTConfigurations = ({
title: `Failed to copy JWT config`,
isClosable: true,
status: 'error',
position: 'bottom-right',
position: 'top-right',
});
}
};
return (
<div>
{' '}
<Flex
borderRadius={5}
width="100%"

View File

@@ -16,8 +16,15 @@ import {
FaLinkedin,
FaApple,
FaTwitter,
FaMicrosoft,
} from 'react-icons/fa';
import { TextInputType, HiddenInputType } from '../../constants';
import {
TextInputType,
HiddenInputType,
ResponseModes,
ResponseTypes,
SelectInputType,
} from '../../constants';
const OAuthConfig = ({
envVariables,
@@ -69,6 +76,42 @@ const OAuthConfig = ({
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Default Response Type:</Text>
</Flex>
<Flex
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '2'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={SelectInputType.DEFAULT_AUTHORIZE_RESPONSE_TYPE}
value={SelectInputType}
options={ResponseTypes}
/>
</Flex>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Default Response Mode:</Text>
</Flex>
<Flex
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '2'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={SelectInputType.DEFAULT_AUTHORIZE_RESPONSE_MODE}
value={SelectInputType}
options={ResponseModes}
/>
</Flex>
</Flex>
</Stack>
<Divider mt={5} mb={2} color="blackAlpha.700" />
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={4}>
@@ -303,6 +346,57 @@ const OAuthConfig = ({
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Center
w={isNotSmallerScreen ? '55px' : '35px'}
h="35px"
marginRight="1.5%"
border="1px solid #3b5998"
borderRadius="5px"
>
<FaMicrosoft />
</Center>
<Center
w={isNotSmallerScreen ? '35%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID}
placeholder="Microsoft Active Directory TenantID"
/>
</Center>
<Center
w={isNotSmallerScreen ? '35%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.MICROSOFT_CLIENT_ID}
placeholder="Microsoft Client ID"
/>
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.MICROSOFT_CLIENT_SECRET}
placeholder="Microsoft Client Secret"
/>
</Center>
</Flex>
</Stack>
</Box>
</div>

View File

@@ -73,7 +73,7 @@ const GenerateKeysModal = ({ jwtType, getData }: propTypes) => {
title: 'Error occurred generating jwt keys',
isClosable: true,
status: 'error',
position: 'bottom-right',
position: 'top-right',
});
closeHandler();
} else {
@@ -107,7 +107,7 @@ const GenerateKeysModal = ({ jwtType, getData }: propTypes) => {
title: 'Error occurred setting jwt keys',
isClosable: true,
status: 'error',
position: 'bottom-right',
position: 'top-right',
});
return;
@@ -116,7 +116,7 @@ const GenerateKeysModal = ({ jwtType, getData }: propTypes) => {
title: 'JWT keys updated successfully',
isClosable: true,
status: 'success',
position: 'bottom-right',
position: 'top-right',
});
closeHandler();
};

View File

@@ -105,7 +105,7 @@ const InviteMembersModal = ({
title: 'Invites sent successfully!',
isClosable: true,
status: 'success',
position: 'bottom-right',
position: 'top-right',
});
setLoading(false);
updateUserList();
@@ -117,7 +117,7 @@ const InviteMembersModal = ({
title: error?.message || 'Error occurred, try again!',
isClosable: true,
status: 'error',
position: 'bottom-right',
position: 'top-right',
});
setLoading(false);
}

View File

@@ -173,7 +173,7 @@ const UpdateEmailTemplate = ({
title: capitalizeFirstLetter(res.error.message),
isClosable: true,
status: 'error',
position: 'bottom-right',
position: 'top-right',
});
} else if (
res.data?._add_email_template ||
@@ -186,7 +186,7 @@ const UpdateEmailTemplate = ({
),
isClosable: true,
status: 'success',
position: 'bottom-right',
position: 'top-right',
});
setTemplateData({
...initTemplateData,

View File

@@ -63,6 +63,7 @@ interface headersValidatorDataType {
interface selecetdWebhookDataTypes {
[WebhookInputDataFields.ID]: string;
[WebhookInputDataFields.EVENT_NAME]: string;
[WebhookInputDataFields.EVENT_DESCRIPTION]?: string;
[WebhookInputDataFields.ENDPOINT]: string;
[WebhookInputDataFields.ENABLED]: boolean;
[WebhookInputDataFields.HEADERS]?: Record<string, string>;
@@ -86,6 +87,7 @@ const initHeadersValidatorData: headersValidatorDataType = {
interface webhookDataType {
[WebhookInputDataFields.EVENT_NAME]: string;
[WebhookInputDataFields.EVENT_DESCRIPTION]?: string;
[WebhookInputDataFields.ENDPOINT]: string;
[WebhookInputDataFields.ENABLED]: boolean;
[WebhookInputDataFields.HEADERS]: headersDataType[];
@@ -98,6 +100,7 @@ interface validatorDataType {
const initWebhookData: webhookDataType = {
[WebhookInputDataFields.EVENT_NAME]: webhookEventNames['User login'],
[WebhookInputDataFields.EVENT_DESCRIPTION]: '',
[WebhookInputDataFields.ENDPOINT]: '',
[WebhookInputDataFields.ENABLED]: true,
[WebhookInputDataFields.HEADERS]: [{ ...initHeadersData }],
@@ -144,6 +147,9 @@ const UpdateWebhookModal = ({
case WebhookInputDataFields.EVENT_NAME:
setWebhook({ ...webhook, [inputType]: value });
break;
case WebhookInputDataFields.EVENT_DESCRIPTION:
setWebhook({ ...webhook, [inputType]: value });
break;
case WebhookInputDataFields.ENDPOINT:
setWebhook({ ...webhook, [inputType]: value });
setValidator({
@@ -246,6 +252,8 @@ const UpdateWebhookModal = ({
let params: any = {
[WebhookInputDataFields.EVENT_NAME]:
webhook[WebhookInputDataFields.EVENT_NAME],
[WebhookInputDataFields.EVENT_DESCRIPTION]:
webhook[WebhookInputDataFields.EVENT_DESCRIPTION],
[WebhookInputDataFields.ENDPOINT]:
webhook[WebhookInputDataFields.ENDPOINT],
[WebhookInputDataFields.ENABLED]: webhook[WebhookInputDataFields.ENABLED],
@@ -290,7 +298,7 @@ const UpdateWebhookModal = ({
title: capitalizeFirstLetter(res.error.message),
isClosable: true,
status: 'error',
position: 'bottom-right',
position: 'top-right',
});
} else if (res.data?._add_webhook || res.data?._update_webhook) {
toast({
@@ -299,7 +307,7 @@ const UpdateWebhookModal = ({
),
isClosable: true,
status: 'success',
position: 'bottom-right',
position: 'top-right',
});
setWebhook({
...initWebhookData,
@@ -402,7 +410,9 @@ const UpdateWebhookModal = ({
<Flex flex="3">
<Select
size="md"
value={webhook[WebhookInputDataFields.EVENT_NAME]}
value={
webhook[WebhookInputDataFields.EVENT_NAME].split('-')[0]
}
onChange={(e) =>
inputChangehandler(
WebhookInputDataFields.EVENT_NAME,
@@ -420,6 +430,30 @@ const UpdateWebhookModal = ({
</Select>
</Flex>
</Flex>
<Flex
width="100%"
justifyContent="start"
alignItems="center"
marginBottom="5%"
>
<Flex flex="1">Event Description</Flex>
<Flex flex="3">
<InputGroup size="md">
<Input
pr="4.5rem"
type="text"
placeholder="User event"
value={webhook[WebhookInputDataFields.EVENT_DESCRIPTION]}
onChange={(e) =>
inputChangehandler(
WebhookInputDataFields.EVENT_DESCRIPTION,
e.currentTarget.value,
)
}
/>
</InputGroup>
</Flex>
</Flex>
<Flex
width="100%"
justifyContent="start"

View File

@@ -10,6 +10,8 @@ export const TextInputType = {
LINKEDIN_CLIENT_ID: 'LINKEDIN_CLIENT_ID',
APPLE_CLIENT_ID: 'APPLE_CLIENT_ID',
TWITTER_CLIENT_ID: 'TWITTER_CLIENT_ID',
MICROSOFT_CLIENT_ID: 'MICROSOFT_CLIENT_ID',
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID: 'MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID',
JWT_ROLE_CLAIM: 'JWT_ROLE_CLAIM',
REDIS_URL: 'REDIS_URL',
SMTP_HOST: 'SMTP_HOST',
@@ -38,6 +40,7 @@ export const HiddenInputType = {
LINKEDIN_CLIENT_SECRET: 'LINKEDIN_CLIENT_SECRET',
APPLE_CLIENT_SECRET: 'APPLE_CLIENT_SECRET',
TWITTER_CLIENT_SECRET: 'TWITTER_CLIENT_SECRET',
MICROSOFT_CLIENT_SECRET: 'MICROSOFT_CLIENT_SECRET',
JWT_SECRET: 'JWT_SECRET',
SMTP_PASSWORD: 'SMTP_PASSWORD',
ADMIN_SECRET: 'ADMIN_SECRET',
@@ -54,6 +57,8 @@ export const ArrayInputType = {
export const SelectInputType = {
JWT_TYPE: 'JWT_TYPE',
GENDER: 'gender',
DEFAULT_AUTHORIZE_RESPONSE_TYPE: 'DEFAULT_AUTHORIZE_RESPONSE_TYPE',
DEFAULT_AUTHORIZE_RESPONSE_MODE: 'DEFAULT_AUTHORIZE_RESPONSE_MODE',
};
export const MultiSelectInputType = {
@@ -120,6 +125,9 @@ export interface envVarTypes {
APPLE_CLIENT_SECRET: string;
TWITTER_CLIENT_ID: string;
TWITTER_CLIENT_SECRET: string;
MICROSOFT_CLIENT_ID: string;
MICROSOFT_CLIENT_SECRET: string;
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID: string;
ROLES: [string] | [];
DEFAULT_ROLES: [string] | [];
PROTECTED_ROLES: [string] | [];
@@ -155,6 +163,8 @@ export interface envVarTypes {
ACCESS_TOKEN_EXPIRY_TIME: string;
DISABLE_MULTI_FACTOR_AUTHENTICATION: boolean;
ENFORCE_MULTI_FACTOR_AUTHENTICATION: boolean;
DEFAULT_AUTHORIZE_RESPONSE_TYPE: string;
DEFAULT_AUTHORIZE_RESPONSE_MODE: string;
}
export const envSubViews = {
@@ -173,6 +183,7 @@ export const envSubViews = {
export enum WebhookInputDataFields {
ID = 'id',
EVENT_DESCRIPTION = 'event_description',
EVENT_NAME = 'event_name',
ENDPOINT = 'endpoint',
ENABLED = 'enabled',
@@ -342,3 +353,16 @@ export enum EmailTemplateEditors {
UNLAYER_EDITOR = 'unlayer_editor',
PLAIN_HTML_EDITOR = 'plain_html_editor',
}
export const ResponseTypes = {
token: 'token',
code: 'code',
id_token: 'id_token',
};
export const ResponseModes = {
query: 'query',
form_post: 'form_post',
fragment: 'fragment',
web_message: 'web_message',
};

View File

@@ -32,6 +32,9 @@ export const EnvVariablesQuery = `
APPLE_CLIENT_SECRET
TWITTER_CLIENT_ID
TWITTER_CLIENT_SECRET
MICROSOFT_CLIENT_ID
MICROSOFT_CLIENT_SECRET
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID
DEFAULT_ROLES
PROTECTED_ROLES
ROLES
@@ -67,6 +70,8 @@ export const EnvVariablesQuery = `
ACCESS_TOKEN_EXPIRY_TIME
DISABLE_MULTI_FACTOR_AUTHENTICATION
ENFORCE_MULTI_FACTOR_AUTHENTICATION
DEFAULT_AUTHORIZE_RESPONSE_TYPE
DEFAULT_AUTHORIZE_RESPONSE_MODE
}
}
`;
@@ -115,6 +120,7 @@ export const WebhooksDataQuery = `
_webhooks(params: $params){
webhooks{
id
event_description
event_name
endpoint
enabled

View File

@@ -57,7 +57,7 @@ export default function Auth() {
title: capitalizeFirstLetter(error.message),
isClosable: true,
status: 'error',
position: 'bottom-right',
position: 'top-right',
});
});
}

View File

@@ -52,6 +52,9 @@ const Environment = () => {
APPLE_CLIENT_SECRET: '',
TWITTER_CLIENT_ID: '',
TWITTER_CLIENT_SECRET: '',
MICROSOFT_CLIENT_ID: '',
MICROSOFT_CLIENT_SECRET: '',
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID: '',
ROLES: [],
DEFAULT_ROLES: [],
PROTECTED_ROLES: [],
@@ -87,6 +90,8 @@ const Environment = () => {
ACCESS_TOKEN_EXPIRY_TIME: '',
DISABLE_MULTI_FACTOR_AUTHENTICATION: false,
ENFORCE_MULTI_FACTOR_AUTHENTICATION: false,
DEFAULT_AUTHORIZE_RESPONSE_TYPE: '',
DEFAULT_AUTHORIZE_RESPONSE_MODE: '',
});
const [fieldVisibility, setFieldVisibility] = React.useState<
@@ -203,7 +208,7 @@ const Environment = () => {
} variables`,
isClosable: true,
status: 'success',
position: 'bottom-right',
position: 'top-right',
});
};

View File

@@ -180,14 +180,14 @@ export default function Users() {
title: 'User verification failed',
isClosable: true,
status: 'error',
position: 'bottom-right',
position: 'top-right',
});
} else if (res.data?._update_user?.id) {
toast({
title: 'User verification successful',
isClosable: true,
status: 'success',
position: 'bottom-right',
position: 'top-right',
});
}
updateUserList();
@@ -211,14 +211,14 @@ export default function Users() {
title: 'User access enable failed',
isClosable: true,
status: 'error',
position: 'bottom-right',
position: 'top-right',
});
} else {
toast({
title: 'User access enabled successfully',
isClosable: true,
status: 'success',
position: 'bottom-right',
position: 'top-right',
});
}
updateUserList();
@@ -236,14 +236,14 @@ export default function Users() {
title: 'User access revoke failed',
isClosable: true,
status: 'error',
position: 'bottom-right',
position: 'top-right',
});
} else {
toast({
title: 'User access revoked successfully',
isClosable: true,
status: 'success',
position: 'bottom-right',
position: 'top-right',
});
}
updateUserList();
@@ -268,7 +268,7 @@ export default function Users() {
} for user`,
isClosable: true,
status: 'success',
position: 'bottom-right',
position: 'top-right',
});
updateUserList();
return;
@@ -277,7 +277,7 @@ export default function Users() {
title: 'Multi factor authentication update failed for user',
isClosable: true,
status: 'error',
position: 'bottom-right',
position: 'top-right',
});
};

View File

@@ -56,6 +56,7 @@ interface paginationPropTypes {
interface webhookDataTypes {
[WebhookInputDataFields.ID]: string;
[WebhookInputDataFields.EVENT_NAME]: string;
[WebhookInputDataFields.EVENT_DESCRIPTION]?: string;
[WebhookInputDataFields.ENDPOINT]: string;
[WebhookInputDataFields.ENABLED]: boolean;
[WebhookInputDataFields.HEADERS]?: Record<string, string>;
@@ -117,6 +118,7 @@ const Webhooks = () => {
useEffect(() => {
fetchWebookData();
}, [paginationProps.page, paginationProps.limit]);
console.log({ webhookData });
return (
<Box m="5" py="5" px="10" bg="white" rounded="md">
<Flex margin="2% 0" justifyContent="space-between" alignItems="center">
@@ -134,6 +136,7 @@ const Webhooks = () => {
<Thead>
<Tr>
<Th>Event Name</Th>
<Th>Event Description</Th>
<Th>Endpoint</Th>
<Th>Enabled</Th>
<Th>Headers</Th>
@@ -147,7 +150,10 @@ const Webhooks = () => {
style={{ fontSize: 14 }}
>
<Td maxW="300">
{webhook[WebhookInputDataFields.EVENT_NAME]}
{webhook[WebhookInputDataFields.EVENT_NAME].split('-')[0]}
</Td>
<Td maxW="300">
{webhook[WebhookInputDataFields.EVENT_DESCRIPTION]}
</Td>
<Td>{webhook[WebhookInputDataFields.ENDPOINT]}</Td>
<Td>
@@ -264,7 +270,7 @@ const Webhooks = () => {
</Text>
</Text>
<Flex alignItems="center">
<Text flexShrink="0">Go to page:</Text>{' '}
<Text>Go to page:</Text>{' '}
<NumberInput
ml={2}
mr={8}

1622
dashboard/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

39
scripts/couchbase-test.sh Normal file
View File

@@ -0,0 +1,39 @@
#!/bin/sh
set -x
set -m
sleep 15
# Setup index and memory quota
# curl -v -X POST http://127.0.0.1:8091/pools/default -d memoryQuota=300 -d indexMemoryQuota=300
# Setup services
curl -v http://127.0.0.1:8091/node/controller/setupServices -d services=kv%2Cn1ql%2Cindex
# Setup credentials
curl -v http://127.0.0.1:8091/settings/web -d port=8091 -d username=Administrator -d password=password
# Setup Memory Optimized Indexes
curl -i -u Administrator:password -X POST http://127.0.0.1:8091/settings/indexes -d 'storageMode=memory_optimized'
# Load travel-sample bucket
#curl -v -u Administrator:password -X POST http://127.0.0.1:8091/sampleBuckets/install -d '["travel-sample"]'
echo "Type: $TYPE"
if [ "$TYPE" = "WORKER" ]; then
echo "Sleeping ..."
sleep 15
#IP=`hostname -s`
IP=`hostname -I | cut -d ' ' -f1`
echo "IP: " $IP
echo "Auto Rebalance: $AUTO_REBALANCE"
if [ "$AUTO_REBALANCE" = "true" ]; then
couchbase-cli rebalance --cluster=$COUCHBASE_MASTER:8091 --user=Administrator --password=password --server-add=$IP --server-add-username=Administrator --server-add-password=password
else
couchbase-cli server-add --cluster=$COUCHBASE_MASTER:8091 --user=Administrator --password=password --server-add=$IP --server-add-username=Administrator --server-add-password=password
fi;
fi;

View File

@@ -3,6 +3,8 @@ package constants
const (
// AuthRecipeMethodBasicAuth is the basic_auth auth method
AuthRecipeMethodBasicAuth = "basic_auth"
// AuthRecipeMethodMobileBasicAuth is the mobile basic_auth method, where user can signup using mobile number and password
AuthRecipeMethodMobileBasicAuth = "mobile_basic_auth"
// AuthRecipeMethodMagicLinkLogin is the magic_link_login auth method
AuthRecipeMethodMagicLinkLogin = "magic_link_login"
// AuthRecipeMethodGoogle is the google auth method
@@ -17,4 +19,6 @@ const (
AuthRecipeMethodApple = "apple"
// AuthRecipeMethodTwitter is the twitter auth method
AuthRecipeMethodTwitter = "twitter"
// AuthRecipeMethodMicrosoft is the microsoft auth method
AuthRecipeMethodMicrosoft = "microsoft"
)

View File

@@ -27,4 +27,6 @@ const (
DbTypePlanetScaleDB = "planetscale"
// DbTypeDynamoDB is the Dynamo database type
DbTypeDynamoDB = "dynamodb"
// DbTypeCouchbaseDB is the Couchbase database type
DbTypeCouchbaseDB = "couchbase"
)

View File

@@ -43,6 +43,13 @@ const (
EnvKeyDatabaseCertKey = "DATABASE_CERT_KEY"
// EnvKeyDatabaseCACert key for env variable DATABASE_CA_CERT
EnvKeyDatabaseCACert = "DATABASE_CA_CERT"
// EnvCouchbaseBucket key for env variable COUCHBASE_BUCKET
EnvCouchbaseBucket = "COUCHBASE_BUCKET"
// EnvCouchbaseBucketRAMQuotaMB key for env variable COUCHBASE_BUCKET_RAM_QUOTA
// This value should be parsed as number
EnvCouchbaseBucketRAMQuotaMB = "COUCHBASE_BUCKET_RAM_QUOTA"
// EnvCouchbaseBucket key for env variable COUCHBASE_SCOPE
EnvCouchbaseScope = "COUCHBASE_SCOPE"
// EnvKeySmtpHost key for env variable SMTP_HOST
EnvKeySmtpHost = "SMTP_HOST"
// EnvKeySmtpPort key for env variable SMTP_PORT
@@ -101,6 +108,12 @@ const (
EnvKeyTwitterClientID = "TWITTER_CLIENT_ID"
// EnvKeyTwitterClientSecret key for env variable TWITTER_CLIENT_SECRET
EnvKeyTwitterClientSecret = "TWITTER_CLIENT_SECRET"
// EnvKeyMicrosoftClientID key for env variable MICROSOFT_CLIENT_ID
EnvKeyMicrosoftClientID = "MICROSOFT_CLIENT_ID"
// EnvKeyMicrosoftActiveDirectoryTenantID key for env variable MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID
EnvKeyMicrosoftActiveDirectoryTenantID = "MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID"
// EnvKeyMicrosoftClientSecret key for env variable MICROSOFT_CLIENT_SECRET
EnvKeyMicrosoftClientSecret = "MICROSOFT_CLIENT_SECRET"
// EnvKeyOrganizationName key for env variable ORGANIZATION_NAME
EnvKeyOrganizationName = "ORGANIZATION_NAME"
// EnvKeyOrganizationLogo key for env variable ORGANIZATION_LOGO
@@ -125,6 +138,8 @@ const (
EnvKeyDisableEmailVerification = "DISABLE_EMAIL_VERIFICATION"
// EnvKeyDisableBasicAuthentication key for env variable DISABLE_BASIC_AUTH
EnvKeyDisableBasicAuthentication = "DISABLE_BASIC_AUTHENTICATION"
// EnvKeyDisableBasicAuthentication key for env variable DISABLE_MOBILE_BASIC_AUTH
EnvKeyDisableMobileBasicAuthentication = "DISABLE_MOBILE_BASIC_AUTHENTICATION"
// EnvKeyDisableMagicLinkLogin key for env variable DISABLE_MAGIC_LINK_LOGIN
EnvKeyDisableMagicLinkLogin = "DISABLE_MAGIC_LINK_LOGIN"
// EnvKeyDisableLoginPage key for env variable DISABLE_LOGIN_PAGE
@@ -151,4 +166,12 @@ const (
EnvKeyDefaultRoles = "DEFAULT_ROLES"
// EnvKeyAllowedOrigins key for env variable ALLOWED_ORIGINS
EnvKeyAllowedOrigins = "ALLOWED_ORIGINS"
// For oauth/openid/authorize
// EnvKeyDefaultAuthorizeResponseType key for env variable DEFAULT_AUTHORIZE_RESPONSE_TYPE
// This env is used for setting default response type in authorize handler
EnvKeyDefaultAuthorizeResponseType = "DEFAULT_AUTHORIZE_RESPONSE_TYPE"
// EnvKeyDefaultAuthorizeResponseMode key for env variable DEFAULT_AUTHORIZE_RESPONSE_MODE
// This env is used for setting default response mode in authorize handler
EnvKeyDefaultAuthorizeResponseMode = "DEFAULT_AUTHORIZE_RESPONSE_MODE"
)

View File

@@ -16,4 +16,8 @@ const (
LinkedInEmailURL = "https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))"
TwitterUserInfoURL = "https://api.twitter.com/2/users/me?user.fields=id,name,profile_image_url,username"
// Get microsoft user info.
// Ref: https://learn.microsoft.com/en-us/azure/active-directory/develop/userinfo
MicrosoftUserInfoURL = "https://graph.microsoft.com/oidc/userinfo"
)

View File

@@ -7,6 +7,7 @@ import (
"github.com/authorizerdev/authorizer/server/db/providers"
"github.com/authorizerdev/authorizer/server/db/providers/arangodb"
"github.com/authorizerdev/authorizer/server/db/providers/cassandradb"
"github.com/authorizerdev/authorizer/server/db/providers/couchbase"
"github.com/authorizerdev/authorizer/server/db/providers/dynamodb"
"github.com/authorizerdev/authorizer/server/db/providers/mongodb"
"github.com/authorizerdev/authorizer/server/db/providers/sql"
@@ -21,11 +22,12 @@ func InitDB() error {
envs := memorystore.RequiredEnvStoreObj.GetRequiredEnv()
isSQL := envs.DatabaseType != constants.DbTypeArangodb && envs.DatabaseType != constants.DbTypeMongodb && envs.DatabaseType != constants.DbTypeCassandraDB && envs.DatabaseType != constants.DbTypeScyllaDB && envs.DatabaseType != constants.DbTypeDynamoDB
isSQL := envs.DatabaseType != constants.DbTypeArangodb && envs.DatabaseType != constants.DbTypeMongodb && envs.DatabaseType != constants.DbTypeCassandraDB && envs.DatabaseType != constants.DbTypeScyllaDB && envs.DatabaseType != constants.DbTypeDynamoDB && envs.DatabaseType != constants.DbTypeCouchbaseDB
isArangoDB := envs.DatabaseType == constants.DbTypeArangodb
isMongoDB := envs.DatabaseType == constants.DbTypeMongodb
isCassandra := envs.DatabaseType == constants.DbTypeCassandraDB || envs.DatabaseType == constants.DbTypeScyllaDB
isDynamoDB := envs.DatabaseType == constants.DbTypeDynamoDB
isCouchbaseDB := envs.DatabaseType == constants.DbTypeCouchbaseDB
if isSQL {
log.Info("Initializing SQL Driver for: ", envs.DatabaseType)
@@ -72,5 +74,14 @@ func InitDB() error {
}
}
if isCouchbaseDB {
log.Info("Initializing CouchbaseDB Driver for: ", envs.DatabaseType)
Provider, err = couchbase.NewProvider()
if err != nil {
log.Fatal("Failed to initialize Couchbase driver: ", err)
return err
}
}
return nil
}

View File

@@ -4,10 +4,11 @@ package models
// Env model for db
type Env struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
EnvData string `json:"env" bson:"env" cql:"env" dynamo:"env"`
Hash string `json:"hash" bson:"hash" cql:"hash" dynamo:"hash"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
EnvData string `json:"env" bson:"env" cql:"env" dynamo:"env"`
Hash string `json:"hash" bson:"hash" cql:"hash" dynamo:"hash"`
EncryptionKey string `json:"encryption_key" bson:"encryption_key" cql:"encryption_key" dynamo:"encryption_key"` // couchbase has "hash" as reserved keyword so we cannot use it. This will be empty for other dbs.
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
}

View File

@@ -10,35 +10,42 @@ import (
// Note: any change here should be reflected in providers/casandra/provider.go as it does not have model support in collection creation
// Event name has been kept unique as per initial design. But later on decided that we can have
// multiple hooks for same event so will be in a pattern `event_name-TIMESTAMP`
// Webhook model for db
type Webhook struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
EventName string `gorm:"unique" json:"event_name" bson:"event_name" cql:"event_name" dynamo:"event_name" index:"event_name,hash"`
EndPoint string `json:"endpoint" bson:"endpoint" cql:"endpoint" dynamo:"endpoint"`
Headers string `json:"headers" bson:"headers" cql:"headers" dynamo:"headers"`
Enabled bool `json:"enabled" bson:"enabled" cql:"enabled" dynamo:"enabled"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
EventName string `gorm:"unique" json:"event_name" bson:"event_name" cql:"event_name" dynamo:"event_name" index:"event_name,hash"`
EventDescription string `json:"event_description" bson:"event_description" cql:"event_description" dynamo:"event_description"`
EndPoint string `json:"endpoint" bson:"endpoint" cql:"endpoint" dynamo:"endpoint"`
Headers string `json:"headers" bson:"headers" cql:"headers" dynamo:"headers"`
Enabled bool `json:"enabled" bson:"enabled" cql:"enabled" dynamo:"enabled"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
}
// AsAPIWebhook to return webhook as graphql response object
func (w *Webhook) AsAPIWebhook() *model.Webhook {
headersMap := make(map[string]interface{})
json.Unmarshal([]byte(w.Headers), &headersMap)
id := w.ID
if strings.Contains(id, Collections.Webhook+"/") {
id = strings.TrimPrefix(id, Collections.Webhook+"/")
}
// set default title to event name without dot(.)
if w.EventDescription == "" {
w.EventDescription = strings.Join(strings.Split(w.EventName, "."), " ")
}
return &model.Webhook{
ID: id,
EventName: refs.NewStringRef(w.EventName),
Endpoint: refs.NewStringRef(w.EndPoint),
Headers: headersMap,
Enabled: refs.NewBoolRef(w.Enabled),
CreatedAt: refs.NewInt64Ref(w.CreatedAt),
UpdatedAt: refs.NewInt64Ref(w.UpdatedAt),
ID: id,
EventName: refs.NewStringRef(w.EventName),
EventDescription: refs.NewStringRef(w.EventDescription),
Endpoint: refs.NewStringRef(w.EndPoint),
Headers: headersMap,
Enabled: refs.NewBoolRef(w.Enabled),
CreatedAt: refs.NewInt64Ref(w.CreatedAt),
UpdatedAt: refs.NewInt64Ref(w.UpdatedAt),
}
}

View File

@@ -5,7 +5,6 @@ import (
"fmt"
"time"
"github.com/arangodb/go-driver"
arangoDriver "github.com/arangodb/go-driver"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/graph/model"
@@ -52,7 +51,7 @@ func (p *provider) ListEmailTemplate(ctx context.Context, pagination model.Pagin
query := fmt.Sprintf("FOR d in %s SORT d.created_at DESC LIMIT %d, %d RETURN d", models.Collections.EmailTemplate, pagination.Offset, pagination.Limit)
sctx := driver.WithQueryFullCount(ctx)
sctx := arangoDriver.WithQueryFullCount(ctx)
cursor, err := p.db.Query(sctx, query, nil)
if err != nil {
return nil, err

View File

@@ -2,8 +2,11 @@ package arangodb
import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"fmt"
"github.com/arangodb/go-driver"
arangoDriver "github.com/arangodb/go-driver"
"github.com/arangodb/go-driver/http"
"github.com/authorizerdev/authorizer/server/db/models"
@@ -22,44 +25,75 @@ type provider struct {
func NewProvider() (*provider, error) {
ctx := context.Background()
dbURL := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabaseURL
conn, err := http.NewConnection(http.ConnectionConfig{
dbUsername := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabaseUsername
dbPassword := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabasePassword
dbCACertificate := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabaseCACert
httpConfig := http.ConnectionConfig{
Endpoints: []string{dbURL},
})
}
// If ca certificate if present, create tls config
if dbCACertificate != "" {
caCert, err := base64.StdEncoding.DecodeString(dbCACertificate)
if err != nil {
return nil, err
}
// Prepare TLS Config
tlsConfig := &tls.Config{}
certPool := x509.NewCertPool()
if success := certPool.AppendCertsFromPEM(caCert); !success {
return nil, fmt.Errorf("invalid certificate")
}
tlsConfig.RootCAs = certPool
httpConfig.TLSConfig = tlsConfig
}
// Create new http connection
conn, err := http.NewConnection(httpConfig)
if err != nil {
return nil, err
}
arangoClient, err := arangoDriver.NewClient(arangoDriver.ClientConfig{
clientConfig := arangoDriver.ClientConfig{
Connection: conn,
})
}
if dbUsername != "" && dbPassword != "" {
clientConfig.Authentication = arangoDriver.BasicAuthentication(dbUsername, dbPassword)
}
arangoClient, err := arangoDriver.NewClient(clientConfig)
if err != nil {
return nil, err
}
var arangodb driver.Database
var arangodb arangoDriver.Database
dbName := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabaseName
arangodb_exists, err := arangoClient.DatabaseExists(nil, dbName)
arangodb_exists, err := arangoClient.DatabaseExists(ctx, dbName)
if err != nil {
return nil, err
}
if arangodb_exists {
arangodb, err = arangoClient.Database(nil, dbName)
arangodb, err = arangoClient.Database(ctx, dbName)
if err != nil {
return nil, err
}
} else {
arangodb, err = arangoClient.CreateDatabase(nil, dbName, nil)
arangodb, err = arangoClient.CreateDatabase(ctx, dbName, nil)
if err != nil {
return nil, err
}
}
userCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.User)
if err != nil {
return nil, err
}
if !userCollectionExists {
_, err = arangodb.CreateCollection(ctx, models.Collections.User, nil)
if err != nil {
return nil, err
}
}
userCollection, _ := arangodb.Collection(nil, models.Collections.User)
userCollection, err := arangodb.Collection(ctx, models.Collections.User)
if err != nil {
return nil, err
}
userCollection.EnsureHashIndex(ctx, []string{"email"}, &arangoDriver.EnsureHashIndexOptions{
Unique: true,
Sparse: true,
@@ -70,6 +104,9 @@ func NewProvider() (*provider, error) {
})
verificationRequestCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.VerificationRequest)
if err != nil {
return nil, err
}
if !verificationRequestCollectionExists {
_, err = arangodb.CreateCollection(ctx, models.Collections.VerificationRequest, nil)
if err != nil {
@@ -77,7 +114,10 @@ func NewProvider() (*provider, error) {
}
}
verificationRequestCollection, _ := arangodb.Collection(nil, models.Collections.VerificationRequest)
verificationRequestCollection, err := arangodb.Collection(ctx, models.Collections.VerificationRequest)
if err != nil {
return nil, err
}
verificationRequestCollection.EnsureHashIndex(ctx, []string{"email", "identifier"}, &arangoDriver.EnsureHashIndexOptions{
Unique: true,
Sparse: true,
@@ -87,6 +127,9 @@ func NewProvider() (*provider, error) {
})
sessionCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.Session)
if err != nil {
return nil, err
}
if !sessionCollectionExists {
_, err = arangodb.CreateCollection(ctx, models.Collections.Session, nil)
if err != nil {
@@ -94,13 +137,19 @@ func NewProvider() (*provider, error) {
}
}
sessionCollection, _ := arangodb.Collection(nil, models.Collections.Session)
sessionCollection, err := arangodb.Collection(ctx, models.Collections.Session)
if err != nil {
return nil, err
}
sessionCollection.EnsureHashIndex(ctx, []string{"user_id"}, &arangoDriver.EnsureHashIndexOptions{
Sparse: true,
})
configCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.Env)
if !configCollectionExists {
envCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.Env)
if err != nil {
return nil, err
}
if !envCollectionExists {
_, err = arangodb.CreateCollection(ctx, models.Collections.Env, nil)
if err != nil {
return nil, err
@@ -108,6 +157,9 @@ func NewProvider() (*provider, error) {
}
webhookCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.Webhook)
if err != nil {
return nil, err
}
if !webhookCollectionExists {
_, err = arangodb.CreateCollection(ctx, models.Collections.Webhook, nil)
if err != nil {
@@ -115,13 +167,19 @@ func NewProvider() (*provider, error) {
}
}
webhookCollection, _ := arangodb.Collection(nil, models.Collections.Webhook)
webhookCollection, err := arangodb.Collection(ctx, models.Collections.Webhook)
if err != nil {
return nil, err
}
webhookCollection.EnsureHashIndex(ctx, []string{"event_name"}, &arangoDriver.EnsureHashIndexOptions{
Unique: true,
Sparse: true,
})
webhookLogCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.WebhookLog)
if err != nil {
return nil, err
}
if !webhookLogCollectionExists {
_, err = arangodb.CreateCollection(ctx, models.Collections.WebhookLog, nil)
if err != nil {
@@ -129,12 +187,18 @@ func NewProvider() (*provider, error) {
}
}
webhookLogCollection, _ := arangodb.Collection(nil, models.Collections.WebhookLog)
webhookLogCollection, err := arangodb.Collection(ctx, models.Collections.WebhookLog)
if err != nil {
return nil, err
}
webhookLogCollection.EnsureHashIndex(ctx, []string{"webhook_id"}, &arangoDriver.EnsureHashIndexOptions{
Sparse: true,
})
emailTemplateCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.EmailTemplate)
if err != nil {
return nil, err
}
if !emailTemplateCollectionExists {
_, err = arangodb.CreateCollection(ctx, models.Collections.EmailTemplate, nil)
if err != nil {
@@ -142,13 +206,19 @@ func NewProvider() (*provider, error) {
}
}
emailTemplateCollection, _ := arangodb.Collection(nil, models.Collections.EmailTemplate)
emailTemplateCollection, err := arangodb.Collection(ctx, models.Collections.EmailTemplate)
if err != nil {
return nil, err
}
emailTemplateCollection.EnsureHashIndex(ctx, []string{"event_name"}, &arangoDriver.EnsureHashIndexOptions{
Unique: true,
Sparse: true,
})
otpCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.OTP)
if err != nil {
return nil, err
}
if !otpCollectionExists {
_, err = arangodb.CreateCollection(ctx, models.Collections.OTP, nil)
if err != nil {
@@ -156,7 +226,10 @@ func NewProvider() (*provider, error) {
}
}
otpCollection, _ := arangodb.Collection(nil, models.Collections.OTP)
otpCollection, err := arangodb.Collection(ctx, models.Collections.OTP)
if err != nil {
return nil, err
}
otpCollection.EnsureHashIndex(ctx, []string{"email"}, &arangoDriver.EnsureHashIndexOptions{
Unique: true,
Sparse: true,

View File

@@ -7,7 +7,6 @@ import (
"strings"
"time"
"github.com/arangodb/go-driver"
arangoDriver "github.com/arangodb/go-driver"
"github.com/google/uuid"
@@ -15,6 +14,7 @@ import (
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/memorystore"
"github.com/authorizerdev/authorizer/server/refs"
)
// AddUser to save user information in database
@@ -32,6 +32,12 @@ func (p *provider) AddUser(ctx context.Context, user models.User) (models.User,
user.Roles = defaultRoles
}
if user.PhoneNumber != nil && strings.TrimSpace(refs.StringValue(user.PhoneNumber)) != "" {
if u, _ := p.GetUserByPhoneNumber(ctx, refs.StringValue(user.PhoneNumber)); u != nil && u.ID != user.ID {
return user, fmt.Errorf("user with given phone number already exists")
}
}
user.CreatedAt = time.Now().Unix()
user.UpdatedAt = time.Now().Unix()
userCollection, _ := p.db.Collection(ctx, models.Collections.User)
@@ -48,6 +54,7 @@ func (p *provider) AddUser(ctx context.Context, user models.User) (models.User,
// UpdateUser to update user information in database
func (p *provider) UpdateUser(ctx context.Context, user models.User) (models.User, error) {
user.UpdatedAt = time.Now().Unix()
collection, _ := p.db.Collection(ctx, models.Collections.User)
meta, err := collection.UpdateDocument(ctx, user.Key, user)
if err != nil {
@@ -83,7 +90,7 @@ func (p *provider) DeleteUser(ctx context.Context, user models.User) error {
// ListUsers to get list of users from database
func (p *provider) ListUsers(ctx context.Context, pagination model.Pagination) (*model.Users, error) {
var users []*model.User
sctx := driver.WithQueryFullCount(ctx)
sctx := arangoDriver.WithQueryFullCount(ctx)
query := fmt.Sprintf("FOR d in %s SORT d.created_at DESC LIMIT %d, %d RETURN d", models.Collections.User, pagination.Offset, pagination.Limit)
@@ -191,7 +198,7 @@ func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{},
}
query := ""
if ids != nil && len(ids) > 0 {
if len(ids) > 0 {
keysArray := ""
for _, id := range ids {
keysArray += fmt.Sprintf("'%s', ", id)
@@ -204,10 +211,40 @@ func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{},
}
_, err = p.db.Query(ctx, query, nil)
if err != nil {
return err
}
return nil
}
// GetUserByPhoneNumber to get user information from database using phone number
func (p *provider) GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*models.User, error) {
var user models.User
query := fmt.Sprintf("FOR d in %s FILTER d.phone_number == @phone_number RETURN d", models.Collections.User)
bindVars := map[string]interface{}{
"phone_number": phoneNumber,
}
cursor, err := p.db.Query(ctx, query, bindVars)
if err != nil {
return nil, err
}
defer cursor.Close()
for {
if !cursor.HasMore() {
if user.Key == "" {
return nil, fmt.Errorf("user not found")
}
break
}
_, err := cursor.ReadDocument(ctx, &user)
if err != nil {
return nil, err
}
}
return &user, nil
}

View File

@@ -5,7 +5,7 @@ import (
"fmt"
"time"
"github.com/arangodb/go-driver"
arangoDriver "github.com/arangodb/go-driver"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/google/uuid"
@@ -96,7 +96,7 @@ func (p *provider) GetVerificationRequestByEmail(ctx context.Context, email stri
// ListVerificationRequests to get list of verification requests from database
func (p *provider) ListVerificationRequests(ctx context.Context, pagination model.Pagination) (*model.VerificationRequests, error) {
var verificationRequests []*model.VerificationRequest
sctx := driver.WithQueryFullCount(ctx)
sctx := arangoDriver.WithQueryFullCount(ctx)
query := fmt.Sprintf("FOR d in %s SORT d.created_at DESC LIMIT %d, %d RETURN d", models.Collections.VerificationRequest, pagination.Offset, pagination.Limit)
cursor, err := p.db.Query(sctx, query, nil)
@@ -112,7 +112,7 @@ func (p *provider) ListVerificationRequests(ctx context.Context, pagination mode
var verificationRequest models.VerificationRequest
meta, err := cursor.ReadDocument(ctx, &verificationRequest)
if driver.IsNoMoreDocuments(err) {
if arangoDriver.IsNoMoreDocuments(err) {
break
} else if err != nil {
return nil, err
@@ -132,8 +132,8 @@ func (p *provider) ListVerificationRequests(ctx context.Context, pagination mode
// DeleteVerificationRequest to delete verification request from database
func (p *provider) DeleteVerificationRequest(ctx context.Context, verificationRequest models.VerificationRequest) error {
collection, _ := p.db.Collection(nil, models.Collections.VerificationRequest)
_, err := collection.RemoveDocument(nil, verificationRequest.Key)
collection, _ := p.db.Collection(ctx, models.Collections.VerificationRequest)
_, err := collection.RemoveDocument(ctx, verificationRequest.Key)
if err != nil {
return err
}

View File

@@ -3,6 +3,7 @@ package arangodb
import (
"context"
"fmt"
"strings"
"time"
"github.com/arangodb/go-driver"
@@ -18,8 +19,9 @@ func (p *provider) AddWebhook(ctx context.Context, webhook models.Webhook) (*mod
webhook.ID = uuid.New().String()
webhook.Key = webhook.ID
}
webhook.Key = webhook.ID
// Add timestamp to make event name unique for legacy version
webhook.EventName = fmt.Sprintf("%s-%d", webhook.EventName, time.Now().Unix())
webhook.CreatedAt = time.Now().Unix()
webhook.UpdatedAt = time.Now().Unix()
webhookCollection, _ := p.db.Collection(ctx, models.Collections.Webhook)
@@ -33,12 +35,15 @@ func (p *provider) AddWebhook(ctx context.Context, webhook models.Webhook) (*mod
// UpdateWebhook to update webhook
func (p *provider) UpdateWebhook(ctx context.Context, webhook models.Webhook) (*model.Webhook, error) {
webhook.UpdatedAt = time.Now().Unix()
// Event is changed
if !strings.Contains(webhook.EventName, "-") {
webhook.EventName = fmt.Sprintf("%s-%d", webhook.EventName, time.Now().Unix())
}
webhookCollection, _ := p.db.Collection(ctx, models.Collections.Webhook)
meta, err := webhookCollection.UpdateDocument(ctx, webhook.Key, webhook)
if err != nil {
return nil, err
}
webhook.Key = meta.Key
webhook.ID = meta.ID.String()
return webhook.AsAPIWebhook(), nil
@@ -50,16 +55,14 @@ func (p *provider) ListWebhook(ctx context.Context, pagination model.Pagination)
query := fmt.Sprintf("FOR d in %s SORT d.created_at DESC LIMIT %d, %d RETURN d", models.Collections.Webhook, pagination.Offset, pagination.Limit)
sctx := driver.WithQueryFullCount(ctx)
sctx := arangoDriver.WithQueryFullCount(ctx)
cursor, err := p.db.Query(sctx, query, nil)
if err != nil {
return nil, err
}
defer cursor.Close()
paginationClone := pagination
paginationClone.Total = cursor.Statistics().FullCount()
for {
var webhook models.Webhook
meta, err := cursor.ReadDocument(ctx, &webhook)
@@ -88,13 +91,11 @@ func (p *provider) GetWebhookByID(ctx context.Context, webhookID string) (*model
bindVars := map[string]interface{}{
"webhook_id": webhookID,
}
cursor, err := p.db.Query(ctx, query, bindVars)
if err != nil {
return nil, err
}
defer cursor.Close()
for {
if !cursor.HasMore() {
if webhook.Key == "" {
@@ -111,32 +112,28 @@ func (p *provider) GetWebhookByID(ctx context.Context, webhookID string) (*model
}
// GetWebhookByEventName to get webhook by event_name
func (p *provider) GetWebhookByEventName(ctx context.Context, eventName string) (*model.Webhook, error) {
var webhook models.Webhook
query := fmt.Sprintf("FOR d in %s FILTER d.event_name == @event_name RETURN d", models.Collections.Webhook)
func (p *provider) GetWebhookByEventName(ctx context.Context, eventName string) ([]*model.Webhook, error) {
query := fmt.Sprintf("FOR d in %s FILTER d.event_name LIKE @event_name RETURN d", models.Collections.Webhook)
bindVars := map[string]interface{}{
"event_name": eventName,
"event_name": eventName + "%",
}
cursor, err := p.db.Query(ctx, query, bindVars)
if err != nil {
return nil, err
}
defer cursor.Close()
webhooks := []*model.Webhook{}
for {
if !cursor.HasMore() {
if webhook.Key == "" {
return nil, fmt.Errorf("webhook not found")
}
var webhook models.Webhook
if _, err := cursor.ReadDocument(ctx, &webhook); driver.IsNoMoreDocuments(err) {
// We're done
break
}
_, err := cursor.ReadDocument(ctx, &webhook)
if err != nil {
} else if err != nil {
return nil, err
}
webhooks = append(webhooks, webhook.AsAPIWebhook())
}
return webhook.AsAPIWebhook(), nil
return webhooks, nil
}
// DeleteWebhook to delete webhook
@@ -146,17 +143,14 @@ func (p *provider) DeleteWebhook(ctx context.Context, webhook *model.Webhook) er
if err != nil {
return err
}
query := fmt.Sprintf("FOR d IN %s FILTER d.webhook_id == @webhook_id REMOVE { _key: d._key } IN %s", models.Collections.WebhookLog, models.Collections.WebhookLog)
bindVars := map[string]interface{}{
"webhook_id": webhook.ID,
}
cursor, err := p.db.Query(ctx, query, bindVars)
if err != nil {
return err
}
defer cursor.Close()
return nil
}

View File

@@ -5,7 +5,6 @@ import (
"fmt"
"time"
"github.com/arangodb/go-driver"
arangoDriver "github.com/arangodb/go-driver"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/graph/model"
@@ -44,7 +43,7 @@ func (p *provider) ListWebhookLogs(ctx context.Context, pagination model.Paginat
}
}
sctx := driver.WithQueryFullCount(ctx)
sctx := arangoDriver.WithQueryFullCount(ctx)
cursor, err := p.db.Query(sctx, query, bindVariables)
if err != nil {
return nil, err

View File

@@ -161,6 +161,12 @@ func NewProvider() (*provider, error) {
if err != nil {
return nil, err
}
userPhoneNumberIndexQuery := fmt.Sprintf("CREATE INDEX IF NOT EXISTS authorizer_user_phone_number ON %s.%s (phone_number)", KeySpace, models.Collections.User)
err = session.Query(userPhoneNumberIndexQuery).Exec()
if err != nil {
return nil, err
}
// add is_multi_factor_auth_enabled on users table
userTableAlterQuery := fmt.Sprintf(`ALTER TABLE %s.%s ADD is_multi_factor_auth_enabled boolean`, KeySpace, models.Collections.User)
err = session.Query(userTableAlterQuery).Exec()
@@ -201,6 +207,13 @@ func NewProvider() (*provider, error) {
if err != nil {
return nil, err
}
// add event_description to webhook table
webhookAlterQuery := fmt.Sprintf(`ALTER TABLE %s.%s ADD (event_description text);`, KeySpace, models.Collections.Webhook)
err = session.Query(webhookAlterQuery).Exec()
if err != nil {
log.Debug("Failed to alter table as column exists: ", err)
// continue
}
webhookLogCollectionQuery := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s.%s (id text, http_status bigint, response text, request text, webhook_id text,updated_at bigint, created_at bigint, PRIMARY KEY (id))", KeySpace, models.Collections.WebhookLog)
err = session.Query(webhookLogCollectionQuery).Exec()

View File

@@ -12,6 +12,7 @@ import (
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/memorystore"
"github.com/authorizerdev/authorizer/server/refs"
"github.com/gocql/gocql"
"github.com/google/uuid"
)
@@ -30,6 +31,12 @@ func (p *provider) AddUser(ctx context.Context, user models.User) (models.User,
user.Roles = defaultRoles
}
if user.PhoneNumber != nil && strings.TrimSpace(refs.StringValue(user.PhoneNumber)) != "" {
if u, _ := p.GetUserByPhoneNumber(ctx, refs.StringValue(user.PhoneNumber)); u != nil && u.ID != user.ID {
return user, fmt.Errorf("user with given phone number already exists")
}
}
user.CreatedAt = time.Now().Unix()
user.UpdatedAt = time.Now().Unix()
@@ -299,3 +306,14 @@ func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{},
return nil
}
// GetUserByPhoneNumber to get user information from database using phone number
func (p *provider) GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*models.User, error) {
var user models.User
query := fmt.Sprintf("SELECT id, email, email_verified_at, password, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, created_at, updated_at FROM %s WHERE phone_number = '%s' LIMIT 1 ALLOW FILTERING", KeySpace+"."+models.Collections.User, phoneNumber)
err := p.db.Query(query).Consistency(gocql.One).Scan(&user.ID, &user.Email, &user.EmailVerifiedAt, &user.Password, &user.SignupMethods, &user.GivenName, &user.FamilyName, &user.MiddleName, &user.Nickname, &user.Birthdate, &user.PhoneNumber, &user.PhoneNumberVerifiedAt, &user.Picture, &user.Roles, &user.RevokedTimestamp, &user.IsMultiFactorAuthEnabled, &user.CreatedAt, &user.UpdatedAt)
if err != nil {
return nil, err
}
return &user, nil
}

View File

@@ -19,29 +19,26 @@ func (p *provider) AddWebhook(ctx context.Context, webhook models.Webhook) (*mod
if webhook.ID == "" {
webhook.ID = uuid.New().String()
}
webhook.Key = webhook.ID
webhook.CreatedAt = time.Now().Unix()
webhook.UpdatedAt = time.Now().Unix()
existingHook, _ := p.GetWebhookByEventName(ctx, webhook.EventName)
if existingHook != nil {
return nil, fmt.Errorf("Webhook with %s event_name already exists", webhook.EventName)
}
insertQuery := fmt.Sprintf("INSERT INTO %s (id, event_name, endpoint, headers, enabled, created_at, updated_at) VALUES ('%s', '%s', '%s', '%s', %t, %d, %d)", KeySpace+"."+models.Collections.Webhook, webhook.ID, webhook.EventName, webhook.EndPoint, webhook.Headers, webhook.Enabled, webhook.CreatedAt, webhook.UpdatedAt)
// Add timestamp to make event name unique for legacy version
webhook.EventName = fmt.Sprintf("%s-%d", webhook.EventName, time.Now().Unix())
insertQuery := fmt.Sprintf("INSERT INTO %s (id, event_description, event_name, endpoint, headers, enabled, created_at, updated_at) VALUES ('%s', '%s', '%s', '%s', '%s', %t, %d, %d)", KeySpace+"."+models.Collections.Webhook, webhook.ID, webhook.EventDescription, webhook.EventName, webhook.EndPoint, webhook.Headers, webhook.Enabled, webhook.CreatedAt, webhook.UpdatedAt)
err := p.db.Query(insertQuery).Exec()
if err != nil {
return nil, err
}
return webhook.AsAPIWebhook(), nil
}
// UpdateWebhook to update webhook
func (p *provider) UpdateWebhook(ctx context.Context, webhook models.Webhook) (*model.Webhook, error) {
webhook.UpdatedAt = time.Now().Unix()
// Event is changed
if !strings.Contains(webhook.EventName, "-") {
webhook.EventName = fmt.Sprintf("%s-%d", webhook.EventName, time.Now().Unix())
}
bytes, err := json.Marshal(webhook)
if err != nil {
return nil, err
@@ -54,22 +51,18 @@ func (p *provider) UpdateWebhook(ctx context.Context, webhook models.Webhook) (*
if err != nil {
return nil, err
}
updateFields := ""
for key, value := range webhookMap {
if key == "_id" {
continue
}
if key == "_key" {
continue
}
if value == nil {
updateFields += fmt.Sprintf("%s = null,", key)
continue
}
valueType := reflect.TypeOf(value)
if valueType.Name() == "string" {
updateFields += fmt.Sprintf("%s = '%s', ", key, value.(string))
@@ -79,7 +72,6 @@ func (p *provider) UpdateWebhook(ctx context.Context, webhook models.Webhook) (*
}
updateFields = strings.Trim(updateFields, " ")
updateFields = strings.TrimSuffix(updateFields, ",")
query := fmt.Sprintf("UPDATE %s SET %s WHERE id = '%s'", KeySpace+"."+models.Collections.Webhook, updateFields, webhook.ID)
err = p.db.Query(query).Exec()
if err != nil {
@@ -92,24 +84,21 @@ func (p *provider) UpdateWebhook(ctx context.Context, webhook models.Webhook) (*
func (p *provider) ListWebhook(ctx context.Context, pagination model.Pagination) (*model.Webhooks, error) {
webhooks := []*model.Webhook{}
paginationClone := pagination
totalCountQuery := fmt.Sprintf(`SELECT COUNT(*) FROM %s`, KeySpace+"."+models.Collections.Webhook)
err := p.db.Query(totalCountQuery).Consistency(gocql.One).Scan(&paginationClone.Total)
if err != nil {
return nil, err
}
// there is no offset in cassandra
// so we fetch till limit + offset
// and return the results from offset to limit
query := fmt.Sprintf("SELECT id, event_name, endpoint, headers, enabled, created_at, updated_at FROM %s LIMIT %d", KeySpace+"."+models.Collections.Webhook, pagination.Limit+pagination.Offset)
query := fmt.Sprintf("SELECT id, event_description, event_name, endpoint, headers, enabled, created_at, updated_at FROM %s LIMIT %d", KeySpace+"."+models.Collections.Webhook, pagination.Limit+pagination.Offset)
scanner := p.db.Query(query).Iter().Scanner()
counter := int64(0)
for scanner.Next() {
if counter >= pagination.Offset {
var webhook models.Webhook
err := scanner.Scan(&webhook.ID, &webhook.EventName, &webhook.EndPoint, &webhook.Headers, &webhook.Enabled, &webhook.CreatedAt, &webhook.UpdatedAt)
err := scanner.Scan(&webhook.ID, &webhook.EventDescription, &webhook.EventName, &webhook.EndPoint, &webhook.Headers, &webhook.Enabled, &webhook.CreatedAt, &webhook.UpdatedAt)
if err != nil {
return nil, err
}
@@ -127,8 +116,8 @@ func (p *provider) ListWebhook(ctx context.Context, pagination model.Pagination)
// GetWebhookByID to get webhook by id
func (p *provider) GetWebhookByID(ctx context.Context, webhookID string) (*model.Webhook, error) {
var webhook models.Webhook
query := fmt.Sprintf(`SELECT id, event_name, endpoint, headers, enabled, created_at, updated_at FROM %s WHERE id = '%s' LIMIT 1`, KeySpace+"."+models.Collections.Webhook, webhookID)
err := p.db.Query(query).Consistency(gocql.One).Scan(&webhook.ID, &webhook.EventName, &webhook.EndPoint, &webhook.Headers, &webhook.Enabled, &webhook.CreatedAt, &webhook.UpdatedAt)
query := fmt.Sprintf(`SELECT id, event_description, event_name, endpoint, headers, enabled, created_at, updated_at FROM %s WHERE id = '%s' LIMIT 1`, KeySpace+"."+models.Collections.Webhook, webhookID)
err := p.db.Query(query).Consistency(gocql.One).Scan(&webhook.ID, &webhook.EventDescription, &webhook.EventName, &webhook.EndPoint, &webhook.Headers, &webhook.Enabled, &webhook.CreatedAt, &webhook.UpdatedAt)
if err != nil {
return nil, err
}
@@ -136,14 +125,19 @@ func (p *provider) GetWebhookByID(ctx context.Context, webhookID string) (*model
}
// GetWebhookByEventName to get webhook by event_name
func (p *provider) GetWebhookByEventName(ctx context.Context, eventName string) (*model.Webhook, error) {
var webhook models.Webhook
query := fmt.Sprintf(`SELECT id, event_name, endpoint, headers, enabled, created_at, updated_at FROM %s WHERE event_name = '%s' LIMIT 1 ALLOW FILTERING`, KeySpace+"."+models.Collections.Webhook, eventName)
err := p.db.Query(query).Consistency(gocql.One).Scan(&webhook.ID, &webhook.EventName, &webhook.EndPoint, &webhook.Headers, &webhook.Enabled, &webhook.CreatedAt, &webhook.UpdatedAt)
if err != nil {
return nil, err
func (p *provider) GetWebhookByEventName(ctx context.Context, eventName string) ([]*model.Webhook, error) {
query := fmt.Sprintf(`SELECT id, event_description, event_name, endpoint, headers, enabled, created_at, updated_at FROM %s WHERE event_name LIKE '%s' ALLOW FILTERING`, KeySpace+"."+models.Collections.Webhook, eventName+"%")
scanner := p.db.Query(query).Iter().Scanner()
webhooks := []*model.Webhook{}
for scanner.Next() {
var webhook models.Webhook
err := scanner.Scan(&webhook.ID, &webhook.EventDescription, &webhook.EventName, &webhook.EndPoint, &webhook.Headers, &webhook.Enabled, &webhook.CreatedAt, &webhook.UpdatedAt)
if err != nil {
return nil, err
}
webhooks = append(webhooks, webhook.AsAPIWebhook())
}
return webhook.AsAPIWebhook(), nil
return webhooks, nil
}
// DeleteWebhook to delete webhook

View File

@@ -0,0 +1,166 @@
package couchbase
import (
"context"
"encoding/json"
"fmt"
"log"
"strings"
"time"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/couchbase/gocb/v2"
"github.com/google/uuid"
)
// AddEmailTemplate to add EmailTemplate
func (p *provider) AddEmailTemplate(ctx context.Context, emailTemplate models.EmailTemplate) (*model.EmailTemplate, error) {
if emailTemplate.ID == "" {
emailTemplate.ID = uuid.New().String()
}
emailTemplate.Key = emailTemplate.ID
emailTemplate.CreatedAt = time.Now().Unix()
emailTemplate.UpdatedAt = time.Now().Unix()
insertOpt := gocb.InsertOptions{
Context: ctx,
}
_, err := p.db.Collection(models.Collections.EmailTemplate).Insert(emailTemplate.ID, emailTemplate, &insertOpt)
if err != nil {
return emailTemplate.AsAPIEmailTemplate(), err
}
return emailTemplate.AsAPIEmailTemplate(), nil
}
// UpdateEmailTemplate to update EmailTemplate
func (p *provider) UpdateEmailTemplate(ctx context.Context, emailTemplate models.EmailTemplate) (*model.EmailTemplate, error) {
bytes, err := json.Marshal(emailTemplate)
if err != nil {
return nil, err
}
// use decoder instead of json.Unmarshall, because it converts int64 -> float64 after unmarshalling
decoder := json.NewDecoder(strings.NewReader(string(bytes)))
decoder.UseNumber()
emailTemplateMap := map[string]interface{}{}
err = decoder.Decode(&emailTemplateMap)
if err != nil {
return nil, err
}
updateFields, params := GetSetFields(emailTemplateMap)
params["emailId"] = emailTemplate.ID
query := fmt.Sprintf("UPDATE %s.%s SET %s WHERE _id = $emailId", p.scopeName, models.Collections.EmailTemplate, updateFields)
_, err = p.db.Query(query, &gocb.QueryOptions{
Context: ctx,
NamedParameters: params,
})
if err != nil {
return nil, err
}
return emailTemplate.AsAPIEmailTemplate(), nil
}
// ListEmailTemplates to list EmailTemplate
func (p *provider) ListEmailTemplate(ctx context.Context, pagination model.Pagination) (*model.EmailTemplates, error) {
emailTemplates := []*model.EmailTemplate{}
paginationClone := pagination
total, err := p.GetTotalDocs(ctx, models.Collections.EmailTemplate)
if err != nil {
return nil, err
}
paginationClone.Total = total
userQuery := fmt.Sprintf("SELECT _id, event_name, subject, design, template, created_at, updated_at FROM %s.%s ORDER BY _id OFFSET $1 LIMIT $2", p.scopeName, models.Collections.EmailTemplate)
queryResult, err := p.db.Query(userQuery, &gocb.QueryOptions{
Context: ctx,
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
PositionalParameters: []interface{}{paginationClone.Offset, paginationClone.Limit},
})
if err != nil {
return nil, err
}
for queryResult.Next() {
emailTemplate := models.EmailTemplate{}
err := queryResult.Row(&emailTemplate)
if err != nil {
log.Fatal(err)
}
emailTemplates = append(emailTemplates, emailTemplate.AsAPIEmailTemplate())
}
if err := queryResult.Err(); err != nil {
return nil, err
}
return &model.EmailTemplates{
Pagination: &paginationClone,
EmailTemplates: emailTemplates,
}, nil
}
// GetEmailTemplateByID to get EmailTemplate by id
func (p *provider) GetEmailTemplateByID(ctx context.Context, emailTemplateID string) (*model.EmailTemplate, error) {
emailTemplate := models.EmailTemplate{}
query := fmt.Sprintf(`SELECT _id, event_name, subject, design, template, created_at, updated_at FROM %s.%s WHERE _id = $1 LIMIT 1`, p.scopeName, models.Collections.EmailTemplate)
q, err := p.db.Query(query, &gocb.QueryOptions{
Context: ctx,
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
PositionalParameters: []interface{}{emailTemplateID},
})
if err != nil {
return nil, err
}
err = q.One(&emailTemplate)
if err != nil {
return nil, err
}
return emailTemplate.AsAPIEmailTemplate(), nil
}
// GetEmailTemplateByEventName to get EmailTemplate by event_name
func (p *provider) GetEmailTemplateByEventName(ctx context.Context, eventName string) (*model.EmailTemplate, error) {
emailTemplate := models.EmailTemplate{}
query := fmt.Sprintf("SELECT _id, event_name, subject, design, template, created_at, updated_at FROM %s.%s WHERE event_name=$1 LIMIT 1", p.scopeName, models.Collections.EmailTemplate)
q, err := p.db.Query(query, &gocb.QueryOptions{
Context: ctx,
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
PositionalParameters: []interface{}{eventName},
})
if err != nil {
return nil, err
}
err = q.One(&emailTemplate)
if err != nil {
return nil, err
}
return emailTemplate.AsAPIEmailTemplate(), nil
}
// DeleteEmailTemplate to delete EmailTemplate
func (p *provider) DeleteEmailTemplate(ctx context.Context, emailTemplate *model.EmailTemplate) error {
removeOpt := gocb.RemoveOptions{
Context: ctx,
}
_, err := p.db.Collection(models.Collections.EmailTemplate).Remove(emailTemplate.ID, &removeOpt)
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,70 @@
package couchbase
import (
"context"
"fmt"
"time"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/couchbase/gocb/v2"
"github.com/google/uuid"
)
// AddEnv to save environment information in database
func (p *provider) AddEnv(ctx context.Context, env models.Env) (models.Env, error) {
if env.ID == "" {
env.ID = uuid.New().String()
}
env.CreatedAt = time.Now().Unix()
env.UpdatedAt = time.Now().Unix()
env.Key = env.ID
env.EncryptionKey = env.Hash
insertOpt := gocb.InsertOptions{
Context: ctx,
}
_, err := p.db.Collection(models.Collections.Env).Insert(env.ID, env, &insertOpt)
if err != nil {
return env, err
}
return env, nil
}
// UpdateEnv to update environment information in database
func (p *provider) UpdateEnv(ctx context.Context, env models.Env) (models.Env, error) {
env.UpdatedAt = time.Now().Unix()
env.EncryptionKey = env.Hash
updateEnvQuery := fmt.Sprintf("UPDATE %s.%s SET env = $1, updated_at = $2 WHERE _id = $3", p.scopeName, models.Collections.Env)
_, err := p.db.Query(updateEnvQuery, &gocb.QueryOptions{
Context: ctx,
PositionalParameters: []interface{}{env.EnvData, env.UpdatedAt, env.UpdatedAt, env.ID},
})
if err != nil {
return env, err
}
return env, nil
}
// GetEnv to get environment information from database
func (p *provider) GetEnv(ctx context.Context) (models.Env, error) {
var env models.Env
query := fmt.Sprintf("SELECT _id, env, encryption_key, created_at, updated_at FROM %s.%s LIMIT 1", p.scopeName, models.Collections.Env)
q, err := p.db.Query(query, &gocb.QueryOptions{
Context: ctx,
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
})
if err != nil {
return env, err
}
err = q.One(&env)
if err != nil {
return env, err
}
env.Hash = env.EncryptionKey
return env, nil
}

View File

@@ -0,0 +1,84 @@
package couchbase
import (
"context"
"fmt"
"time"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/couchbase/gocb/v2"
"github.com/google/uuid"
)
// UpsertOTP to add or update otp
func (p *provider) UpsertOTP(ctx context.Context, otpParam *models.OTP) (*models.OTP, error) {
otp, _ := p.GetOTPByEmail(ctx, otpParam.Email)
shouldCreate := false
if otp == nil {
shouldCreate = true
otp = &models.OTP{
ID: uuid.NewString(),
Otp: otpParam.Otp,
Email: otpParam.Email,
ExpiresAt: otpParam.ExpiresAt,
CreatedAt: time.Now().Unix(),
UpdatedAt: time.Now().Unix(),
}
} else {
otp.Otp = otpParam.Otp
otp.ExpiresAt = otpParam.ExpiresAt
}
otp.UpdatedAt = time.Now().Unix()
if shouldCreate {
insertOpt := gocb.InsertOptions{
Context: ctx,
}
_, err := p.db.Collection(models.Collections.OTP).Insert(otp.ID, otp, &insertOpt)
if err != nil {
return otp, err
}
} else {
query := fmt.Sprintf(`UPDATE %s.%s SET otp=$1, expires_at=$2, updated_at=$3 WHERE _id=$4`, p.scopeName, models.Collections.OTP)
_, err := p.db.Query(query, &gocb.QueryOptions{
PositionalParameters: []interface{}{otp.Otp, otp.ExpiresAt, otp.UpdatedAt, otp.ID},
})
if err != nil {
return otp, err
}
}
return otp, nil
}
// GetOTPByEmail to get otp for a given email address
func (p *provider) GetOTPByEmail(ctx context.Context, emailAddress string) (*models.OTP, error) {
otp := models.OTP{}
query := fmt.Sprintf(`SELECT _id, email, otp, expires_at, created_at, updated_at FROM %s.%s WHERE email = $1 LIMIT 1`, p.scopeName, models.Collections.OTP)
q, err := p.db.Query(query, &gocb.QueryOptions{
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
PositionalParameters: []interface{}{emailAddress},
})
if err != nil {
return nil, err
}
err = q.One(&otp)
if err != nil {
return nil, err
}
return &otp, nil
}
// DeleteOTP to delete otp
func (p *provider) DeleteOTP(ctx context.Context, otp *models.OTP) error {
removeOpt := gocb.RemoveOptions{
Context: ctx,
}
_, err := p.db.Collection(models.Collections.OTP).Remove(otp.ID, &removeOpt)
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,170 @@
package couchbase
import (
"context"
"errors"
"fmt"
"reflect"
"strconv"
"strings"
"time"
"github.com/couchbase/gocb/v2"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/memorystore"
)
const (
defaultBucketName = "authorizer"
defaultScope = "_default"
)
type provider struct {
db *gocb.Scope
scopeName string
}
// NewProvider returns a new Couchbase provider
func NewProvider() (*provider, error) {
bucketName := memorystore.RequiredEnvStoreObj.GetRequiredEnv().CouchbaseBucket
scopeName := memorystore.RequiredEnvStoreObj.GetRequiredEnv().CouchbaseScope
dbURL := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabaseURL
userName := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabaseUsername
password := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabasePassword
opts := gocb.ClusterOptions{
Username: userName,
Password: password,
}
if bucketName == "" {
bucketName = defaultBucketName
}
if scopeName == "" {
scopeName = defaultScope
}
cluster, err := gocb.Connect(dbURL, opts)
if err != nil {
return nil, err
}
// To create the bucket and scope if not exist
bucket, err := CreateBucketAndScope(cluster, bucketName, scopeName)
if err != nil {
return nil, err
}
scope := bucket.Scope(scopeName)
scopeIdentifier := fmt.Sprintf("%s.%s", bucketName, scopeName)
v := reflect.ValueOf(models.Collections)
for i := 0; i < v.NumField(); i++ {
collectionName := v.Field(i)
user := gocb.CollectionSpec{
Name: collectionName.String(),
ScopeName: scopeName,
}
collectionOpts := gocb.CreateCollectionOptions{
Context: context.TODO(),
}
err = bucket.Collections().CreateCollection(user, &collectionOpts)
if err != nil && !errors.Is(err, gocb.ErrCollectionExists) {
return nil, err
}
// TODO: find how to fix this sleep time.
// Add wait time for successful collection creation
time.Sleep(5 * time.Second)
indexQuery := fmt.Sprintf("CREATE PRIMARY INDEX ON %s.%s", scopeIdentifier, collectionName.String())
_, err = scope.Query(indexQuery, nil)
if err != nil && !strings.Contains(err.Error(), "The index #primary already exists") {
return nil, err
}
}
indices := GetIndex(scopeIdentifier)
for i := 0; i < v.NumField(); i++ {
field := v.Field(i)
for _, indexQuery := range indices[field.String()] {
scope.Query(indexQuery, nil)
}
}
return &provider{
db: scope,
scopeName: scopeIdentifier,
}, nil
}
func CreateBucketAndScope(cluster *gocb.Cluster, bucketName string, scopeName string) (*gocb.Bucket, error) {
bucketRAMQuotaMB := memorystore.RequiredEnvStoreObj.GetRequiredEnv().CouchbaseBucketRAMQuotaMB
if bucketRAMQuotaMB == "" {
bucketRAMQuotaMB = "1000"
}
bucketRAMQuota, err := strconv.ParseInt(bucketRAMQuotaMB, 10, 64)
if err != nil {
return nil, err
}
settings := gocb.BucketSettings{
Name: bucketName,
RAMQuotaMB: uint64(bucketRAMQuota),
BucketType: gocb.CouchbaseBucketType,
EvictionPolicy: gocb.EvictionPolicyTypeValueOnly,
FlushEnabled: true,
CompressionMode: gocb.CompressionModeActive,
}
shouldCreateBucket := false
// check if bucket exists
_, err = cluster.Buckets().GetBucket(bucketName, nil)
if err != nil {
// bucket not found
shouldCreateBucket = true
}
if shouldCreateBucket {
err = cluster.Buckets().CreateBucket(gocb.CreateBucketSettings{
BucketSettings: settings,
ConflictResolutionType: gocb.ConflictResolutionTypeSequenceNumber,
}, nil)
if err != nil {
return nil, err
}
}
bucket := cluster.Bucket(bucketName)
if scopeName != defaultScope {
err = bucket.Collections().CreateScope(scopeName, nil)
if err != nil && !errors.Is(err, gocb.ErrScopeExists) {
return bucket, err
}
}
return bucket, nil
}
func GetIndex(scopeName string) map[string][]string {
indices := make(map[string][]string)
// User Index
userIndex1 := fmt.Sprintf("CREATE INDEX userEmailIndex ON %s.%s(email)", scopeName, models.Collections.User)
userIndex2 := fmt.Sprintf("CREATE INDEX userPhoneIndex ON %s.%s(phone_number)", scopeName, models.Collections.User)
indices[models.Collections.User] = []string{userIndex1, userIndex2}
// VerificationRequest
verificationIndex1 := fmt.Sprintf("CREATE INDEX verificationRequestTokenIndex ON %s.%s(token)", scopeName, models.Collections.VerificationRequest)
verificationIndex2 := fmt.Sprintf("CREATE INDEX verificationRequestEmailAndIdentifierIndex ON %s.%s(email,identifier)", scopeName, models.Collections.VerificationRequest)
indices[models.Collections.VerificationRequest] = []string{verificationIndex1, verificationIndex2}
// Session index
sessionIndex1 := fmt.Sprintf("CREATE INDEX SessionUserIdIndex ON %s.%s(user_id)", scopeName, models.Collections.Session)
indices[models.Collections.Session] = []string{sessionIndex1}
// Webhook index
webhookIndex1 := fmt.Sprintf("CREATE INDEX webhookEventNameIndex ON %s.%s(event_name)", scopeName, models.Collections.Webhook)
indices[models.Collections.Webhook] = []string{webhookIndex1}
// WebhookLog index
webhookLogIndex1 := fmt.Sprintf("CREATE INDEX webhookLogIdIndex ON %s.%s(webhook_id)", scopeName, models.Collections.WebhookLog)
indices[models.Collections.Webhook] = []string{webhookLogIndex1}
// WebhookLog index
emailTempIndex1 := fmt.Sprintf("CREATE INDEX EmailTemplateEventNameIndex ON %s.%s(event_name)", scopeName, models.Collections.EmailTemplate)
indices[models.Collections.EmailTemplate] = []string{emailTempIndex1}
// OTP index
otpIndex1 := fmt.Sprintf("CREATE INDEX OTPEmailIndex ON %s.%s(email)", scopeName, models.Collections.OTP)
indices[models.Collections.OTP] = []string{otpIndex1}
return indices
}

View File

@@ -0,0 +1,34 @@
package couchbase
import (
"context"
"time"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/couchbase/gocb/v2"
"github.com/google/uuid"
)
// AddSession to save session information in database
func (p *provider) AddSession(ctx context.Context, session models.Session) error {
if session.ID == "" {
session.ID = uuid.New().String()
}
session.CreatedAt = time.Now().Unix()
session.UpdatedAt = time.Now().Unix()
insertOpt := gocb.InsertOptions{
Context: ctx,
}
_, err := p.db.Collection(models.Collections.Session).Insert(session.ID, session, &insertOpt)
if err != nil {
return err
}
return nil
}
// DeleteSession to delete session information from database
func (p *provider) DeleteSession(ctx context.Context, userId string) error {
return nil
}

View File

@@ -0,0 +1,65 @@
package couchbase
import (
"context"
"fmt"
"reflect"
"strings"
"github.com/couchbase/gocb/v2"
)
func GetSetFields(webhookMap map[string]interface{}) (string, map[string]interface{}) {
params := make(map[string]interface{}, 1)
updateFields := ""
for key, value := range webhookMap {
if key == "_id" {
continue
}
if key == "_key" {
continue
}
if value == nil {
updateFields += fmt.Sprintf("%s=$%s,", key, key)
params[key] = "null"
continue
}
valueType := reflect.TypeOf(value)
if valueType.Name() == "string" {
updateFields += fmt.Sprintf("%s = $%s, ", key, key)
params[key] = value.(string)
} else {
updateFields += fmt.Sprintf("%s = $%s, ", key, key)
params[key] = value
}
}
updateFields = strings.Trim(updateFields, " ")
updateFields = strings.TrimSuffix(updateFields, ",")
return updateFields, params
}
func (p *provider) GetTotalDocs(ctx context.Context, collection string) (int64, error) {
totalDocs := TotalDocs{}
countQuery := fmt.Sprintf("SELECT COUNT(*) as Total FROM %s.%s", p.scopeName, collection)
queryRes, err := p.db.Query(countQuery, &gocb.QueryOptions{
Context: ctx,
})
queryRes.One(&totalDocs)
if err != nil {
return totalDocs.Total, err
}
return totalDocs.Total, nil
}
type TotalDocs struct {
Total int64
}

View File

@@ -0,0 +1,199 @@
package couchbase
import (
"context"
"fmt"
"log"
"time"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/memorystore"
"github.com/couchbase/gocb/v2"
"github.com/google/uuid"
)
// AddUser to save user information in database
func (p *provider) AddUser(ctx context.Context, user models.User) (models.User, error) {
if user.ID == "" {
user.ID = uuid.New().String()
}
if user.Roles == "" {
defaultRoles, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyDefaultRoles)
if err != nil {
return user, err
}
user.Roles = defaultRoles
}
user.CreatedAt = time.Now().Unix()
user.UpdatedAt = time.Now().Unix()
insertOpt := gocb.InsertOptions{
Context: ctx,
}
_, err := p.db.Collection(models.Collections.User).Insert(user.ID, user, &insertOpt)
if err != nil {
return user, err
}
return user, nil
}
// UpdateUser to update user information in database
func (p *provider) UpdateUser(ctx context.Context, user models.User) (models.User, error) {
user.UpdatedAt = time.Now().Unix()
unsertOpt := gocb.UpsertOptions{
Context: ctx,
}
_, err := p.db.Collection(models.Collections.User).Upsert(user.ID, user, &unsertOpt)
if err != nil {
return user, err
}
return user, nil
}
// DeleteUser to delete user information from database
func (p *provider) DeleteUser(ctx context.Context, user models.User) error {
removeOpt := gocb.RemoveOptions{
Context: ctx,
}
_, err := p.db.Collection(models.Collections.User).Remove(user.ID, &removeOpt)
if err != nil {
return err
}
return nil
}
// ListUsers to get list of users from database
func (p *provider) ListUsers(ctx context.Context, pagination model.Pagination) (*model.Users, error) {
users := []*model.User{}
paginationClone := pagination
userQuery := fmt.Sprintf("SELECT _id, email, email_verified_at, `password`, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, created_at, updated_at FROM %s.%s ORDER BY id OFFSET $1 LIMIT $2", p.scopeName, models.Collections.User)
queryResult, err := p.db.Query(userQuery, &gocb.QueryOptions{
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
Context: ctx,
PositionalParameters: []interface{}{paginationClone.Offset, paginationClone.Limit},
})
if err != nil {
return nil, err
}
total, err := p.GetTotalDocs(ctx, models.Collections.User)
if err != nil {
return nil, err
}
paginationClone.Total = total
for queryResult.Next() {
var user models.User
err := queryResult.Row(&user)
if err != nil {
log.Fatal(err)
}
users = append(users, user.AsAPIUser())
}
if err := queryResult.Err(); err != nil {
return nil, err
}
return &model.Users{
Pagination: &paginationClone,
Users: users,
}, nil
}
// GetUserByEmail to get user information from database using email address
func (p *provider) GetUserByEmail(ctx context.Context, email string) (models.User, error) {
user := models.User{}
query := fmt.Sprintf("SELECT _id, email, email_verified_at, `password`, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, created_at, updated_at FROM %s.%s WHERE email = $1 LIMIT 1", p.scopeName, models.Collections.User)
q, err := p.db.Query(query, &gocb.QueryOptions{
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
Context: ctx,
PositionalParameters: []interface{}{email},
})
if err != nil {
return user, err
}
err = q.One(&user)
if err != nil {
return user, err
}
return user, nil
}
// GetUserByID to get user information from database using user ID
func (p *provider) GetUserByID(ctx context.Context, id string) (models.User, error) {
user := models.User{}
query := fmt.Sprintf("SELECT _id, email, email_verified_at, `password`, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, created_at, updated_at FROM %s.%s WHERE _id = $1 LIMIT 1", p.scopeName, models.Collections.User)
q, err := p.db.Query(query, &gocb.QueryOptions{
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
Context: ctx,
PositionalParameters: []interface{}{id},
})
if err != nil {
return user, err
}
err = q.One(&user)
if err != nil {
return user, err
}
return user, nil
}
// UpdateUsers to update multiple users, with parameters of user IDs slice
// If ids set to nil / empty all the users will be updated
func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{}, ids []string) error {
// set updated_at time for all users
data["updated_at"] = time.Now().Unix()
updateFields, params := GetSetFields(data)
if len(ids) > 0 {
for _, id := range ids {
params["id"] = id
userQuery := fmt.Sprintf("UPDATE %s.%s SET %s WHERE _id = $id", p.scopeName, models.Collections.User, updateFields)
_, err := p.db.Query(userQuery, &gocb.QueryOptions{
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
Context: ctx,
NamedParameters: params,
})
if err != nil {
return err
}
}
} else {
userQuery := fmt.Sprintf("UPDATE %s.%s SET %s WHERE _id IS NOT NULL", p.scopeName, models.Collections.User, updateFields)
_, err := p.db.Query(userQuery, &gocb.QueryOptions{
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
Context: ctx,
NamedParameters: params,
})
if err != nil {
return err
}
}
return nil
}
// GetUserByPhoneNumber to get user information from database using phone number
func (p *provider) GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*models.User, error) {
var user *models.User
query := fmt.Sprintf("SELECT _id, email, email_verified_at, `password`, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, created_at, updated_at FROM %s.%s WHERE phone_number = $1 LIMIT 1", p.scopeName, models.Collections.User)
q, err := p.db.Query(query, &gocb.QueryOptions{
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
Context: ctx,
PositionalParameters: []interface{}{phoneNumber},
})
if err != nil {
return user, err
}
err = q.One(&user)
if err != nil {
return user, err
}
return user, nil
}

View File

@@ -0,0 +1,128 @@
package couchbase
import (
"context"
"fmt"
"log"
"time"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/couchbase/gocb/v2"
"github.com/google/uuid"
)
// AddVerification to save verification request in database
func (p *provider) AddVerificationRequest(ctx context.Context, verificationRequest models.VerificationRequest) (models.VerificationRequest, error) {
if verificationRequest.ID == "" {
verificationRequest.ID = uuid.New().String()
}
verificationRequest.Key = verificationRequest.ID
verificationRequest.CreatedAt = time.Now().Unix()
verificationRequest.UpdatedAt = time.Now().Unix()
insertOpt := gocb.InsertOptions{
Context: ctx,
}
_, err := p.db.Collection(models.Collections.VerificationRequest).Insert(verificationRequest.ID, verificationRequest, &insertOpt)
if err != nil {
return verificationRequest, err
}
return verificationRequest, nil
}
// GetVerificationRequestByToken to get verification request from database using token
func (p *provider) GetVerificationRequestByToken(ctx context.Context, token string) (models.VerificationRequest, error) {
verificationRequest := models.VerificationRequest{}
params := make(map[string]interface{}, 1)
params["token"] = token
query := fmt.Sprintf("SELECT _id, token, identifier, expires_at, email, nonce, redirect_uri, created_at, updated_at FROM %s.%s WHERE token=$1 LIMIT 1", p.scopeName, models.Collections.VerificationRequest)
queryResult, err := p.db.Query(query, &gocb.QueryOptions{
Context: ctx,
PositionalParameters: []interface{}{token},
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
})
if err != nil {
return verificationRequest, err
}
err = queryResult.One(&verificationRequest)
if err != nil {
return verificationRequest, err
}
return verificationRequest, nil
}
// GetVerificationRequestByEmail to get verification request by email from database
func (p *provider) GetVerificationRequestByEmail(ctx context.Context, email string, identifier string) (models.VerificationRequest, error) {
query := fmt.Sprintf("SELECT _id, identifier, token, expires_at, email, nonce, redirect_uri, created_at, updated_at FROM %s.%s WHERE email=$1 AND identifier=$2 LIMIT 1", p.scopeName, models.Collections.VerificationRequest)
queryResult, err := p.db.Query(query, &gocb.QueryOptions{
Context: ctx,
PositionalParameters: []interface{}{email, identifier},
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
})
verificationRequest := models.VerificationRequest{}
if err != nil {
return verificationRequest, err
}
err = queryResult.One(&verificationRequest)
if err != nil {
return verificationRequest, err
}
return verificationRequest, nil
}
// ListVerificationRequests to get list of verification requests from database
func (p *provider) ListVerificationRequests(ctx context.Context, pagination model.Pagination) (*model.VerificationRequests, error) {
var verificationRequests []*model.VerificationRequest
paginationClone := pagination
total, err := p.GetTotalDocs(ctx, models.Collections.VerificationRequest)
if err != nil {
return nil, err
}
paginationClone.Total = total
query := fmt.Sprintf("SELECT _id, env, created_at, updated_at FROM %s.%s OFFSET $1 LIMIT $2", p.scopeName, models.Collections.VerificationRequest)
queryResult, err := p.db.Query(query, &gocb.QueryOptions{
Context: ctx,
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
PositionalParameters: []interface{}{paginationClone.Offset, paginationClone.Limit},
})
if err != nil {
return nil, err
}
for queryResult.Next() {
var verificationRequest models.VerificationRequest
err := queryResult.Row(&verificationRequest)
if err != nil {
log.Fatal(err)
}
verificationRequests = append(verificationRequests, verificationRequest.AsAPIVerificationRequest())
}
if err := queryResult.Err(); err != nil {
return nil, err
}
return &model.VerificationRequests{
VerificationRequests: verificationRequests,
Pagination: &paginationClone,
}, nil
}
// DeleteVerificationRequest to delete verification request from database
func (p *provider) DeleteVerificationRequest(ctx context.Context, verificationRequest models.VerificationRequest) error {
removeOpt := gocb.RemoveOptions{
Context: ctx,
}
_, err := p.db.Collection(models.Collections.VerificationRequest).Remove(verificationRequest.ID, &removeOpt)
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,181 @@
package couchbase
import (
"context"
"encoding/json"
"fmt"
"log"
"strings"
"time"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/couchbase/gocb/v2"
"github.com/google/uuid"
)
// AddWebhook to add webhook
func (p *provider) AddWebhook(ctx context.Context, webhook models.Webhook) (*model.Webhook, error) {
if webhook.ID == "" {
webhook.ID = uuid.New().String()
}
webhook.Key = webhook.ID
webhook.CreatedAt = time.Now().Unix()
webhook.UpdatedAt = time.Now().Unix()
// Add timestamp to make event name unique for legacy version
webhook.EventName = fmt.Sprintf("%s-%d", webhook.EventName, time.Now().Unix())
insertOpt := gocb.InsertOptions{
Context: ctx,
}
_, err := p.db.Collection(models.Collections.Webhook).Insert(webhook.ID, webhook, &insertOpt)
if err != nil {
return webhook.AsAPIWebhook(), err
}
return webhook.AsAPIWebhook(), nil
}
// UpdateWebhook to update webhook
func (p *provider) UpdateWebhook(ctx context.Context, webhook models.Webhook) (*model.Webhook, error) {
webhook.UpdatedAt = time.Now().Unix()
// Event is changed
if !strings.Contains(webhook.EventName, "-") {
webhook.EventName = fmt.Sprintf("%s-%d", webhook.EventName, time.Now().Unix())
}
bytes, err := json.Marshal(webhook)
if err != nil {
return nil, err
}
// use decoder instead of json.Unmarshall, because it converts int64 -> float64 after unmarshalling
decoder := json.NewDecoder(strings.NewReader(string(bytes)))
decoder.UseNumber()
webhookMap := map[string]interface{}{}
err = decoder.Decode(&webhookMap)
if err != nil {
return nil, err
}
updateFields, params := GetSetFields(webhookMap)
query := fmt.Sprintf(`UPDATE %s.%s SET %s WHERE _id='%s'`, p.scopeName, models.Collections.Webhook, updateFields, webhook.ID)
_, err = p.db.Query(query, &gocb.QueryOptions{
Context: ctx,
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
NamedParameters: params,
})
if err != nil {
return nil, err
}
return webhook.AsAPIWebhook(), nil
}
// ListWebhooks to list webhook
func (p *provider) ListWebhook(ctx context.Context, pagination model.Pagination) (*model.Webhooks, error) {
webhooks := []*model.Webhook{}
paginationClone := pagination
params := make(map[string]interface{}, 1)
params["offset"] = paginationClone.Offset
params["limit"] = paginationClone.Limit
total, err := p.GetTotalDocs(ctx, models.Collections.Webhook)
if err != nil {
return nil, err
}
paginationClone.Total = total
query := fmt.Sprintf("SELECT _id, event_description, event_name, endpoint, headers, enabled, created_at, updated_at FROM %s.%s OFFSET $offset LIMIT $limit", p.scopeName, models.Collections.Webhook)
queryResult, err := p.db.Query(query, &gocb.QueryOptions{
Context: ctx,
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
NamedParameters: params,
})
if err != nil {
return nil, err
}
for queryResult.Next() {
var webhook models.Webhook
err := queryResult.Row(&webhook)
if err != nil {
log.Fatal(err)
}
webhooks = append(webhooks, webhook.AsAPIWebhook())
}
if err := queryResult.Err(); err != nil {
return nil, err
}
return &model.Webhooks{
Pagination: &paginationClone,
Webhooks: webhooks,
}, nil
}
// GetWebhookByID to get webhook by id
func (p *provider) GetWebhookByID(ctx context.Context, webhookID string) (*model.Webhook, error) {
var webhook models.Webhook
params := make(map[string]interface{}, 1)
params["_id"] = webhookID
query := fmt.Sprintf(`SELECT _id, event_description, event_name, endpoint, headers, enabled, created_at, updated_at FROM %s.%s WHERE _id=$_id LIMIT 1`, p.scopeName, models.Collections.Webhook)
q, err := p.db.Query(query, &gocb.QueryOptions{
Context: ctx,
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
NamedParameters: params,
})
if err != nil {
return nil, err
}
err = q.One(&webhook)
if err != nil {
return nil, err
}
return webhook.AsAPIWebhook(), nil
}
// GetWebhookByEventName to get webhook by event_name
func (p *provider) GetWebhookByEventName(ctx context.Context, eventName string) ([]*model.Webhook, error) {
params := make(map[string]interface{}, 1)
// params["event_name"] = eventName + "%"
query := fmt.Sprintf(`SELECT _id, event_description, event_name, endpoint, headers, enabled, created_at, updated_at FROM %s.%s WHERE event_name LIKE '%s'`, p.scopeName, models.Collections.Webhook, eventName+"%")
queryResult, err := p.db.Query(query, &gocb.QueryOptions{
Context: ctx,
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
NamedParameters: params,
})
if err != nil {
return nil, err
}
webhooks := []*model.Webhook{}
for queryResult.Next() {
var webhook models.Webhook
err := queryResult.Row(&webhook)
if err != nil {
log.Fatal(err)
}
webhooks = append(webhooks, webhook.AsAPIWebhook())
}
if err := queryResult.Err(); err != nil {
return nil, err
}
return webhooks, nil
}
// DeleteWebhook to delete webhook
func (p *provider) DeleteWebhook(ctx context.Context, webhook *model.Webhook) error {
params := make(map[string]interface{}, 1)
params["webhook_id"] = webhook.ID
removeOpt := gocb.RemoveOptions{
Context: ctx,
}
_, err := p.db.Collection(models.Collections.Webhook).Remove(webhook.ID, &removeOpt)
if err != nil {
return err
}
query := fmt.Sprintf(`DELETE FROM %s.%s WHERE webhook_id=$webhook_id`, p.scopeName, models.Collections.WebhookLog)
_, err = p.db.Query(query, &gocb.QueryOptions{
Context: ctx,
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
NamedParameters: params,
})
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,85 @@
package couchbase
import (
"context"
"fmt"
"log"
"time"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/couchbase/gocb/v2"
"github.com/google/uuid"
)
// AddWebhookLog to add webhook log
func (p *provider) AddWebhookLog(ctx context.Context, webhookLog models.WebhookLog) (*model.WebhookLog, error) {
if webhookLog.ID == "" {
webhookLog.ID = uuid.New().String()
}
webhookLog.Key = webhookLog.ID
webhookLog.CreatedAt = time.Now().Unix()
webhookLog.UpdatedAt = time.Now().Unix()
insertOpt := gocb.InsertOptions{
Context: ctx,
}
_, err := p.db.Collection(models.Collections.WebhookLog).Insert(webhookLog.ID, webhookLog, &insertOpt)
if err != nil {
return webhookLog.AsAPIWebhookLog(), err
}
return webhookLog.AsAPIWebhookLog(), nil
}
// ListWebhookLogs to list webhook logs
func (p *provider) ListWebhookLogs(ctx context.Context, pagination model.Pagination, webhookID string) (*model.WebhookLogs, error) {
var query string
var err error
webhookLogs := []*model.WebhookLog{}
params := make(map[string]interface{}, 1)
paginationClone := pagination
params["webhookID"] = webhookID
params["offset"] = paginationClone.Offset
params["limit"] = paginationClone.Limit
total, err := p.GetTotalDocs(ctx, models.Collections.WebhookLog)
if err != nil {
return nil, err
}
paginationClone.Total = total
if webhookID != "" {
query = fmt.Sprintf(`SELECT _id, http_status, response, request, webhook_id, created_at, updated_at FROM %s.%s WHERE webhook_id=$webhookID`, p.scopeName, models.Collections.WebhookLog)
} else {
query = fmt.Sprintf("SELECT _id, http_status, response, request, webhook_id, created_at, updated_at FROM %s.%s OFFSET $offset LIMIT $limit", p.scopeName, models.Collections.WebhookLog)
}
queryResult, err := p.db.Query(query, &gocb.QueryOptions{
Context: ctx,
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
NamedParameters: params,
})
if err != nil {
return nil, err
}
for queryResult.Next() {
var webhookLog models.WebhookLog
err := queryResult.Row(&webhookLog)
if err != nil {
log.Fatal(err)
}
webhookLogs = append(webhookLogs, webhookLog.AsAPIWebhookLog())
}
if err := queryResult.Err(); err != nil {
return nil, err
}
return &model.WebhookLogs{
Pagination: &paginationClone,
WebhookLogs: webhookLogs,
}, nil
}

View File

@@ -34,7 +34,6 @@ func (p *provider) AddEnv(ctx context.Context, env models.Env) (models.Env, erro
// UpdateEnv to update environment information in database
func (p *provider) UpdateEnv(ctx context.Context, env models.Env) (models.Env, error) {
collection := p.db.Table(models.Collections.Env)
env.UpdatedAt = time.Now().Unix()

View File

@@ -3,12 +3,15 @@ package dynamodb
import (
"context"
"errors"
"fmt"
"strings"
"time"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/memorystore"
"github.com/authorizerdev/authorizer/server/refs"
"github.com/google/uuid"
"github.com/guregu/dynamo"
log "github.com/sirupsen/logrus"
@@ -30,6 +33,12 @@ func (p *provider) AddUser(ctx context.Context, user models.User) (models.User,
user.Roles = defaultRoles
}
if user.PhoneNumber != nil && strings.TrimSpace(refs.StringValue(user.PhoneNumber)) != "" {
if u, _ := p.GetUserByPhoneNumber(ctx, refs.StringValue(user.PhoneNumber)); u != nil {
return user, fmt.Errorf("user with given phone number already exists")
}
}
user.CreatedAt = time.Now().Unix()
user.UpdatedAt = time.Now().Unix()
@@ -193,3 +202,23 @@ func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{},
}
return nil
}
// GetUserByPhoneNumber to get user information from database using phone number
func (p *provider) GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*models.User, error) {
var users []models.User
var user models.User
collection := p.db.Table(models.Collections.User)
err := collection.Scan().Filter("'phone_number' = ?", phoneNumber).AllWithContext(ctx, &users)
if err != nil {
return nil, err
}
if len(users) > 0 {
user = users[0]
return &user, nil
} else {
return nil, errors.New("no record found")
}
}

View File

@@ -3,28 +3,29 @@ package dynamodb
import (
"context"
"errors"
"fmt"
"strings"
"time"
"github.com/google/uuid"
"github.com/guregu/dynamo"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/google/uuid"
"github.com/guregu/dynamo"
)
// AddWebhook to add webhook
func (p *provider) AddWebhook(ctx context.Context, webhook models.Webhook) (*model.Webhook, error) {
collection := p.db.Table(models.Collections.Webhook)
if webhook.ID == "" {
webhook.ID = uuid.New().String()
}
webhook.Key = webhook.ID
webhook.CreatedAt = time.Now().Unix()
webhook.UpdatedAt = time.Now().Unix()
// Add timestamp to make event name unique for legacy version
webhook.EventName = fmt.Sprintf("%s-%d", webhook.EventName, time.Now().Unix())
err := collection.Put(webhook).RunWithContext(ctx)
if err != nil {
return nil, err
}
@@ -33,11 +34,13 @@ func (p *provider) AddWebhook(ctx context.Context, webhook models.Webhook) (*mod
// UpdateWebhook to update webhook
func (p *provider) UpdateWebhook(ctx context.Context, webhook models.Webhook) (*model.Webhook, error) {
collection := p.db.Table(models.Collections.Webhook)
webhook.UpdatedAt = time.Now().Unix()
// Event is changed
if !strings.Contains(webhook.EventName, "-") {
webhook.EventName = fmt.Sprintf("%s-%d", webhook.EventName, time.Now().Unix())
}
collection := p.db.Table(models.Collections.Webhook)
err := UpdateByHashKey(collection, "id", webhook.ID, webhook)
if err != nil {
return nil, err
}
@@ -51,16 +54,13 @@ func (p *provider) ListWebhook(ctx context.Context, pagination model.Pagination)
var lastEval dynamo.PagingKey
var iter dynamo.PagingIter
var iteration int64 = 0
collection := p.db.Table(models.Collections.Webhook)
paginationClone := pagination
scanner := collection.Scan()
count, err := scanner.Count()
if err != nil {
return nil, err
}
for (paginationClone.Offset + paginationClone.Limit) > iteration {
iter = scanner.StartFrom(lastEval).Limit(paginationClone.Limit).Iter()
for iter.NextWithContext(ctx, &webhook) {
@@ -75,9 +75,7 @@ func (p *provider) ListWebhook(ctx context.Context, pagination model.Pagination)
lastEval = iter.LastEvaluatedKey()
iteration += paginationClone.Limit
}
paginationClone.Total = count
return &model.Webhooks{
Pagination: &paginationClone,
Webhooks: webhooks,
@@ -88,37 +86,29 @@ func (p *provider) ListWebhook(ctx context.Context, pagination model.Pagination)
func (p *provider) GetWebhookByID(ctx context.Context, webhookID string) (*model.Webhook, error) {
collection := p.db.Table(models.Collections.Webhook)
var webhook models.Webhook
err := collection.Get("id", webhookID).OneWithContext(ctx, &webhook)
if err != nil {
return nil, err
}
if webhook.ID == "" {
return webhook.AsAPIWebhook(), errors.New("no documets found")
}
return webhook.AsAPIWebhook(), nil
}
// GetWebhookByEventName to get webhook by event_name
func (p *provider) GetWebhookByEventName(ctx context.Context, eventName string) (*model.Webhook, error) {
var webhook models.Webhook
func (p *provider) GetWebhookByEventName(ctx context.Context, eventName string) ([]*model.Webhook, error) {
webhooks := []models.Webhook{}
collection := p.db.Table(models.Collections.Webhook)
iter := collection.Scan().Index("event_name").Filter("'event_name' = ?", eventName).Iter()
for iter.NextWithContext(ctx, &webhook) {
return webhook.AsAPIWebhook(), nil
}
err := iter.Err()
err := collection.Scan().Index("event_name").Filter("contains(event_name, ?)", eventName).AllWithContext(ctx, &webhooks)
if err != nil {
return webhook.AsAPIWebhook(), err
return nil, err
}
return webhook.AsAPIWebhook(), nil
resWebhooks := []*model.Webhook{}
for _, w := range webhooks {
resWebhooks = append(resWebhooks, w.AsAPIWebhook())
}
return resWebhooks, nil
}
// DeleteWebhook to delete webhook
@@ -133,7 +123,6 @@ func (p *provider) DeleteWebhook(ctx context.Context, webhook *model.Webhook) er
return err
}
webhookLogs, errIs := p.ListWebhookLogs(ctx, pagination, webhook.ID)
for _, webhookLog := range webhookLogs.WebhookLogs {
err = webhookLogCollection.Delete("id", webhookLog.ID).RunWithContext(ctx)
if err != nil {

View File

@@ -155,3 +155,16 @@ func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{},
}
return nil
}
// GetUserByPhoneNumber to get user information from database using phone number
func (p *provider) GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*models.User, error) {
var user models.User
userCollection := p.db.Collection(models.Collections.User, options.Collection())
err := userCollection.FindOne(ctx, bson.M{"phone_number": phoneNumber}).Decode(&user)
if err != nil {
return nil, err
}
return &user, nil
}

View File

@@ -2,6 +2,8 @@ package mongodb
import (
"context"
"fmt"
"strings"
"time"
"github.com/authorizerdev/authorizer/server/db/models"
@@ -16,11 +18,11 @@ func (p *provider) AddWebhook(ctx context.Context, webhook models.Webhook) (*mod
if webhook.ID == "" {
webhook.ID = uuid.New().String()
}
webhook.Key = webhook.ID
webhook.CreatedAt = time.Now().Unix()
webhook.UpdatedAt = time.Now().Unix()
// Add timestamp to make event name unique for legacy version
webhook.EventName = fmt.Sprintf("%s-%d", webhook.EventName, time.Now().Unix())
webhookCollection := p.db.Collection(models.Collections.Webhook, options.Collection())
_, err := webhookCollection.InsertOne(ctx, webhook)
if err != nil {
@@ -32,39 +34,37 @@ func (p *provider) AddWebhook(ctx context.Context, webhook models.Webhook) (*mod
// UpdateWebhook to update webhook
func (p *provider) UpdateWebhook(ctx context.Context, webhook models.Webhook) (*model.Webhook, error) {
webhook.UpdatedAt = time.Now().Unix()
// Event is changed
if !strings.Contains(webhook.EventName, "-") {
webhook.EventName = fmt.Sprintf("%s-%d", webhook.EventName, time.Now().Unix())
}
webhookCollection := p.db.Collection(models.Collections.Webhook, options.Collection())
_, err := webhookCollection.UpdateOne(ctx, bson.M{"_id": bson.M{"$eq": webhook.ID}}, bson.M{"$set": webhook}, options.MergeUpdateOptions())
if err != nil {
return nil, err
}
return webhook.AsAPIWebhook(), nil
}
// ListWebhooks to list webhook
func (p *provider) ListWebhook(ctx context.Context, pagination model.Pagination) (*model.Webhooks, error) {
var webhooks []*model.Webhook
webhooks := []*model.Webhook{}
opts := options.Find()
opts.SetLimit(pagination.Limit)
opts.SetSkip(pagination.Offset)
opts.SetSort(bson.M{"created_at": -1})
paginationClone := pagination
webhookCollection := p.db.Collection(models.Collections.Webhook, options.Collection())
count, err := webhookCollection.CountDocuments(ctx, bson.M{}, options.Count())
if err != nil {
return nil, err
}
paginationClone.Total = count
cursor, err := webhookCollection.Find(ctx, bson.M{}, opts)
if err != nil {
return nil, err
}
defer cursor.Close(ctx)
for cursor.Next(ctx) {
var webhook models.Webhook
err := cursor.Decode(&webhook)
@@ -73,7 +73,6 @@ func (p *provider) ListWebhook(ctx context.Context, pagination model.Pagination)
}
webhooks = append(webhooks, webhook.AsAPIWebhook())
}
return &model.Webhooks{
Pagination: &paginationClone,
Webhooks: webhooks,
@@ -92,14 +91,27 @@ func (p *provider) GetWebhookByID(ctx context.Context, webhookID string) (*model
}
// GetWebhookByEventName to get webhook by event_name
func (p *provider) GetWebhookByEventName(ctx context.Context, eventName string) (*model.Webhook, error) {
var webhook models.Webhook
func (p *provider) GetWebhookByEventName(ctx context.Context, eventName string) ([]*model.Webhook, error) {
webhooks := []*model.Webhook{}
webhookCollection := p.db.Collection(models.Collections.Webhook, options.Collection())
err := webhookCollection.FindOne(ctx, bson.M{"event_name": eventName}).Decode(&webhook)
opts := options.Find()
opts.SetSort(bson.M{"created_at": -1})
cursor, err := webhookCollection.Find(ctx, bson.M{"event_name": bson.M{
"$regex": fmt.Sprintf("^%s", eventName),
}}, opts)
if err != nil {
return nil, err
}
return webhook.AsAPIWebhook(), nil
defer cursor.Close(ctx)
for cursor.Next(ctx) {
var webhook models.Webhook
err := cursor.Decode(&webhook)
if err != nil {
return nil, err
}
webhooks = append(webhooks, webhook.AsAPIWebhook())
}
return webhooks, nil
}
// DeleteWebhook to delete webhook
@@ -109,12 +121,10 @@ func (p *provider) DeleteWebhook(ctx context.Context, webhook *model.Webhook) er
if err != nil {
return err
}
webhookLogCollection := p.db.Collection(models.Collections.WebhookLog, options.Collection())
_, err = webhookLogCollection.DeleteMany(nil, bson.M{"webhook_id": webhook.ID}, options.Delete())
if err != nil {
return err
}
return nil
}

View File

@@ -69,3 +69,10 @@ func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{},
return nil
}
// GetUserByPhoneNumber to get user information from database using phone number
func (p *provider) GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*models.User, error) {
var user *models.User
return user, nil
}

View File

@@ -2,6 +2,8 @@ package provider_template
import (
"context"
"fmt"
"strings"
"time"
"github.com/authorizerdev/authorizer/server/db/models"
@@ -14,16 +16,21 @@ func (p *provider) AddWebhook(ctx context.Context, webhook models.Webhook) (*mod
if webhook.ID == "" {
webhook.ID = uuid.New().String()
}
webhook.Key = webhook.ID
webhook.CreatedAt = time.Now().Unix()
webhook.UpdatedAt = time.Now().Unix()
// Add timestamp to make event name unique for legacy version
webhook.EventName = fmt.Sprintf("%s-%d", webhook.EventName, time.Now().Unix())
return webhook.AsAPIWebhook(), nil
}
// UpdateWebhook to update webhook
func (p *provider) UpdateWebhook(ctx context.Context, webhook models.Webhook) (*model.Webhook, error) {
webhook.UpdatedAt = time.Now().Unix()
// Event is changed
if !strings.Contains(webhook.EventName, "-") {
webhook.EventName = fmt.Sprintf("%s-%d", webhook.EventName, time.Now().Unix())
}
return webhook.AsAPIWebhook(), nil
}
@@ -38,7 +45,7 @@ func (p *provider) GetWebhookByID(ctx context.Context, webhookID string) (*model
}
// GetWebhookByEventName to get webhook by event_name
func (p *provider) GetWebhookByEventName(ctx context.Context, eventName string) (*model.Webhook, error) {
func (p *provider) GetWebhookByEventName(ctx context.Context, eventName string) ([]*model.Webhook, error) {
return nil, nil
}

View File

@@ -18,6 +18,8 @@ type Provider interface {
ListUsers(ctx context.Context, pagination model.Pagination) (*model.Users, error)
// GetUserByEmail to get user information from database using email address
GetUserByEmail(ctx context.Context, email string) (models.User, error)
// GetUserByPhoneNumber to get user information from database using phone number
GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*models.User, error)
// GetUserByID to get user information from database using user ID
GetUserByID(ctx context.Context, id string) (models.User, error)
// UpdateUsers to update multiple users, with parameters of user IDs slice
@@ -54,7 +56,7 @@ type Provider interface {
// GetWebhookByID to get webhook by id
GetWebhookByID(ctx context.Context, webhookID string) (*model.Webhook, error)
// GetWebhookByEventName to get webhook by event_name
GetWebhookByEventName(ctx context.Context, eventName string) (*model.Webhook, error)
GetWebhookByEventName(ctx context.Context, eventName string) ([]*model.Webhook, error)
// DeleteWebhook to delete webhook
DeleteWebhook(ctx context.Context, webhook *model.Webhook) error

View File

@@ -31,7 +31,7 @@ func (p *provider) AddUser(ctx context.Context, user models.User) (models.User,
}
if user.PhoneNumber != nil && strings.TrimSpace(refs.StringValue(user.PhoneNumber)) != "" {
if u, _ := p.GetUserByPhone(ctx, refs.StringValue(user.PhoneNumber)); u != nil {
if u, _ := p.GetUserByPhoneNumber(ctx, refs.StringValue(user.PhoneNumber)); u != nil {
return user, fmt.Errorf("user with given phone number already exists")
}
}
@@ -56,12 +56,6 @@ func (p *provider) AddUser(ctx context.Context, user models.User) (models.User,
func (p *provider) UpdateUser(ctx context.Context, user models.User) (models.User, error) {
user.UpdatedAt = time.Now().Unix()
if user.PhoneNumber != nil && strings.TrimSpace(refs.StringValue(user.PhoneNumber)) != "" {
if u, _ := p.GetUserByPhone(ctx, refs.StringValue(user.PhoneNumber)); u != nil && u.ID != user.ID {
return user, fmt.Errorf("user with given phone number already exists")
}
}
result := p.db.Save(&user)
if result.Error != nil {
@@ -156,12 +150,13 @@ func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{},
return nil
}
func (p *provider) GetUserByPhone(ctx context.Context, phoneNumber string) (*models.User, error) {
// GetUserByPhoneNumber to get user information from database using phone number
func (p *provider) GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*models.User, error) {
var user *models.User
result := p.db.Where("phone_number = ?", phoneNumber).First(&user)
if result.Error != nil {
return user, result.Error
return nil, result.Error
}
return user, nil

View File

@@ -2,6 +2,8 @@ package sql
import (
"context"
"fmt"
"strings"
"time"
"github.com/authorizerdev/authorizer/server/db/models"
@@ -14,10 +16,11 @@ func (p *provider) AddWebhook(ctx context.Context, webhook models.Webhook) (*mod
if webhook.ID == "" {
webhook.ID = uuid.New().String()
}
webhook.Key = webhook.ID
webhook.CreatedAt = time.Now().Unix()
webhook.UpdatedAt = time.Now().Unix()
// Add timestamp to make event name unique for legacy version
webhook.EventName = fmt.Sprintf("%s-%d", webhook.EventName, time.Now().Unix())
res := p.db.Create(&webhook)
if res.Error != nil {
return nil, res.Error
@@ -28,33 +31,31 @@ func (p *provider) AddWebhook(ctx context.Context, webhook models.Webhook) (*mod
// UpdateWebhook to update webhook
func (p *provider) UpdateWebhook(ctx context.Context, webhook models.Webhook) (*model.Webhook, error) {
webhook.UpdatedAt = time.Now().Unix()
// Event is changed
if !strings.Contains(webhook.EventName, "-") {
webhook.EventName = fmt.Sprintf("%s-%d", webhook.EventName, time.Now().Unix())
}
result := p.db.Save(&webhook)
if result.Error != nil {
return nil, result.Error
}
return webhook.AsAPIWebhook(), nil
}
// ListWebhooks to list webhook
func (p *provider) ListWebhook(ctx context.Context, pagination model.Pagination) (*model.Webhooks, error) {
var webhooks []models.Webhook
result := p.db.Limit(int(pagination.Limit)).Offset(int(pagination.Offset)).Order("created_at DESC").Find(&webhooks)
if result.Error != nil {
return nil, result.Error
}
var total int64
totalRes := p.db.Model(&models.Webhook{}).Count(&total)
if totalRes.Error != nil {
return nil, totalRes.Error
}
paginationClone := pagination
paginationClone.Total = total
responseWebhooks := []*model.Webhook{}
for _, w := range webhooks {
responseWebhooks = append(responseWebhooks, w.AsAPIWebhook())
@@ -77,14 +78,17 @@ func (p *provider) GetWebhookByID(ctx context.Context, webhookID string) (*model
}
// GetWebhookByEventName to get webhook by event_name
func (p *provider) GetWebhookByEventName(ctx context.Context, eventName string) (*model.Webhook, error) {
var webhook models.Webhook
result := p.db.Where("event_name = ?", eventName).First(&webhook)
func (p *provider) GetWebhookByEventName(ctx context.Context, eventName string) ([]*model.Webhook, error) {
var webhooks []models.Webhook
result := p.db.Where("event_name LIKE ?", eventName+"%").Find(&webhooks)
if result.Error != nil {
return nil, result.Error
}
return webhook.AsAPIWebhook(), nil
responseWebhooks := []*model.Webhook{}
for _, w := range webhooks {
responseWebhooks = append(responseWebhooks, w.AsAPIWebhook())
}
return responseWebhooks, nil
}
// DeleteWebhook to delete webhook

90
server/env/env.go vendored
View File

@@ -75,17 +75,26 @@ func InitAllEnv() error {
osAppleClientSecret := os.Getenv(constants.EnvKeyAppleClientSecret)
osTwitterClientID := os.Getenv(constants.EnvKeyTwitterClientID)
osTwitterClientSecret := os.Getenv(constants.EnvKeyTwitterClientSecret)
osMicrosoftClientID := os.Getenv(constants.EnvKeyMicrosoftClientID)
osMicrosoftClientSecret := os.Getenv(constants.EnvKeyMicrosoftClientSecret)
osMicrosoftActiveDirectoryTenantID := os.Getenv(constants.EnvKeyMicrosoftActiveDirectoryTenantID)
osResetPasswordURL := os.Getenv(constants.EnvKeyResetPasswordURL)
osOrganizationName := os.Getenv(constants.EnvKeyOrganizationName)
osOrganizationLogo := os.Getenv(constants.EnvKeyOrganizationLogo)
osAwsRegion := os.Getenv(constants.EnvAwsRegion)
osAwsAccessKey := os.Getenv(constants.EnvAwsAccessKeyID)
osAwsSecretKey := os.Getenv(constants.EnvAwsSecretAccessKey)
osCouchbaseBucket := os.Getenv(constants.EnvCouchbaseBucket)
osCouchbaseScope := os.Getenv(constants.EnvCouchbaseScope)
osCouchbaseBucketRAMQuotaMB := os.Getenv(constants.EnvCouchbaseBucketRAMQuotaMB)
osAuthorizeResponseType := os.Getenv(constants.EnvKeyDefaultAuthorizeResponseType)
osAuthorizeResponseMode := os.Getenv(constants.EnvKeyDefaultAuthorizeResponseMode)
// os bool vars
osAppCookieSecure := os.Getenv(constants.EnvKeyAppCookieSecure)
osAdminCookieSecure := os.Getenv(constants.EnvKeyAdminCookieSecure)
osDisableBasicAuthentication := os.Getenv(constants.EnvKeyDisableBasicAuthentication)
osDisableMobileBasicAuthentication := os.Getenv(constants.AuthRecipeMethodMobileBasicAuth)
osDisableEmailVerification := os.Getenv(constants.EnvKeyDisableEmailVerification)
osDisableMagicLinkLogin := os.Getenv(constants.EnvKeyDisableMagicLinkLogin)
osDisableLoginPage := os.Getenv(constants.EnvKeyDisableLoginPage)
@@ -133,17 +142,38 @@ func InitAllEnv() error {
if val, ok := envData[constants.EnvAwsAccessKeyID]; !ok || val == "" {
envData[constants.EnvAwsAccessKeyID] = osAwsAccessKey
}
if osAwsAccessKey != "" && envData[constants.EnvAwsAccessKeyID] != osAwsRegion {
if osAwsAccessKey != "" && envData[constants.EnvAwsAccessKeyID] != osAwsAccessKey {
envData[constants.EnvAwsAccessKeyID] = osAwsAccessKey
}
if val, ok := envData[constants.EnvAwsSecretAccessKey]; !ok || val == "" {
envData[constants.EnvAwsSecretAccessKey] = osAwsSecretKey
}
if osAwsSecretKey != "" && envData[constants.EnvAwsSecretAccessKey] != osAwsRegion {
if osAwsSecretKey != "" && envData[constants.EnvAwsSecretAccessKey] != osAwsSecretKey {
envData[constants.EnvAwsSecretAccessKey] = osAwsSecretKey
}
if val, ok := envData[constants.EnvCouchbaseBucket]; !ok || val == "" {
envData[constants.EnvCouchbaseBucket] = osCouchbaseBucket
}
if osCouchbaseBucket != "" && envData[constants.EnvCouchbaseBucket] != osCouchbaseBucket {
envData[constants.EnvCouchbaseBucket] = osCouchbaseBucket
}
if val, ok := envData[constants.EnvCouchbaseBucketRAMQuotaMB]; !ok || val == "" {
envData[constants.EnvCouchbaseBucketRAMQuotaMB] = osCouchbaseBucketRAMQuotaMB
}
if osCouchbaseBucketRAMQuotaMB != "" && envData[constants.EnvCouchbaseBucketRAMQuotaMB] != osCouchbaseBucketRAMQuotaMB {
envData[constants.EnvCouchbaseBucketRAMQuotaMB] = osCouchbaseBucketRAMQuotaMB
}
if val, ok := envData[constants.EnvCouchbaseScope]; !ok || val == "" {
envData[constants.EnvCouchbaseScope] = osCouchbaseScope
}
if osCouchbaseScope != "" && envData[constants.EnvCouchbaseScope] != osCouchbaseScope {
envData[constants.EnvCouchbaseScope] = osCouchbaseScope
}
if val, ok := envData[constants.EnvKeyAppURL]; !ok || val == "" {
envData[constants.EnvKeyAppURL] = osAppURL
}
@@ -430,6 +460,27 @@ func InitAllEnv() error {
envData[constants.EnvKeyTwitterClientSecret] = osTwitterClientSecret
}
if val, ok := envData[constants.EnvKeyMicrosoftClientID]; !ok || val == "" {
envData[constants.EnvKeyMicrosoftClientID] = osMicrosoftClientID
}
if osMicrosoftClientID != "" && envData[constants.EnvKeyMicrosoftClientID] != osMicrosoftClientID {
envData[constants.EnvKeyMicrosoftClientID] = osMicrosoftClientID
}
if val, ok := envData[constants.EnvKeyMicrosoftClientSecret]; !ok || val == "" {
envData[constants.EnvKeyMicrosoftClientSecret] = osMicrosoftClientSecret
}
if osMicrosoftClientSecret != "" && envData[constants.EnvKeyMicrosoftClientSecret] != osMicrosoftClientSecret {
envData[constants.EnvKeyMicrosoftClientSecret] = osMicrosoftClientSecret
}
if val, ok := envData[constants.EnvKeyMicrosoftActiveDirectoryTenantID]; !ok || val == "" {
envData[constants.EnvKeyMicrosoftActiveDirectoryTenantID] = osMicrosoftActiveDirectoryTenantID
}
if osMicrosoftActiveDirectoryTenantID != "" && envData[constants.EnvKeyMicrosoftActiveDirectoryTenantID] != osMicrosoftActiveDirectoryTenantID {
envData[constants.EnvKeyMicrosoftActiveDirectoryTenantID] = osMicrosoftActiveDirectoryTenantID
}
if val, ok := envData[constants.EnvKeyResetPasswordURL]; !ok || val == "" {
envData[constants.EnvKeyResetPasswordURL] = strings.TrimPrefix(osResetPasswordURL, "/")
}
@@ -498,6 +549,19 @@ func InitAllEnv() error {
}
}
if _, ok := envData[constants.EnvKeyDisableMobileBasicAuthentication]; !ok {
envData[constants.EnvKeyDisableMobileBasicAuthentication] = osDisableBasicAuthentication == "true"
}
if osDisableMobileBasicAuthentication != "" {
boolValue, err := strconv.ParseBool(osDisableMobileBasicAuthentication)
if err != nil {
return err
}
if boolValue != envData[constants.EnvKeyDisableMobileBasicAuthentication].(bool) {
envData[constants.EnvKeyDisableMobileBasicAuthentication] = boolValue
}
}
if _, ok := envData[constants.EnvKeyDisableEmailVerification]; !ok {
envData[constants.EnvKeyDisableEmailVerification] = osDisableEmailVerification == "true"
}
@@ -673,6 +737,28 @@ func InitAllEnv() error {
envData[constants.EnvKeyProtectedRoles] = osProtectedRoles
}
if val, ok := envData[constants.EnvKeyDefaultAuthorizeResponseType]; !ok || val == "" {
envData[constants.EnvKeyDefaultAuthorizeResponseType] = osAuthorizeResponseType
// Set the default value to token type
if envData[constants.EnvKeyDefaultAuthorizeResponseType] == "" {
envData[constants.EnvKeyDefaultAuthorizeResponseType] = constants.ResponseTypeToken
}
}
if osAuthorizeResponseType != "" && envData[constants.EnvKeyDefaultAuthorizeResponseType] != osAuthorizeResponseType {
envData[constants.EnvKeyDefaultAuthorizeResponseType] = osAuthorizeResponseType
}
if val, ok := envData[constants.EnvKeyDefaultAuthorizeResponseMode]; !ok || val == "" {
envData[constants.EnvKeyDefaultAuthorizeResponseMode] = osAuthorizeResponseMode
// Set the default value to token type
if envData[constants.EnvKeyDefaultAuthorizeResponseMode] == "" {
envData[constants.EnvKeyDefaultAuthorizeResponseMode] = constants.ResponseModeQuery
}
}
if osAuthorizeResponseMode != "" && envData[constants.EnvKeyDefaultAuthorizeResponseMode] != osAuthorizeResponseMode {
envData[constants.EnvKeyDefaultAuthorizeResponseMode] = osAuthorizeResponseMode
}
err = memorystore.Provider.UpdateEnvStore(envData)
if err != nil {
log.Debug("Error while updating env store: ", err)

View File

@@ -75,7 +75,6 @@ func GetEnvData() (map[string]interface{}, error) {
}
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyEncryptionKey, decryptedEncryptionKey)
b64DecryptedConfig, err := crypto.DecryptB64(env.EnvData)
if err != nil {
log.Debug("Error while decrypting env data from B64: ", err)
@@ -201,7 +200,7 @@ func PersistEnv() error {
envValue := strings.TrimSpace(os.Getenv(key))
if envValue != "" {
switch key {
case constants.EnvKeyIsProd, constants.EnvKeyDisableBasicAuthentication, constants.EnvKeyDisableEmailVerification, constants.EnvKeyDisableLoginPage, constants.EnvKeyDisableMagicLinkLogin, constants.EnvKeyDisableSignUp, constants.EnvKeyDisableRedisForEnv, constants.EnvKeyDisableStrongPassword, constants.EnvKeyIsEmailServiceEnabled, constants.EnvKeyEnforceMultiFactorAuthentication, constants.EnvKeyDisableMultiFactorAuthentication, constants.EnvKeyAdminCookieSecure, constants.EnvKeyAppCookieSecure:
case constants.EnvKeyIsProd, constants.EnvKeyDisableBasicAuthentication, constants.EnvKeyDisableMobileBasicAuthentication, constants.EnvKeyDisableEmailVerification, constants.EnvKeyDisableLoginPage, constants.EnvKeyDisableMagicLinkLogin, constants.EnvKeyDisableSignUp, constants.EnvKeyDisableRedisForEnv, constants.EnvKeyDisableStrongPassword, constants.EnvKeyIsEmailServiceEnabled, constants.EnvKeyEnforceMultiFactorAuthentication, constants.EnvKeyDisableMultiFactorAuthentication, constants.EnvKeyAdminCookieSecure, constants.EnvKeyAppCookieSecure:
if envValueBool, err := strconv.ParseBool(envValue); err == nil {
if value.(bool) != envValueBool {
storeData[key] = envValueBool

View File

@@ -7,6 +7,7 @@ require (
github.com/arangodb/go-driver v1.2.1
github.com/aws/aws-sdk-go v1.44.109
github.com/coreos/go-oidc/v3 v3.1.0
github.com/couchbase/gocb/v2 v2.6.0
github.com/gin-gonic/gin v1.8.1
github.com/glebarez/sqlite v1.5.0
github.com/go-playground/validator/v10 v10.11.1 // indirect
@@ -20,12 +21,13 @@ require (
github.com/joho/godotenv v1.3.0
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
github.com/redis/go-redis/v9 v9.0.3 // indirect
github.com/robertkrimen/otto v0.0.0-20211024170158-b87d35c0b86f
github.com/sirupsen/logrus v1.8.1
github.com/stretchr/testify v1.8.0
github.com/vektah/gqlparser/v2 v2.5.1
go.mongodb.org/mongo-driver v1.8.1
golang.org/x/crypto v0.3.0
golang.org/x/crypto v0.4.0
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.28.1 // indirect
@@ -33,7 +35,7 @@ require (
gopkg.in/mail.v2 v2.3.1
gopkg.in/square/go-jose.v2 v2.6.0
gorm.io/driver/mysql v1.4.3
gorm.io/driver/postgres v1.4.5
gorm.io/driver/postgres v1.4.7
gorm.io/driver/sqlserver v1.4.1
gorm.io/gorm v1.24.1
gorm.io/gorm v1.24.2
)

View File

@@ -40,8 +40,6 @@ github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0/go.mod h1:Vt9s
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
@@ -60,26 +58,32 @@ github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYE
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w=
github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo=
github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/coreos/go-iptables v0.4.3/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
github.com/coreos/go-oidc/v3 v3.1.0 h1:6avEvcdvTa1qYsOZ6I5PRkSYHzpTNWgKYmaJfaYbrRw=
github.com/coreos/go-oidc/v3 v3.1.0/go.mod h1:rEJ/idjfUyfkBit1eI1fvyr+64/g9dcKpAm8MJMesvo=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/couchbase/gocb/v2 v2.6.0 h1:DhkLNatDcddCcS411D6kNwZspSEAWVeI/N3abzt/HLc=
github.com/couchbase/gocb/v2 v2.6.0/go.mod h1:5su8b1gBF3V4j07SiGw+CA0bK9a84YWEb6UH7up0MEs=
github.com/couchbase/gocbcore/v10 v10.2.0 h1:ZoSBLtcmt+lXbxVVT4SAhXDVNR+D48iSOZWNzHucVVk=
github.com/couchbase/gocbcore/v10 v10.2.0/go.mod h1:qkPnOBziCs0guMEEvd0cRFo+AjOW0yEL99cU3I4n3Ao=
github.com/couchbaselabs/gocaves/client v0.0.0-20220223122017-22859b310bd2 h1:UlwJ2GWpZQAQCLHyO3xHKcqAjUUcX2w7FKpbxCIUQks=
github.com/couchbaselabs/gocaves/client v0.0.0-20220223122017-22859b310bd2/go.mod h1:AVekAZwIY2stsJOMWLAS/0uA/+qdp7pjO8EHnl61QkY=
github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -110,8 +114,6 @@ github.com/glebarez/sqlite v1.5.0/go.mod h1:0wzXzTvfVJIN2GqRhCdMbnYd+m+aH5/QV7B3
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
@@ -132,7 +134,6 @@ github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/gocql/gocql v1.2.0 h1:TZhsCd7fRuye4VyHr3WCvWwIQaZUmjsqnSIXK9FcVCE=
github.com/gocql/gocql v1.2.0/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0=
github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
@@ -172,8 +173,9 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@@ -216,55 +218,14 @@ github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+l
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
github.com/jackc/pgconn v1.13.0 h1:3L1XMNV2Zvca/8BYhzcRFS70Lr0WlDg16Di6SFGAbys=
github.com/jackc/pgconn v1.13.0/go.mod h1:AnowpAqO4CMIIJNZl2VJp+KrkAZciAkhEl0W0JIobpI=
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc=
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.3.1 h1:nwj7qwf0S+Q7ISFfBndqeLwSwxs+4DPsbRFjECT1Y4Y=
github.com/jackc/pgproto3/v2 v2.3.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
github.com/jackc/pgtype v1.12.0 h1:Dlq8Qvcch7kiehm8wPGIW0W3KsCCHJnRacKW0UM8n5w=
github.com/jackc/pgtype v1.12.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
github.com/jackc/pgx/v4 v4.17.2 h1:0Ut0rpeKwvIVbMQ1KbMBU4h6wxehBI535LK6Flheh8E=
github.com/jackc/pgx/v4 v4.17.2/go.mod h1:lcxIZN44yMIrWI78a5CpucdD14hX0SBDbNRvjDBItsw=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.2.0 h1:NdPpngX0Y6z6XDFKqmFQaE+bCtkqzvQIOt1wvBlAqs8=
github.com/jackc/pgx/v5 v5.2.0/go.mod h1:Ptn7zmohNsWEsdxRawMzk3gaKma2obW+NWTnKa0S4nk=
github.com/jackc/puddle/v2 v2.1.2/go.mod h1:2lpufsF5mRHO6SuZkm0fNYxM6SWHfvyFj62KwNzgels=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
@@ -285,32 +246,20 @@ github.com/kevinmbeaulieu/eq-go v1.0.0/go.mod h1:G3S8ajA56gKBZm4UB9AOyoOS37JO3ro
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/logrusorgru/aurora/v3 v3.0.0/go.mod h1:vsR12bk5grlLvLXAYrBsb5Oc/N+LxAlxggSjiwMnCUc=
github.com/matryer/moq v0.2.7/go.mod h1:kITsx543GOENm48TUAQyJ9+SAvFSr7iGQXPoth/VUBk=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
@@ -350,6 +299,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/redis/go-redis/v9 v9.0.3 h1:+7mmR26M0IvyLxGZUHxu4GiBkJkVDid0Un+j4ScYu4k=
github.com/redis/go-redis/v9 v9.0.3/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/robertkrimen/otto v0.0.0-20211024170158-b87d35c0b86f h1:a7clxaGmmqtdNTXyvrp/lVO/Gnkzlhc/+dLs5v965GM=
@@ -359,24 +310,16 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/rs/zerolog v1.19.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
@@ -413,7 +356,6 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
go.mongodb.org/mongo-driver v1.8.1 h1:OZE4Wni/SJlrcmSIBRYNzunX5TKxjrTS4jKSnA99oKU=
go.mongodb.org/mongo-driver v1.8.1/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
@@ -421,36 +363,21 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b h1:huxqepDufQpLLIRXiVkTvnxrzJlpwmIWAObmcCcUFr0=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8=
golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -497,7 +424,6 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -524,10 +450,8 @@ golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220930213112-107f3e3c3b0b h1:uKO3Js8lXGjpjdc4J3rqs0/Ex5yDKUGfk43tTYWVLas=
golang.org/x/net v0.0.0-20220930213112-107f3e3c3b0b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.3.0 h1:VWL6FNY2bEEmsGVKabSlHu5Irp34xmMRoqb/9lF9lxk=
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -545,23 +469,19 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7 h1:ZrnxWX62AgTKOSagEqxvb3ffipvEDX2pl7E1TdqLqIc=
golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -598,29 +518,24 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI=
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -631,7 +546,6 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
@@ -639,12 +553,9 @@ golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgw
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -652,7 +563,6 @@ golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
@@ -676,8 +586,6 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -772,7 +680,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/mail.v2 v2.3.1 h1:WYFn/oANrAGP2C0dcV6/pbkPzv8yGzqTjPmTeO7qoXk=
@@ -798,15 +705,14 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.4.3 h1:/JhWJhO2v17d8hjApTltKNADm7K7YI2ogkR7avJUL3k=
gorm.io/driver/mysql v1.4.3/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c=
gorm.io/driver/postgres v1.4.5 h1:mTeXTTtHAgnS9PgmhN2YeUbazYpLhUI1doLnw42XUZc=
gorm.io/driver/postgres v1.4.5/go.mod h1:GKNQYSJ14qvWkvPwXljMGehpKrhlDNsqYRr5HnYGncg=
gorm.io/driver/postgres v1.4.7 h1:J06jXZCNq7Pdf7LIPn8tZn9LsWjd81BRSKveKNr0ZfA=
gorm.io/driver/postgres v1.4.7/go.mod h1:UJChCNLFKeBqQRE+HrkFUbKbq9idPXmTOk2u4Wok8S4=
gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0=
gorm.io/driver/sqlserver v1.4.1/go.mod h1:DJ4P+MeZbc5rvY58PnmN1Lnyvb5gw5NPzGshHDnJLig=
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
gorm.io/gorm v1.24.1-0.20221019064659-5dd2bb482755/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
gorm.io/gorm v1.24.1 h1:CgvzRniUdG67hBAzsxDGOAuq4Te1osVMYsa1eQbd4fs=
gorm.io/gorm v1.24.1/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
gorm.io/gorm v1.24.2 h1:9wR6CFD+G8nOusLdvkZelOEhpJVwwHzpQOUM+REd6U0=
gorm.io/gorm v1.24.2/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

File diff suppressed because it is too large Load Diff

View File

@@ -10,10 +10,11 @@ type AddEmailTemplateRequest struct {
}
type AddWebhookRequest struct {
EventName string `json:"event_name"`
Endpoint string `json:"endpoint"`
Enabled bool `json:"enabled"`
Headers map[string]interface{} `json:"headers"`
EventName string `json:"event_name"`
EventDescription *string `json:"event_description"`
Endpoint string `json:"endpoint"`
Enabled bool `json:"enabled"`
Headers map[string]interface{} `json:"headers"`
}
type AdminLoginInput struct {
@@ -109,10 +110,15 @@ type Env struct {
AppleClientSecret *string `json:"APPLE_CLIENT_SECRET"`
TwitterClientID *string `json:"TWITTER_CLIENT_ID"`
TwitterClientSecret *string `json:"TWITTER_CLIENT_SECRET"`
MicrosoftClientID *string `json:"MICROSOFT_CLIENT_ID"`
MicrosoftClientSecret *string `json:"MICROSOFT_CLIENT_SECRET"`
MicrosoftActiveDirectoryTenantID *string `json:"MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID"`
OrganizationName *string `json:"ORGANIZATION_NAME"`
OrganizationLogo *string `json:"ORGANIZATION_LOGO"`
AppCookieSecure bool `json:"APP_COOKIE_SECURE"`
AdminCookieSecure bool `json:"ADMIN_COOKIE_SECURE"`
DefaultAuthorizeResponseType *string `json:"DEFAULT_AUTHORIZE_RESPONSE_TYPE"`
DefaultAuthorizeResponseMode *string `json:"DEFAULT_AUTHORIZE_RESPONSE_MODE"`
}
type Error struct {
@@ -136,6 +142,10 @@ type GenerateJWTKeysResponse struct {
PrivateKey *string `json:"private_key"`
}
type GetUserRequest struct {
ID string `json:"id"`
}
type InviteMemberInput struct {
Emails []string `json:"emails"`
RedirectURI *string `json:"redirect_uri"`
@@ -171,6 +181,7 @@ type Meta struct {
IsLinkedinLoginEnabled bool `json:"is_linkedin_login_enabled"`
IsAppleLoginEnabled bool `json:"is_apple_login_enabled"`
IsTwitterLoginEnabled bool `json:"is_twitter_login_enabled"`
IsMicrosoftLoginEnabled bool `json:"is_microsoft_login_enabled"`
IsEmailVerificationEnabled bool `json:"is_email_verification_enabled"`
IsBasicAuthenticationEnabled bool `json:"is_basic_authentication_enabled"`
IsMagicLinkLoginEnabled bool `json:"is_magic_link_login_enabled"`
@@ -179,6 +190,33 @@ type Meta struct {
IsMultiFactorAuthEnabled bool `json:"is_multi_factor_auth_enabled"`
}
type MobileLoginInput struct {
PhoneNumber string `json:"phone_number"`
Password string `json:"password"`
Roles []string `json:"roles"`
Scope []string `json:"scope"`
State *string `json:"state"`
}
type MobileSignUpInput struct {
Email *string `json:"email"`
GivenName *string `json:"given_name"`
FamilyName *string `json:"family_name"`
MiddleName *string `json:"middle_name"`
Nickname *string `json:"nickname"`
Gender *string `json:"gender"`
Birthdate *string `json:"birthdate"`
PhoneNumber string `json:"phone_number"`
Picture *string `json:"picture"`
Password string `json:"password"`
ConfirmPassword string `json:"confirm_password"`
Roles []string `json:"roles"`
Scope []string `json:"scope"`
RedirectURI *string `json:"redirect_uri"`
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled"`
State *string `json:"state"`
}
type OAuthRevokeInput struct {
RefreshToken string `json:"refresh_token"`
}
@@ -312,8 +350,13 @@ type UpdateEnvInput struct {
AppleClientSecret *string `json:"APPLE_CLIENT_SECRET"`
TwitterClientID *string `json:"TWITTER_CLIENT_ID"`
TwitterClientSecret *string `json:"TWITTER_CLIENT_SECRET"`
MicrosoftClientID *string `json:"MICROSOFT_CLIENT_ID"`
MicrosoftClientSecret *string `json:"MICROSOFT_CLIENT_SECRET"`
MicrosoftActiveDirectoryTenantID *string `json:"MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID"`
OrganizationName *string `json:"ORGANIZATION_NAME"`
OrganizationLogo *string `json:"ORGANIZATION_LOGO"`
DefaultAuthorizeResponseType *string `json:"DEFAULT_AUTHORIZE_RESPONSE_TYPE"`
DefaultAuthorizeResponseMode *string `json:"DEFAULT_AUTHORIZE_RESPONSE_MODE"`
}
type UpdateProfileInput struct {
@@ -349,11 +392,12 @@ type UpdateUserInput struct {
}
type UpdateWebhookRequest struct {
ID string `json:"id"`
EventName *string `json:"event_name"`
Endpoint *string `json:"endpoint"`
Enabled *bool `json:"enabled"`
Headers map[string]interface{} `json:"headers"`
ID string `json:"id"`
EventName *string `json:"event_name"`
EventDescription *string `json:"event_description"`
Endpoint *string `json:"endpoint"`
Enabled *bool `json:"enabled"`
Headers map[string]interface{} `json:"headers"`
}
type User struct {
@@ -423,13 +467,14 @@ type VerifyOTPRequest struct {
}
type Webhook struct {
ID string `json:"id"`
EventName *string `json:"event_name"`
Endpoint *string `json:"endpoint"`
Enabled *bool `json:"enabled"`
Headers map[string]interface{} `json:"headers"`
CreatedAt *int64 `json:"created_at"`
UpdatedAt *int64 `json:"updated_at"`
ID string `json:"id"`
EventName *string `json:"event_name"`
EventDescription *string `json:"event_description"`
Endpoint *string `json:"endpoint"`
Enabled *bool `json:"enabled"`
Headers map[string]interface{} `json:"headers"`
CreatedAt *int64 `json:"created_at"`
UpdatedAt *int64 `json:"updated_at"`
}
type WebhookLog struct {

View File

@@ -6,527 +6,581 @@ scalar Map
scalar Any
type Pagination {
limit: Int64!
page: Int64!
offset: Int64!
total: Int64!
limit: Int64!
page: Int64!
offset: Int64!
total: Int64!
}
type Meta {
version: String!
client_id: String!
is_google_login_enabled: Boolean!
is_facebook_login_enabled: Boolean!
is_github_login_enabled: Boolean!
is_linkedin_login_enabled: Boolean!
is_apple_login_enabled: Boolean!
is_twitter_login_enabled: Boolean!
is_email_verification_enabled: Boolean!
is_basic_authentication_enabled: Boolean!
is_magic_link_login_enabled: Boolean!
is_sign_up_enabled: Boolean!
is_strong_password_enabled: Boolean!
is_multi_factor_auth_enabled: Boolean!
version: String!
client_id: String!
is_google_login_enabled: Boolean!
is_facebook_login_enabled: Boolean!
is_github_login_enabled: Boolean!
is_linkedin_login_enabled: Boolean!
is_apple_login_enabled: Boolean!
is_twitter_login_enabled: Boolean!
is_microsoft_login_enabled: Boolean!
is_email_verification_enabled: Boolean!
is_basic_authentication_enabled: Boolean!
is_magic_link_login_enabled: Boolean!
is_sign_up_enabled: Boolean!
is_strong_password_enabled: Boolean!
is_multi_factor_auth_enabled: Boolean!
}
type User {
id: ID!
email: String!
email_verified: Boolean!
signup_methods: String!
given_name: String
family_name: String
middle_name: String
nickname: String
# defaults to email
preferred_username: String
gender: String
birthdate: String
phone_number: String
phone_number_verified: Boolean
picture: String
roles: [String!]!
created_at: Int64
updated_at: Int64
revoked_timestamp: Int64
is_multi_factor_auth_enabled: Boolean
id: ID!
email: String!
email_verified: Boolean!
signup_methods: String!
given_name: String
family_name: String
middle_name: String
nickname: String
# defaults to email
preferred_username: String
gender: String
birthdate: String
phone_number: String
phone_number_verified: Boolean
picture: String
roles: [String!]!
created_at: Int64
updated_at: Int64
revoked_timestamp: Int64
is_multi_factor_auth_enabled: Boolean
}
type Users {
pagination: Pagination!
users: [User!]!
pagination: Pagination!
users: [User!]!
}
type VerificationRequest {
id: ID!
identifier: String
token: String
email: String
expires: Int64
created_at: Int64
updated_at: Int64
nonce: String
redirect_uri: String
id: ID!
identifier: String
token: String
email: String
expires: Int64
created_at: Int64
updated_at: Int64
nonce: String
redirect_uri: String
}
type VerificationRequests {
pagination: Pagination!
verification_requests: [VerificationRequest!]!
pagination: Pagination!
verification_requests: [VerificationRequest!]!
}
type Error {
message: String!
reason: String!
message: String!
reason: String!
}
type AuthResponse {
message: String!
should_show_otp_screen: Boolean
access_token: String
id_token: String
refresh_token: String
expires_in: Int64
user: User
message: String!
should_show_otp_screen: Boolean
access_token: String
id_token: String
refresh_token: String
expires_in: Int64
user: User
}
type Response {
message: String!
message: String!
}
type Env {
ACCESS_TOKEN_EXPIRY_TIME: String
ADMIN_SECRET: String
DATABASE_NAME: String
DATABASE_URL: String
DATABASE_TYPE: String
DATABASE_USERNAME: String
DATABASE_PASSWORD: String
DATABASE_HOST: String
DATABASE_PORT: String
CLIENT_ID: String!
CLIENT_SECRET: String!
CUSTOM_ACCESS_TOKEN_SCRIPT: String
SMTP_HOST: String
SMTP_PORT: String
SMTP_USERNAME: String
SMTP_PASSWORD: String
SMTP_LOCAL_NAME: String
SENDER_EMAIL: String
JWT_TYPE: String
JWT_SECRET: String
JWT_PRIVATE_KEY: String
JWT_PUBLIC_KEY: String
ALLOWED_ORIGINS: [String!]
APP_URL: String
REDIS_URL: String
RESET_PASSWORD_URL: String
DISABLE_EMAIL_VERIFICATION: Boolean!
DISABLE_BASIC_AUTHENTICATION: Boolean!
DISABLE_MAGIC_LINK_LOGIN: Boolean!
DISABLE_LOGIN_PAGE: Boolean!
DISABLE_SIGN_UP: Boolean!
DISABLE_REDIS_FOR_ENV: Boolean!
DISABLE_STRONG_PASSWORD: Boolean!
DISABLE_MULTI_FACTOR_AUTHENTICATION: Boolean!
ENFORCE_MULTI_FACTOR_AUTHENTICATION: Boolean!
ROLES: [String!]
PROTECTED_ROLES: [String!]
DEFAULT_ROLES: [String!]
JWT_ROLE_CLAIM: String
GOOGLE_CLIENT_ID: String
GOOGLE_CLIENT_SECRET: String
GITHUB_CLIENT_ID: String
GITHUB_CLIENT_SECRET: String
FACEBOOK_CLIENT_ID: String
FACEBOOK_CLIENT_SECRET: String
LINKEDIN_CLIENT_ID: String
LINKEDIN_CLIENT_SECRET: String
APPLE_CLIENT_ID: String
APPLE_CLIENT_SECRET: String
TWITTER_CLIENT_ID: String
TWITTER_CLIENT_SECRET: String
ORGANIZATION_NAME: String
ORGANIZATION_LOGO: String
APP_COOKIE_SECURE: Boolean!
ADMIN_COOKIE_SECURE: Boolean!
ACCESS_TOKEN_EXPIRY_TIME: String
ADMIN_SECRET: String
DATABASE_NAME: String
DATABASE_URL: String
DATABASE_TYPE: String
DATABASE_USERNAME: String
DATABASE_PASSWORD: String
DATABASE_HOST: String
DATABASE_PORT: String
CLIENT_ID: String!
CLIENT_SECRET: String!
CUSTOM_ACCESS_TOKEN_SCRIPT: String
SMTP_HOST: String
SMTP_PORT: String
SMTP_USERNAME: String
SMTP_PASSWORD: String
SMTP_LOCAL_NAME: String
SENDER_EMAIL: String
JWT_TYPE: String
JWT_SECRET: String
JWT_PRIVATE_KEY: String
JWT_PUBLIC_KEY: String
ALLOWED_ORIGINS: [String!]
APP_URL: String
REDIS_URL: String
RESET_PASSWORD_URL: String
DISABLE_EMAIL_VERIFICATION: Boolean!
DISABLE_BASIC_AUTHENTICATION: Boolean!
DISABLE_MAGIC_LINK_LOGIN: Boolean!
DISABLE_LOGIN_PAGE: Boolean!
DISABLE_SIGN_UP: Boolean!
DISABLE_REDIS_FOR_ENV: Boolean!
DISABLE_STRONG_PASSWORD: Boolean!
DISABLE_MULTI_FACTOR_AUTHENTICATION: Boolean!
ENFORCE_MULTI_FACTOR_AUTHENTICATION: Boolean!
ROLES: [String!]
PROTECTED_ROLES: [String!]
DEFAULT_ROLES: [String!]
JWT_ROLE_CLAIM: String
GOOGLE_CLIENT_ID: String
GOOGLE_CLIENT_SECRET: String
GITHUB_CLIENT_ID: String
GITHUB_CLIENT_SECRET: String
FACEBOOK_CLIENT_ID: String
FACEBOOK_CLIENT_SECRET: String
LINKEDIN_CLIENT_ID: String
LINKEDIN_CLIENT_SECRET: String
APPLE_CLIENT_ID: String
APPLE_CLIENT_SECRET: String
TWITTER_CLIENT_ID: String
TWITTER_CLIENT_SECRET: String
MICROSOFT_CLIENT_ID: String
MICROSOFT_CLIENT_SECRET: String
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID: String
ORGANIZATION_NAME: String
ORGANIZATION_LOGO: String
APP_COOKIE_SECURE: Boolean!
ADMIN_COOKIE_SECURE: Boolean!
DEFAULT_AUTHORIZE_RESPONSE_TYPE: String
DEFAULT_AUTHORIZE_RESPONSE_MODE: String
}
type ValidateJWTTokenResponse {
is_valid: Boolean!
claims: Map
is_valid: Boolean!
claims: Map
}
type GenerateJWTKeysResponse {
secret: String
public_key: String
private_key: String
secret: String
public_key: String
private_key: String
}
type Webhook {
id: ID!
event_name: String
endpoint: String
enabled: Boolean
headers: Map
created_at: Int64
updated_at: Int64
id: ID!
event_name: String # this is unique string
event_description: String
endpoint: String
enabled: Boolean
headers: Map
created_at: Int64
updated_at: Int64
}
type Webhooks {
pagination: Pagination!
webhooks: [Webhook!]!
pagination: Pagination!
webhooks: [Webhook!]!
}
type WebhookLog {
id: ID!
http_status: Int64
response: String
request: String
webhook_id: ID
created_at: Int64
updated_at: Int64
id: ID!
http_status: Int64
response: String
request: String
webhook_id: ID
created_at: Int64
updated_at: Int64
}
type TestEndpointResponse {
http_status: Int64
response: String
http_status: Int64
response: String
}
type WebhookLogs {
pagination: Pagination!
webhook_logs: [WebhookLog!]!
pagination: Pagination!
webhook_logs: [WebhookLog!]!
}
type EmailTemplate {
id: ID!
event_name: String!
template: String!
design: String!
subject: String!
created_at: Int64
updated_at: Int64
id: ID!
event_name: String!
template: String!
design: String!
subject: String!
created_at: Int64
updated_at: Int64
}
type EmailTemplates {
pagination: Pagination!
email_templates: [EmailTemplate!]!
pagination: Pagination!
email_templates: [EmailTemplate!]!
}
input UpdateEnvInput {
ACCESS_TOKEN_EXPIRY_TIME: String
ADMIN_SECRET: String
CUSTOM_ACCESS_TOKEN_SCRIPT: String
OLD_ADMIN_SECRET: String
SMTP_HOST: String
SMTP_PORT: String
SMTP_USERNAME: String
SMTP_PASSWORD: String
SMTP_LOCAL_NAME: String
SENDER_EMAIL: String
JWT_TYPE: String
JWT_SECRET: String
JWT_PRIVATE_KEY: String
JWT_PUBLIC_KEY: String
ALLOWED_ORIGINS: [String!]
APP_URL: String
RESET_PASSWORD_URL: String
APP_COOKIE_SECURE: Boolean
ADMIN_COOKIE_SECURE: Boolean
DISABLE_EMAIL_VERIFICATION: Boolean
DISABLE_BASIC_AUTHENTICATION: Boolean
DISABLE_MAGIC_LINK_LOGIN: Boolean
DISABLE_LOGIN_PAGE: Boolean
DISABLE_SIGN_UP: Boolean
DISABLE_REDIS_FOR_ENV: Boolean
DISABLE_STRONG_PASSWORD: Boolean
DISABLE_MULTI_FACTOR_AUTHENTICATION: Boolean
ENFORCE_MULTI_FACTOR_AUTHENTICATION: Boolean
ROLES: [String!]
PROTECTED_ROLES: [String!]
DEFAULT_ROLES: [String!]
JWT_ROLE_CLAIM: String
GOOGLE_CLIENT_ID: String
GOOGLE_CLIENT_SECRET: String
GITHUB_CLIENT_ID: String
GITHUB_CLIENT_SECRET: String
FACEBOOK_CLIENT_ID: String
FACEBOOK_CLIENT_SECRET: String
LINKEDIN_CLIENT_ID: String
LINKEDIN_CLIENT_SECRET: String
APPLE_CLIENT_ID: String
APPLE_CLIENT_SECRET: String
TWITTER_CLIENT_ID: String
TWITTER_CLIENT_SECRET: String
ORGANIZATION_NAME: String
ORGANIZATION_LOGO: String
ACCESS_TOKEN_EXPIRY_TIME: String
ADMIN_SECRET: String
CUSTOM_ACCESS_TOKEN_SCRIPT: String
OLD_ADMIN_SECRET: String
SMTP_HOST: String
SMTP_PORT: String
SMTP_USERNAME: String
SMTP_PASSWORD: String
SMTP_LOCAL_NAME: String
SENDER_EMAIL: String
JWT_TYPE: String
JWT_SECRET: String
JWT_PRIVATE_KEY: String
JWT_PUBLIC_KEY: String
ALLOWED_ORIGINS: [String!]
APP_URL: String
RESET_PASSWORD_URL: String
APP_COOKIE_SECURE: Boolean
ADMIN_COOKIE_SECURE: Boolean
DISABLE_EMAIL_VERIFICATION: Boolean
DISABLE_BASIC_AUTHENTICATION: Boolean
DISABLE_MAGIC_LINK_LOGIN: Boolean
DISABLE_LOGIN_PAGE: Boolean
DISABLE_SIGN_UP: Boolean
DISABLE_REDIS_FOR_ENV: Boolean
DISABLE_STRONG_PASSWORD: Boolean
DISABLE_MULTI_FACTOR_AUTHENTICATION: Boolean
ENFORCE_MULTI_FACTOR_AUTHENTICATION: Boolean
ROLES: [String!]
PROTECTED_ROLES: [String!]
DEFAULT_ROLES: [String!]
JWT_ROLE_CLAIM: String
GOOGLE_CLIENT_ID: String
GOOGLE_CLIENT_SECRET: String
GITHUB_CLIENT_ID: String
GITHUB_CLIENT_SECRET: String
FACEBOOK_CLIENT_ID: String
FACEBOOK_CLIENT_SECRET: String
LINKEDIN_CLIENT_ID: String
LINKEDIN_CLIENT_SECRET: String
APPLE_CLIENT_ID: String
APPLE_CLIENT_SECRET: String
TWITTER_CLIENT_ID: String
TWITTER_CLIENT_SECRET: String
MICROSOFT_CLIENT_ID: String
MICROSOFT_CLIENT_SECRET: String
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID: String
ORGANIZATION_NAME: String
ORGANIZATION_LOGO: String
DEFAULT_AUTHORIZE_RESPONSE_TYPE: String
DEFAULT_AUTHORIZE_RESPONSE_MODE: String
}
input AdminLoginInput {
admin_secret: String!
admin_secret: String!
}
input AdminSignupInput {
admin_secret: String!
admin_secret: String!
}
input MobileSignUpInput {
email: String
given_name: String
family_name: String
middle_name: String
nickname: String
gender: String
birthdate: String
phone_number: String!
picture: String
password: String!
confirm_password: String!
roles: [String!]
scope: [String!]
redirect_uri: String
is_multi_factor_auth_enabled: Boolean
# state is used for authorization code grant flow
# it is used to get code for an on-going auth process during login
# and use that code for setting `c_hash` in id_token
state: String
}
input SignUpInput {
email: String!
given_name: String
family_name: String
middle_name: String
nickname: String
gender: String
birthdate: String
phone_number: String
picture: String
password: String!
confirm_password: String!
roles: [String!]
scope: [String!]
redirect_uri: String
is_multi_factor_auth_enabled: Boolean
# state is used for authorization code grant flow
# it is used to get code for an on-going auth process during login
# and use that code for setting `c_hash` in id_token
state: String
email: String!
given_name: String
family_name: String
middle_name: String
nickname: String
gender: String
birthdate: String
phone_number: String
picture: String
password: String!
confirm_password: String!
roles: [String!]
scope: [String!]
redirect_uri: String
is_multi_factor_auth_enabled: Boolean
# state is used for authorization code grant flow
# it is used to get code for an on-going auth process during login
# and use that code for setting `c_hash` in id_token
state: String
}
input LoginInput {
email: String!
password: String!
roles: [String!]
scope: [String!]
# state is used for authorization code grant flow
# it is used to get code for an on-going auth process during login
# and use that code for setting `c_hash` in id_token
state: String
email: String!
password: String!
roles: [String!]
scope: [String!]
# state is used for authorization code grant flow
# it is used to get code for an on-going auth process during login
# and use that code for setting `c_hash` in id_token
state: String
}
input MobileLoginInput {
phone_number: String!
password: String!
roles: [String!]
scope: [String!]
# state is used for authorization code grant flow
# it is used to get code for an on-going auth process during login
# and use that code for setting `c_hash` in id_token
state: String
}
input VerifyEmailInput {
token: String!
# state is used for authorization code grant flow
# it is used to get code for an on-going auth process during login
# and use that code for setting `c_hash` in id_token
state: String
token: String!
# state is used for authorization code grant flow
# it is used to get code for an on-going auth process during login
# and use that code for setting `c_hash` in id_token
state: String
}
input ResendVerifyEmailInput {
email: String!
identifier: String!
# state is used for authorization code grant flow
# it is used to get code for an on-going auth process during login
# and use that code for setting `c_hash` in id_token
state: String
email: String!
identifier: String!
# state is used for authorization code grant flow
# it is used to get code for an on-going auth process during login
# and use that code for setting `c_hash` in id_token
state: String
}
input UpdateProfileInput {
old_password: String
new_password: String
confirm_new_password: String
email: String
given_name: String
family_name: String
middle_name: String
nickname: String
gender: String
birthdate: String
phone_number: String
picture: String
is_multi_factor_auth_enabled: Boolean
old_password: String
new_password: String
confirm_new_password: String
email: String
given_name: String
family_name: String
middle_name: String
nickname: String
gender: String
birthdate: String
phone_number: String
picture: String
is_multi_factor_auth_enabled: Boolean
}
input UpdateUserInput {
id: ID!
email: String
email_verified: Boolean
given_name: String
family_name: String
middle_name: String
nickname: String
gender: String
birthdate: String
phone_number: String
picture: String
roles: [String]
is_multi_factor_auth_enabled: Boolean
id: ID!
email: String
email_verified: Boolean
given_name: String
family_name: String
middle_name: String
nickname: String
gender: String
birthdate: String
phone_number: String
picture: String
roles: [String]
is_multi_factor_auth_enabled: Boolean
}
input ForgotPasswordInput {
email: String!
state: String
redirect_uri: String
email: String!
state: String
redirect_uri: String
}
input ResetPasswordInput {
token: String!
password: String!
confirm_password: String!
token: String!
password: String!
confirm_password: String!
}
input DeleteUserInput {
email: String!
email: String!
}
input MagicLinkLoginInput {
email: String!
roles: [String!]
scope: [String!]
state: String
redirect_uri: String
email: String!
roles: [String!]
scope: [String!]
state: String
redirect_uri: String
}
input SessionQueryInput {
roles: [String!]
scope: [String!]
roles: [String!]
scope: [String!]
}
input PaginationInput {
limit: Int64
page: Int64
limit: Int64
page: Int64
}
input PaginatedInput {
pagination: PaginationInput
pagination: PaginationInput
}
input OAuthRevokeInput {
refresh_token: String!
refresh_token: String!
}
input InviteMemberInput {
emails: [String!]!
redirect_uri: String
emails: [String!]!
redirect_uri: String
}
input UpdateAccessInput {
user_id: String!
user_id: String!
}
input ValidateJWTTokenInput {
token_type: String!
token: String!
roles: [String!]
token_type: String!
token: String!
roles: [String!]
}
input GenerateJWTKeysInput {
type: String!
type: String!
}
input ListWebhookLogRequest {
pagination: PaginationInput
webhook_id: String
pagination: PaginationInput
webhook_id: String
}
input AddWebhookRequest {
event_name: String!
endpoint: String!
enabled: Boolean!
headers: Map
event_name: String!
event_description: String
endpoint: String!
enabled: Boolean!
headers: Map
}
input UpdateWebhookRequest {
id: ID!
event_name: String
endpoint: String
enabled: Boolean
headers: Map
id: ID!
event_name: String
event_description: String
endpoint: String
enabled: Boolean
headers: Map
}
input WebhookRequest {
id: ID!
id: ID!
}
input TestEndpointRequest {
endpoint: String!
event_name: String!
headers: Map
endpoint: String!
event_name: String!
headers: Map
}
input AddEmailTemplateRequest {
event_name: String!
subject: String!
template: String!
# Design value is set when editor is used
# If raw HTML is used design value is set to null
design: String
event_name: String!
subject: String!
template: String!
# Design value is set when editor is used
# If raw HTML is used design value is set to null
design: String
}
input UpdateEmailTemplateRequest {
id: ID!
event_name: String
template: String
subject: String
# Design value is set when editor is used
# If raw HTML is used design value is set to null
design: String
id: ID!
event_name: String
template: String
subject: String
# Design value is set when editor is used
# If raw HTML is used design value is set to null
design: String
}
input DeleteEmailTemplateRequest {
id: ID!
id: ID!
}
input VerifyOTPRequest {
email: String!
otp: String!
# state is used for authorization code grant flow
# it is used to get code for an on-going auth process during login
# and use that code for setting `c_hash` in id_token
state: String
email: String!
otp: String!
# state is used for authorization code grant flow
# it is used to get code for an on-going auth process during login
# and use that code for setting `c_hash` in id_token
state: String
}
input ResendOTPRequest {
email: String!
# state is used for authorization code grant flow
# it is used to get code for an on-going auth process during login
# and use that code for setting `c_hash` in id_token
state: String
email: String!
# state is used for authorization code grant flow
# it is used to get code for an on-going auth process during login
# and use that code for setting `c_hash` in id_token
state: String
}
input GetUserRequest {
id: String!
}
type Mutation {
signup(params: SignUpInput!): AuthResponse!
login(params: LoginInput!): AuthResponse!
magic_link_login(params: MagicLinkLoginInput!): Response!
logout: Response!
update_profile(params: UpdateProfileInput!): Response!
verify_email(params: VerifyEmailInput!): AuthResponse!
resend_verify_email(params: ResendVerifyEmailInput!): Response!
forgot_password(params: ForgotPasswordInput!): Response!
reset_password(params: ResetPasswordInput!): Response!
revoke(params: OAuthRevokeInput!): Response!
verify_otp(params: VerifyOTPRequest!): AuthResponse!
resend_otp(params: ResendOTPRequest!): Response!
# admin only apis
_delete_user(params: DeleteUserInput!): Response!
_update_user(params: UpdateUserInput!): User!
_admin_signup(params: AdminSignupInput!): Response!
_admin_login(params: AdminLoginInput!): Response!
_admin_logout: Response!
_update_env(params: UpdateEnvInput!): Response!
_invite_members(params: InviteMemberInput!): Response!
_revoke_access(param: UpdateAccessInput!): Response!
_enable_access(param: UpdateAccessInput!): Response!
_generate_jwt_keys(params: GenerateJWTKeysInput!): GenerateJWTKeysResponse!
_add_webhook(params: AddWebhookRequest!): Response!
_update_webhook(params: UpdateWebhookRequest!): Response!
_delete_webhook(params: WebhookRequest!): Response!
_test_endpoint(params: TestEndpointRequest!): TestEndpointResponse!
_add_email_template(params: AddEmailTemplateRequest!): Response!
_update_email_template(params: UpdateEmailTemplateRequest!): Response!
_delete_email_template(params: DeleteEmailTemplateRequest!): Response!
signup(params: SignUpInput!): AuthResponse!
mobile_signup(params: MobileSignUpInput): AuthResponse!
login(params: LoginInput!): AuthResponse!
mobile_login(params: MobileLoginInput!): AuthResponse!
magic_link_login(params: MagicLinkLoginInput!): Response!
logout: Response!
update_profile(params: UpdateProfileInput!): Response!
verify_email(params: VerifyEmailInput!): AuthResponse!
resend_verify_email(params: ResendVerifyEmailInput!): Response!
forgot_password(params: ForgotPasswordInput!): Response!
reset_password(params: ResetPasswordInput!): Response!
revoke(params: OAuthRevokeInput!): Response!
verify_otp(params: VerifyOTPRequest!): AuthResponse!
resend_otp(params: ResendOTPRequest!): Response!
# admin only apis
_delete_user(params: DeleteUserInput!): Response!
_update_user(params: UpdateUserInput!): User!
_admin_signup(params: AdminSignupInput!): Response!
_admin_login(params: AdminLoginInput!): Response!
_admin_logout: Response!
_update_env(params: UpdateEnvInput!): Response!
_invite_members(params: InviteMemberInput!): Response!
_revoke_access(param: UpdateAccessInput!): Response!
_enable_access(param: UpdateAccessInput!): Response!
_generate_jwt_keys(params: GenerateJWTKeysInput!): GenerateJWTKeysResponse!
_add_webhook(params: AddWebhookRequest!): Response!
_update_webhook(params: UpdateWebhookRequest!): Response!
_delete_webhook(params: WebhookRequest!): Response!
_test_endpoint(params: TestEndpointRequest!): TestEndpointResponse!
_add_email_template(params: AddEmailTemplateRequest!): Response!
_update_email_template(params: UpdateEmailTemplateRequest!): Response!
_delete_email_template(params: DeleteEmailTemplateRequest!): Response!
}
type Query {
meta: Meta!
session(params: SessionQueryInput): AuthResponse!
profile: User!
validate_jwt_token(params: ValidateJWTTokenInput!): ValidateJWTTokenResponse!
# admin only apis
_users(params: PaginatedInput): Users!
_verification_requests(params: PaginatedInput): VerificationRequests!
_admin_session: Response!
_env: Env!
_webhook(params: WebhookRequest!): Webhook!
_webhooks(params: PaginatedInput): Webhooks!
_webhook_logs(params: ListWebhookLogRequest): WebhookLogs!
_email_templates(params: PaginatedInput): EmailTemplates!
meta: Meta!
session(params: SessionQueryInput): AuthResponse!
profile: User!
validate_jwt_token(params: ValidateJWTTokenInput!): ValidateJWTTokenResponse!
# admin only apis
_users(params: PaginatedInput): Users!
_user(params: GetUserRequest!): User!
_verification_requests(params: PaginatedInput): VerificationRequests!
_admin_session: Response!
_env: Env!
_webhook(params: WebhookRequest!): Webhook!
_webhooks(params: PaginatedInput): Webhooks!
_webhook_logs(params: ListWebhookLogRequest): WebhookLogs!
_email_templates(params: PaginatedInput): EmailTemplates!
}

View File

@@ -16,11 +16,21 @@ func (r *mutationResolver) Signup(ctx context.Context, params model.SignUpInput)
return resolvers.SignupResolver(ctx, params)
}
// MobileSignup is the resolver for the mobile_signup field.
func (r *mutationResolver) MobileSignup(ctx context.Context, params *model.MobileSignUpInput) (*model.AuthResponse, error) {
return resolvers.MobileSignupResolver(ctx, params)
}
// Login is the resolver for the login field.
func (r *mutationResolver) Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, error) {
return resolvers.LoginResolver(ctx, params)
}
// MobileLogin is the resolver for the mobile_login field.
func (r *mutationResolver) MobileLogin(ctx context.Context, params model.MobileLoginInput) (*model.AuthResponse, error) {
return resolvers.MobileLoginResolver(ctx, params)
}
// MagicLinkLogin is the resolver for the magic_link_login field.
func (r *mutationResolver) MagicLinkLogin(ctx context.Context, params model.MagicLinkLoginInput) (*model.Response, error) {
return resolvers.MagicLinkLoginResolver(ctx, params)
@@ -181,6 +191,11 @@ func (r *queryResolver) Users(ctx context.Context, params *model.PaginatedInput)
return resolvers.UsersResolver(ctx, params)
}
// User is the resolver for the _user field.
func (r *queryResolver) User(ctx context.Context, params model.GetUserRequest) (*model.User, error) {
return resolvers.UserResolver(ctx, params)
}
// VerificationRequests is the resolver for the _verification_requests field.
func (r *queryResolver) VerificationRequests(ctx context.Context, params *model.PaginatedInput) (*model.VerificationRequests, error) {
return resolvers.VerificationRequestsResolver(ctx, params)

View File

@@ -76,7 +76,7 @@ func AppHandler() gin.HandlerFunc {
"data": map[string]interface{}{
"authorizerURL": hostname,
"redirectURL": redirectURI,
"scope": scope,
"scope": strings.Join(scope, " "),
"state": state,
"organizationName": orgName,
"organizationLogo": orgLogo,

View File

@@ -83,7 +83,11 @@ func AuthorizeHandler() gin.HandlerFunc {
}
if responseMode == "" {
responseMode = constants.ResponseModeQuery
if val, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyDefaultAuthorizeResponseMode); err == nil {
responseType = val
} else {
responseType = constants.ResponseModeQuery
}
}
if redirectURI == "" {
@@ -91,7 +95,11 @@ func AuthorizeHandler() gin.HandlerFunc {
}
if responseType == "" {
responseType = "token"
if val, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyDefaultAuthorizeResponseType); err == nil {
responseType = val
} else {
responseType = constants.ResponseTypeToken
}
}
if err := validateAuthorizeRequest(responseType, responseMode, clientID, state, codeChallenge); err != nil {
@@ -186,7 +194,7 @@ func AuthorizeHandler() gin.HandlerFunc {
// rollover the session for security
go memorystore.Provider.DeleteUserSession(sessionKey, claims.Nonce)
if responseType == constants.ResponseTypeCode {
newSessionTokenData, newSessionToken, err := token.CreateSessionToken(user, nonce, claims.Roles, scope, claims.LoginMethod)
newSessionTokenData, newSessionToken, newSessionExpiresAt, err := token.CreateSessionToken(user, nonce, claims.Roles, scope, claims.LoginMethod)
if err != nil {
log.Debug("CreateSessionToken failed: ", err)
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
@@ -207,7 +215,7 @@ func AuthorizeHandler() gin.HandlerFunc {
return
}
if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+newSessionTokenData.Nonce, newSessionToken); err != nil {
if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+newSessionTokenData.Nonce, newSessionToken, newSessionExpiresAt); err != nil {
log.Debug("SetUserSession failed: ", err)
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
return
@@ -263,13 +271,13 @@ func AuthorizeHandler() gin.HandlerFunc {
return
}
if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+nonce, authToken.FingerPrintHash); err != nil {
if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+nonce, authToken.FingerPrintHash, authToken.SessionTokenExpiresAt); err != nil {
log.Debug("SetUserSession failed: ", err)
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
return
}
if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+nonce, authToken.AccessToken.Token); err != nil {
if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+nonce, authToken.AccessToken.Token, authToken.AccessToken.ExpiresAt); err != nil {
log.Debug("SetUserSession failed: ", err)
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
return
@@ -284,7 +292,7 @@ func AuthorizeHandler() gin.HandlerFunc {
"access_token": authToken.AccessToken.Token,
"id_token": authToken.IDToken.Token,
"state": state,
"scope": scope,
"scope": strings.Join(scope, " "),
"token_type": "Bearer",
"expires_in": authToken.AccessToken.ExpiresAt,
}
@@ -297,7 +305,7 @@ func AuthorizeHandler() gin.HandlerFunc {
if authToken.RefreshToken != nil {
res["refresh_token"] = authToken.RefreshToken.Token
params += "&refresh_token=" + authToken.RefreshToken.Token
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token, authToken.RefreshToken.ExpiresAt)
}
if responseMode == constants.ResponseModeQuery {
@@ -322,13 +330,12 @@ func AuthorizeHandler() gin.HandlerFunc {
}
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
return
}
}
func validateAuthorizeRequest(responseType, responseMode, clientID, state, codeChallenge string) error {
if strings.TrimSpace(state) == "" {
return fmt.Errorf("invalid state. state is required to prevent csrf attack", responseMode)
return fmt.Errorf("invalid state. state is required to prevent csrf attack")
}
if responseType != constants.ResponseTypeCode && responseType != constants.ResponseTypeToken && responseType != constants.ResponseTypeIDToken {
return fmt.Errorf("invalid response type %s. 'code' & 'token' are valid response_type", responseMode)

View File

@@ -2,7 +2,7 @@ package handlers
import (
"github.com/99designs/gqlgen/graphql/handler"
graph "github.com/authorizerdev/authorizer/server/graph"
"github.com/authorizerdev/authorizer/server/graph"
"github.com/authorizerdev/authorizer/server/graph/generated"
"github.com/gin-gonic/gin"
)

View File

@@ -27,10 +27,8 @@ func JWKsHandler() gin.HandlerFunc {
c.JSON(500, gin.H{
"error": err.Error(),
})
return
}
c.JSON(200, gin.H{
"keys": []map[string]string{
data,

View File

@@ -47,7 +47,14 @@ func LogoutHandler() gin.HandlerFunc {
return
}
memorystore.Provider.DeleteUserSession(sessionData.Subject, sessionData.Nonce)
userID := sessionData.Subject
loginMethod := sessionData.LoginMethod
sessionToken := userID
if loginMethod != "" {
sessionToken = loginMethod + ":" + userID
}
memorystore.Provider.DeleteUserSession(sessionToken, sessionData.Nonce)
cookie.DeleteSession(gc)
if redirectURL != "" {

View File

@@ -5,7 +5,7 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"io"
"net/http"
"strconv"
"strings"
@@ -70,6 +70,8 @@ func OAuthCallbackHandler() gin.HandlerFunc {
user, err = processAppleUserInfo(oauthCode)
case constants.AuthRecipeMethodTwitter:
user, err = processTwitterUserInfo(oauthCode, sessionState)
case constants.AuthRecipeMethodMicrosoft:
user, err = processMicrosoftUserInfo(oauthCode)
default:
log.Info("Invalid oauth provider")
err = fmt.Errorf(`invalid oauth provider`)
@@ -247,12 +249,12 @@ func OAuthCallbackHandler() gin.HandlerFunc {
sessionKey := provider + ":" + user.ID
cookie.SetSession(ctx, authToken.FingerPrintHash)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash, authToken.SessionTokenExpiresAt)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token, authToken.AccessToken.ExpiresAt)
if authToken.RefreshToken != nil {
params += `&refresh_token=` + authToken.RefreshToken.Token
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token, authToken.RefreshToken.ExpiresAt)
}
go func() {
@@ -312,7 +314,7 @@ func processGoogleUserInfo(code string) (models.User, error) {
func processGithubUserInfo(code string) (models.User, error) {
user := models.User{}
oauth2Token, err := oauth.OAuthProviders.GithubConfig.Exchange(oauth2.NoContext, code)
oauth2Token, err := oauth.OAuthProviders.GithubConfig.Exchange(context.TODO(), code)
if err != nil {
log.Debug("Failed to exchange code for token: ", err)
return user, fmt.Errorf("invalid github exchange code: %s", err.Error())
@@ -334,7 +336,7 @@ func processGithubUserInfo(code string) (models.User, error) {
}
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
body, err := io.ReadAll(response.Body)
if err != nil {
log.Debug("Failed to read github user info response body: ", err)
return user, fmt.Errorf("failed to read github response body: %s", err.Error())
@@ -383,7 +385,7 @@ func processGithubUserInfo(code string) (models.User, error) {
}
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
body, err := io.ReadAll(response.Body)
if err != nil {
log.Debug("Failed to read github user email response body: ", err)
return user, fmt.Errorf("failed to read github response body: %s", err.Error())
@@ -419,7 +421,7 @@ func processGithubUserInfo(code string) (models.User, error) {
func processFacebookUserInfo(code string) (models.User, error) {
user := models.User{}
oauth2Token, err := oauth.OAuthProviders.FacebookConfig.Exchange(oauth2.NoContext, code)
oauth2Token, err := oauth.OAuthProviders.FacebookConfig.Exchange(context.TODO(), code)
if err != nil {
log.Debug("Invalid facebook exchange code: ", err)
return user, fmt.Errorf("invalid facebook exchange code: %s", err.Error())
@@ -438,7 +440,7 @@ func processFacebookUserInfo(code string) (models.User, error) {
}
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
body, err := io.ReadAll(response.Body)
if err != nil {
log.Debug("Failed to read facebook response: ", err)
return user, fmt.Errorf("failed to read facebook response body: %s", err.Error())
@@ -470,7 +472,7 @@ func processFacebookUserInfo(code string) (models.User, error) {
func processLinkedInUserInfo(code string) (models.User, error) {
user := models.User{}
oauth2Token, err := oauth.OAuthProviders.LinkedInConfig.Exchange(oauth2.NoContext, code)
oauth2Token, err := oauth.OAuthProviders.LinkedInConfig.Exchange(context.TODO(), code)
if err != nil {
log.Debug("Failed to exchange code for token: ", err)
return user, fmt.Errorf("invalid linkedin exchange code: %s", err.Error())
@@ -493,7 +495,7 @@ func processLinkedInUserInfo(code string) (models.User, error) {
}
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
body, err := io.ReadAll(response.Body)
if err != nil {
log.Debug("Failed to read linkedin user info response body: ", err)
return user, fmt.Errorf("failed to read linkedin response body: %s", err.Error())
@@ -523,7 +525,7 @@ func processLinkedInUserInfo(code string) (models.User, error) {
}
defer response.Body.Close()
body, err = ioutil.ReadAll(response.Body)
body, err = io.ReadAll(response.Body)
if err != nil {
log.Debug("Failed to read linkedin email info response body: ", err)
return user, fmt.Errorf("failed to read linkedin email response body: %s", err.Error())
@@ -552,7 +554,7 @@ func processLinkedInUserInfo(code string) (models.User, error) {
func processAppleUserInfo(code string) (models.User, error) {
user := models.User{}
oauth2Token, err := oauth.OAuthProviders.AppleConfig.Exchange(oauth2.NoContext, code)
oauth2Token, err := oauth.OAuthProviders.AppleConfig.Exchange(context.TODO(), code)
if err != nil {
log.Debug("Failed to exchange code for token: ", err)
return user, fmt.Errorf("invalid apple exchange code: %s", err.Error())
@@ -605,7 +607,7 @@ func processAppleUserInfo(code string) (models.User, error) {
func processTwitterUserInfo(code, verifier string) (models.User, error) {
user := models.User{}
oauth2Token, err := oauth.OAuthProviders.TwitterConfig.Exchange(oauth2.NoContext, code, oauth2.SetAuthURLParam("code_verifier", verifier))
oauth2Token, err := oauth.OAuthProviders.TwitterConfig.Exchange(context.TODO(), code, oauth2.SetAuthURLParam("code_verifier", verifier))
if err != nil {
log.Debug("Failed to exchange code for token: ", err)
return user, fmt.Errorf("invalid twitter exchange code: %s", err.Error())
@@ -628,7 +630,7 @@ func processTwitterUserInfo(code, verifier string) (models.User, error) {
}
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
body, err := io.ReadAll(response.Body)
if err != nil {
log.Debug("Failed to read Twitter user info response body: ", err)
return user, fmt.Errorf("failed to read Twitter response body: %s", err.Error())
@@ -669,3 +671,37 @@ func processTwitterUserInfo(code, verifier string) (models.User, error) {
return user, nil
}
// process microsoft user information
func processMicrosoftUserInfo(code string) (models.User, error) {
user := models.User{}
ctx := context.Background()
oauth2Token, err := oauth.OAuthProviders.MicrosoftConfig.Exchange(ctx, code)
if err != nil {
log.Debug("Failed to exchange code for token: ", err)
return user, fmt.Errorf("invalid google exchange code: %s", err.Error())
}
verifier := oauth.OIDCProviders.MicrosoftOIDC.Verifier(&oidc.Config{ClientID: oauth.OAuthProviders.MicrosoftConfig.ClientID})
// Extract the ID Token from OAuth2 token.
rawIDToken, ok := oauth2Token.Extra("id_token").(string)
if !ok {
log.Debug("Failed to extract ID Token from OAuth2 token")
return user, fmt.Errorf("unable to extract id_token")
}
// Parse and verify ID Token payload.
idToken, err := verifier.Verify(ctx, rawIDToken)
if err != nil {
log.Debug("Failed to verify ID Token: ", err)
return user, fmt.Errorf("unable to verify id_token: %s", err.Error())
}
if err := idToken.Claims(&user); err != nil {
log.Debug("Failed to parse ID Token claims: ", err)
return user, fmt.Errorf("unable to extract claims")
}
return user, nil
}

View File

@@ -209,6 +209,24 @@ func OAuthLoginHandler() gin.HandlerFunc {
// check: https://github.com/golang/oauth2/issues/449
url := oauth.OAuthProviders.AppleConfig.AuthCodeURL(oauthStateString, oauth2.SetAuthURLParam("response_mode", "form_post")) + "&scope=name email"
c.Redirect(http.StatusTemporaryRedirect, url)
case constants.AuthRecipeMethodMicrosoft:
if oauth.OAuthProviders.MicrosoftConfig == nil {
log.Debug("Microsoft OAuth provider is not configured")
isProviderConfigured = false
break
}
err := memorystore.Provider.SetState(oauthStateString, constants.AuthRecipeMethodMicrosoft)
if err != nil {
log.Debug("Error setting state: ", err)
c.JSON(500, gin.H{
"error": "internal server error",
})
return
}
// during the init of OAuthProvider authorizer url might be empty
oauth.OAuthProviders.MicrosoftConfig.RedirectURL = hostname + "/oauth_callback/" + constants.AuthRecipeMethodMicrosoft
url := oauth.OAuthProviders.MicrosoftConfig.AuthCodeURL(oauthStateString)
c.Redirect(http.StatusTemporaryRedirect, url)
default:
log.Debug("Invalid oauth provider: ", provider)
c.JSON(422, gin.H{

View File

@@ -20,9 +20,11 @@ func OpenIDConfigurationHandler() gin.HandlerFunc {
"token_endpoint": issuer + "/oauth/token",
"userinfo_endpoint": issuer + "/userinfo",
"jwks_uri": issuer + "/.well-known/jwks.json",
"registration_endpoint": issuer + "/app",
"response_types_supported": []string{"code", "token", "id_token"},
"scopes_supported": []string{"openid", "email", "profile", "email_verified", "given_name", "family_name", "nick_name", "picture"},
"scopes_supported": []string{"openid", "email", "profile"},
"response_modes_supported": []string{"query", "fragment", "form_post", "web_message"},
"subject_types_supported": "public",
"id_token_signing_alg_values_supported": []string{jwtType},
"claims_supported": []string{"aud", "exp", "iss", "iat", "sub", "given_name", "family_name", "middle_name", "nickname", "preferred_username", "picture", "email", "email_verified", "roles", "role", "gender", "birthdate", "phone_number", "phone_number_verified", "nonce", "updated_at", "created_at", "revoked_timestamp", "login_method", "signup_methods", "token_type"},
})

View File

@@ -247,8 +247,8 @@ func TokenHandler() gin.HandlerFunc {
return
}
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash, authToken.SessionTokenExpiresAt)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token, authToken.AccessToken.ExpiresAt)
cookie.SetSession(gc, authToken.FingerPrintHash)
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
@@ -259,14 +259,14 @@ func TokenHandler() gin.HandlerFunc {
res := map[string]interface{}{
"access_token": authToken.AccessToken.Token,
"id_token": authToken.IDToken.Token,
"scope": scope,
"scope": strings.Join(scope, " "),
"roles": roles,
"expires_in": expiresIn,
}
if authToken.RefreshToken != nil {
res["refresh_token"] = authToken.RefreshToken.Token
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token, authToken.RefreshToken.ExpiresAt)
}
gc.JSON(http.StatusOK, res)

View File

@@ -1,6 +1,7 @@
package handlers
import (
"encoding/json"
"net/http"
"github.com/gin-gonic/gin"
@@ -39,7 +40,27 @@ func UserInfoHandler() gin.HandlerFunc {
})
return
}
gc.JSON(http.StatusOK, user.AsAPIUser())
apiUser := user.AsAPIUser()
userBytes, err := json.Marshal(apiUser)
if err != nil {
log.Debug("Error marshalling user: ", err)
gc.JSON(http.StatusUnauthorized, gin.H{
"error": err.Error(),
})
return
}
res := map[string]interface{}{}
err = json.Unmarshal(userBytes, &res)
if err != nil {
log.Debug("Error un-marshalling user: ", err)
gc.JSON(http.StatusUnauthorized, gin.H{
"error": err.Error(),
})
return
}
// add sub field to user as per openid standards
// https://github.com/authorizerdev/authorizer/issues/327
res["sub"] = userID
gc.JSON(http.StatusOK, res)
}
}

View File

@@ -154,12 +154,12 @@ func VerifyEmailHandler() gin.HandlerFunc {
sessionKey := loginMethod + ":" + user.ID
cookie.SetSession(c, authToken.FingerPrintHash)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash, authToken.SessionTokenExpiresAt)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token, authToken.AccessToken.ExpiresAt)
if authToken.RefreshToken != nil {
params = params + `&refresh_token=` + authToken.RefreshToken.Token
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token, authToken.RefreshToken.ExpiresAt)
}
if redirectURL == "" {

View File

@@ -26,6 +26,7 @@ func InitMemStore() error {
// boolean envs
constants.EnvKeyDisableBasicAuthentication: false,
constants.EnvKeyDisableMobileBasicAuthentication: false,
constants.EnvKeyDisableMagicLinkLogin: false,
constants.EnvKeyDisableEmailVerification: false,
constants.EnvKeyDisableLoginPage: false,

View File

@@ -0,0 +1,14 @@
package inmemory
import (
"testing"
"github.com/authorizerdev/authorizer/server/memorystore/providers"
"github.com/stretchr/testify/assert"
)
func TestInMemoryProvider(t *testing.T) {
p, err := NewInMemoryProvider()
assert.NoError(t, err)
providers.ProviderTests(t, p)
}

View File

@@ -8,44 +8,31 @@ import (
)
// SetUserSession sets the user session for given user identifier in form recipe:user_id
func (c *provider) SetUserSession(userId, key, token string) error {
c.sessionStore.Set(userId, key, token)
func (c *provider) SetUserSession(userId, key, token string, expiration int64) error {
c.sessionStore.Set(userId, key, token, expiration)
return nil
}
// GetAllUserSessions returns all the user sessions token from the in-memory store.
func (c *provider) GetAllUserSessions(userId string) (map[string]string, error) {
data := c.sessionStore.GetAll(userId)
return data, nil
}
// GetUserSession returns value for given session token
func (c *provider) GetUserSession(userId, sessionToken string) (string, error) {
return c.sessionStore.Get(userId, sessionToken), nil
val := c.sessionStore.Get(userId, sessionToken)
if val == "" {
return "", fmt.Errorf("Not found")
}
return val, nil
}
// DeleteAllUserSessions deletes all the user sessions from in-memory store.
func (c *provider) DeleteAllUserSessions(userId string) error {
namespaces := []string{
constants.AuthRecipeMethodBasicAuth,
constants.AuthRecipeMethodMagicLinkLogin,
constants.AuthRecipeMethodApple,
constants.AuthRecipeMethodFacebook,
constants.AuthRecipeMethodGithub,
constants.AuthRecipeMethodGoogle,
constants.AuthRecipeMethodLinkedIn,
constants.AuthRecipeMethodTwitter,
}
for _, namespace := range namespaces {
c.sessionStore.RemoveAll(namespace + ":" + userId)
}
c.sessionStore.RemoveAll(userId)
return nil
}
// DeleteUserSession deletes the user session from the in-memory store.
func (c *provider) DeleteUserSession(userId, sessionToken string) error {
c.sessionStore.Remove(userId, sessionToken)
c.sessionStore.Remove(userId, constants.TokenTypeSessionToken+"_"+sessionToken)
c.sessionStore.Remove(userId, constants.TokenTypeAccessToken+"_"+sessionToken)
c.sessionStore.Remove(userId, constants.TokenTypeRefreshToken+"_"+sessionToken)
return nil
}

View File

@@ -31,11 +31,15 @@ func (e *EnvStore) UpdateStore(store map[string]interface{}) {
// GetStore returns the env store
func (e *EnvStore) GetStore() map[string]interface{} {
e.mutex.Lock()
defer e.mutex.Unlock()
return e.store
}
// Get returns the value of the key in evn store
func (e *EnvStore) Get(key string) interface{} {
e.mutex.Lock()
defer e.mutex.Unlock()
return e.store[key]
}

View File

@@ -1,73 +1,140 @@
package stores
import (
"fmt"
"sort"
"strings"
"sync"
"time"
)
const (
// Maximum entries to keep in session storage
maxCacheSize = 1000
// Cache clear interval
clearInterval = 10 * time.Minute
)
// SessionEntry is the struct for entry stored in store
type SessionEntry struct {
Value string
ExpiresAt int64
}
// SessionStore struct to store the env variables
type SessionStore struct {
wg sync.WaitGroup
mutex sync.Mutex
store map[string]map[string]string
store map[string]*SessionEntry
// stores expireTime: key to remove data when cache is full
// map is sorted by key so older most entry can be deleted first
keyIndex map[int64]string
stop chan struct{}
}
// NewSessionStore create a new session store
func NewSessionStore() *SessionStore {
return &SessionStore{
mutex: sync.Mutex{},
store: make(map[string]map[string]string),
store := &SessionStore{
mutex: sync.Mutex{},
store: make(map[string]*SessionEntry),
keyIndex: make(map[int64]string),
stop: make(chan struct{}),
}
store.wg.Add(1)
go func() {
defer store.wg.Done()
store.clean()
}()
return store
}
func (s *SessionStore) clean() {
t := time.NewTicker(clearInterval)
defer t.Stop()
for {
select {
case <-s.stop:
return
case <-t.C:
s.mutex.Lock()
currentTime := time.Now().Unix()
for k, v := range s.store {
if v.ExpiresAt < currentTime {
delete(s.store, k)
delete(s.keyIndex, v.ExpiresAt)
}
}
s.mutex.Unlock()
}
}
}
// Get returns the value of the key in state store
func (s *SessionStore) Get(key, subKey string) string {
return s.store[key][subKey]
s.mutex.Lock()
defer s.mutex.Unlock()
currentTime := time.Now().Unix()
k := fmt.Sprintf("%s:%s", key, subKey)
if v, ok := s.store[k]; ok {
if v.ExpiresAt > currentTime {
return v.Value
}
// Delete expired items
delete(s.store, k)
}
return ""
}
// Set sets the value of the key in state store
func (s *SessionStore) Set(key string, subKey, value string) {
func (s *SessionStore) Set(key string, subKey, value string, expiration int64) {
s.mutex.Lock()
defer s.mutex.Unlock()
if _, ok := s.store[key]; !ok {
s.store[key] = make(map[string]string)
k := fmt.Sprintf("%s:%s", key, subKey)
if _, ok := s.store[k]; !ok {
// check if there is enough space in cache
// else delete entries based on FIFO
if len(s.store) == maxCacheSize {
// remove older most entry
sortedKeys := []int64{}
for ik := range s.keyIndex {
sortedKeys = append(sortedKeys, ik)
}
sort.Slice(sortedKeys, func(i, j int) bool { return sortedKeys[i] < sortedKeys[j] })
itemToRemove := sortedKeys[0]
delete(s.store, s.keyIndex[itemToRemove])
delete(s.keyIndex, itemToRemove)
}
}
s.store[key][subKey] = value
s.store[k] = &SessionEntry{
Value: value,
ExpiresAt: expiration,
}
s.keyIndex[expiration] = k
}
// RemoveAll all values for given key
func (s *SessionStore) RemoveAll(key string) {
s.mutex.Lock()
defer s.mutex.Unlock()
delete(s.store, key)
for k := range s.store {
if strings.Contains(k, key) {
delete(s.store, k)
}
}
}
// Remove value for given key and subkey
func (s *SessionStore) Remove(key, subKey string) {
s.mutex.Lock()
defer s.mutex.Unlock()
if _, ok := s.store[key]; ok {
delete(s.store[key], subKey)
}
}
// Get all the values for given key
func (s *SessionStore) GetAll(key string) map[string]string {
s.mutex.Lock()
defer s.mutex.Unlock()
if _, ok := s.store[key]; !ok {
s.store[key] = make(map[string]string)
}
return s.store[key]
k := fmt.Sprintf("%s:%s", key, subKey)
delete(s.store, k)
}
// RemoveByNamespace to delete session for a given namespace example google,github
func (s *SessionStore) RemoveByNamespace(namespace string) error {
s.mutex.Lock()
defer s.mutex.Unlock()
for key := range s.store {
if strings.Contains(key, namespace+":") {
delete(s.store, key)

View File

@@ -20,6 +20,8 @@ func NewStateStore() *StateStore {
// Get returns the value of the key in state store
func (s *StateStore) Get(key string) string {
s.mutex.Lock()
defer s.mutex.Unlock()
return s.store[key]
}

View File

@@ -0,0 +1,115 @@
package providers
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
)
// ProviderTests runs all provider tests
func ProviderTests(t *testing.T, p Provider) {
err := p.SetUserSession("auth_provider:123", "session_token_key", "test_hash123", time.Now().Add(60*time.Second).Unix())
assert.NoError(t, err)
err = p.SetUserSession("auth_provider:123", "access_token_key", "test_jwt123", time.Now().Add(60*time.Second).Unix())
assert.NoError(t, err)
// Same user multiple session
err = p.SetUserSession("auth_provider:123", "session_token_key1", "test_hash1123", time.Now().Add(60*time.Second).Unix())
assert.NoError(t, err)
err = p.SetUserSession("auth_provider:123", "access_token_key1", "test_jwt1123", time.Now().Add(60*time.Second).Unix())
assert.NoError(t, err)
// Different user session
err = p.SetUserSession("auth_provider:124", "session_token_key", "test_hash124", time.Now().Add(5*time.Second).Unix())
assert.NoError(t, err)
err = p.SetUserSession("auth_provider:124", "access_token_key", "test_jwt124", time.Now().Add(5*time.Second).Unix())
assert.NoError(t, err)
// Different provider session
err = p.SetUserSession("auth_provider1:124", "session_token_key", "test_hash124", time.Now().Add(60*time.Second).Unix())
assert.NoError(t, err)
err = p.SetUserSession("auth_provider1:124", "access_token_key", "test_jwt124", time.Now().Add(60*time.Second).Unix())
assert.NoError(t, err)
// Different provider session
err = p.SetUserSession("auth_provider1:123", "session_token_key", "test_hash1123", time.Now().Add(60*time.Second).Unix())
assert.NoError(t, err)
err = p.SetUserSession("auth_provider1:123", "access_token_key", "test_jwt1123", time.Now().Add(60*time.Second).Unix())
assert.NoError(t, err)
// Get session
key, err := p.GetUserSession("auth_provider:123", "session_token_key")
assert.NoError(t, err)
assert.Equal(t, "test_hash123", key)
key, err = p.GetUserSession("auth_provider:123", "access_token_key")
assert.NoError(t, err)
assert.Equal(t, "test_jwt123", key)
key, err = p.GetUserSession("auth_provider:124", "session_token_key")
assert.NoError(t, err)
assert.Equal(t, "test_hash124", key)
key, err = p.GetUserSession("auth_provider:124", "access_token_key")
assert.NoError(t, err)
assert.Equal(t, "test_jwt124", key)
// Expire some tokens and make sure they are empty
time.Sleep(5 * time.Second)
key, err = p.GetUserSession("auth_provider:124", "session_token_key")
assert.Empty(t, key)
assert.Error(t, err)
key, err = p.GetUserSession("auth_provider:124", "access_token_key")
assert.Empty(t, key)
assert.Error(t, err)
// Delete user session
err = p.DeleteUserSession("auth_provider:123", "key")
assert.NoError(t, err)
err = p.DeleteUserSession("auth_provider:123", "key")
assert.NoError(t, err)
key, err = p.GetUserSession("auth_provider:123", "key")
assert.Empty(t, key)
assert.Error(t, err)
key, err = p.GetUserSession("auth_provider:123", "access_token_key")
assert.Empty(t, key)
assert.Error(t, err)
// Delete all user session
err = p.DeleteAllUserSessions("123")
assert.NoError(t, err)
err = p.DeleteAllUserSessions("123")
assert.NoError(t, err)
key, err = p.GetUserSession("auth_provider:123", "session_token_key1")
assert.Empty(t, key)
assert.Error(t, err)
key, err = p.GetUserSession("auth_provider:123", "access_token_key1")
assert.Empty(t, key)
assert.Error(t, err)
key, err = p.GetUserSession("auth_provider1:123", "session_token_key")
assert.Empty(t, key)
assert.Error(t, err)
key, err = p.GetUserSession("auth_provider1:123", "access_token_key")
assert.Empty(t, key)
assert.Error(t, err)
// Delete namespace
err = p.DeleteSessionForNamespace("auth_provider")
assert.NoError(t, err)
err = p.DeleteSessionForNamespace("auth_provider1")
assert.NoError(t, err)
key, err = p.GetUserSession("auth_provider:123", "session_token_key1")
assert.Empty(t, key)
assert.Error(t, err)
key, err = p.GetUserSession("auth_provider:123", "access_token_key1")
assert.Empty(t, key)
assert.Error(t, err)
key, err = p.GetUserSession("auth_provider1:123", "session_token_key")
assert.Empty(t, key)
assert.Error(t, err)
key, err = p.GetUserSession("auth_provider1:123", "access_token_key")
assert.Empty(t, key)
assert.Error(t, err)
key, err = p.GetUserSession("auth_provider:124", "session_token_key1")
assert.Empty(t, key)
assert.Error(t, err)
key, err = p.GetUserSession("auth_provider:124", "access_token_key1")
assert.Empty(t, key)
assert.Error(t, err)
key, err = p.GetUserSession("auth_provider1:124", "session_token_key")
assert.Empty(t, key)
assert.Error(t, err)
key, err = p.GetUserSession("auth_provider1:124", "access_token_key")
assert.Empty(t, key)
assert.Error(t, err)
}

View File

@@ -3,9 +3,7 @@ package providers
// Provider defines current memory store provider
type Provider interface {
// SetUserSession sets the user session for given user identifier in form recipe:user_id
SetUserSession(userId, key, token string) error
// GetAllUserSessions returns all the user sessions from the session store
GetAllUserSessions(userId string) (map[string]string, error)
SetUserSession(userId, key, token string, expiration int64) error
// GetUserSession returns the session token for given token
GetUserSession(userId, key string) (string, error)
// DeleteUserSession deletes the user session

View File

@@ -5,7 +5,7 @@ import (
"strings"
"time"
"github.com/go-redis/redis/v8"
"github.com/redis/go-redis/v9"
log "github.com/sirupsen/logrus"
)
@@ -17,10 +17,11 @@ type RedisClient interface {
HMGet(ctx context.Context, key string, fields ...string) *redis.SliceCmd
HSet(ctx context.Context, key string, values ...interface{}) *redis.IntCmd
HGet(ctx context.Context, key, field string) *redis.StringCmd
HGetAll(ctx context.Context, key string) *redis.StringStringMapCmd
HGetAll(ctx context.Context, key string) *redis.MapStringStringCmd
Set(ctx context.Context, key string, value interface{}, expiration time.Duration) *redis.StatusCmd
Get(ctx context.Context, key string) *redis.StringCmd
Scan(ctx context.Context, cursor uint64, match string, count int64) *redis.ScanCmd
Keys(ctx context.Context, pattern string) *redis.StringSliceCmd
}
type provider struct {
@@ -31,7 +32,6 @@ type provider struct {
// NewRedisProvider returns a new redis provider
func NewRedisProvider(redisURL string) (*provider, error) {
redisURLHostPortsList := strings.Split(redisURL, ",")
if len(redisURLHostPortsList) > 1 {
opt, err := redis.ParseURL(redisURLHostPortsList[0])
if err != nil {
@@ -70,7 +70,6 @@ func NewRedisProvider(redisURL string) (*provider, error) {
log.Debug("error connecting to redis: ", err)
return nil, err
}
return &provider{
ctx: ctx,
store: rdb,

View File

@@ -0,0 +1,15 @@
package redis
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/authorizerdev/authorizer/server/memorystore/providers"
)
func TestRedisProvider(t *testing.T) {
p, err := NewRedisProvider("redis://127.0.0.1:6379")
assert.NoError(t, err)
providers.ProviderTests(t, p)
}

View File

@@ -1,7 +1,9 @@
package redis
import (
"fmt"
"strconv"
"time"
"github.com/authorizerdev/authorizer/server/constants"
log "github.com/sirupsen/logrus"
@@ -15,29 +17,21 @@ var (
)
// SetUserSession sets the user session for given user identifier in form recipe:user_id
func (c *provider) SetUserSession(userId, key, token string) error {
err := c.store.HSet(c.ctx, userId, key, token).Err()
func (c *provider) SetUserSession(userId, key, token string, expiration int64) error {
currentTime := time.Now()
expireTime := time.Unix(expiration, 0)
duration := expireTime.Sub(currentTime)
err := c.store.Set(c.ctx, fmt.Sprintf("%s:%s", userId, key), token, duration).Err()
if err != nil {
log.Debug("Error saving to redis: ", err)
log.Debug("Error saving user session to redis: ", err)
return err
}
return nil
}
// GetAllUserSessions returns all the user session token from the redis store.
func (c *provider) GetAllUserSessions(userID string) (map[string]string, error) {
data, err := c.store.HGetAll(c.ctx, userID).Result()
if err != nil {
log.Debug("error getting all user sessions from redis store: ", err)
return nil, err
}
return data, nil
}
// GetUserSession returns the user session from redis store.
func (c *provider) GetUserSession(userId, key string) (string, error) {
data, err := c.store.HGet(c.ctx, userId, key).Result()
data, err := c.store.Get(c.ctx, fmt.Sprintf("%s:%s", userId, key)).Result()
if err != nil {
return "", err
}
@@ -46,38 +40,34 @@ func (c *provider) GetUserSession(userId, key string) (string, error) {
// DeleteUserSession deletes the user session from redis store.
func (c *provider) DeleteUserSession(userId, key string) error {
if err := c.store.HDel(c.ctx, userId, constants.TokenTypeSessionToken+"_"+key).Err(); err != nil {
if err := c.store.Del(c.ctx, fmt.Sprintf("%s:%s", userId, constants.TokenTypeSessionToken+"_"+key)).Err(); err != nil {
log.Debug("Error deleting user session from redis: ", err)
return err
// continue
}
if err := c.store.HDel(c.ctx, userId, constants.TokenTypeAccessToken+"_"+key).Err(); err != nil {
if err := c.store.Del(c.ctx, fmt.Sprintf("%s:%s", userId, constants.TokenTypeAccessToken+"_"+key)).Err(); err != nil {
log.Debug("Error deleting user session from redis: ", err)
return err
// continue
}
if err := c.store.HDel(c.ctx, userId, constants.TokenTypeRefreshToken+"_"+key).Err(); err != nil {
if err := c.store.Del(c.ctx, fmt.Sprintf("%s:%s", userId, constants.TokenTypeRefreshToken+"_"+key)).Err(); err != nil {
log.Debug("Error deleting user session from redis: ", err)
return err
// continue
}
return nil
}
// DeleteAllUserSessions deletes all the user session from redis
func (c *provider) DeleteAllUserSessions(userID string) error {
namespaces := []string{
constants.AuthRecipeMethodBasicAuth,
constants.AuthRecipeMethodMagicLinkLogin,
constants.AuthRecipeMethodApple,
constants.AuthRecipeMethodFacebook,
constants.AuthRecipeMethodGithub,
constants.AuthRecipeMethodGoogle,
constants.AuthRecipeMethodLinkedIn,
constants.AuthRecipeMethodTwitter,
res := c.store.Keys(c.ctx, fmt.Sprintf("*%s*", userID))
if res.Err() != nil {
log.Debug("Error getting all user sessions from redis: ", res.Err())
return res.Err()
}
for _, namespace := range namespaces {
err := c.store.Del(c.ctx, namespace+":"+userID).Err()
keys := res.Val()
for _, key := range keys {
err := c.store.Del(c.ctx, key).Err()
if err != nil {
log.Debug("Error deleting all user sessions from redis: ", err)
return err
continue
}
}
return nil
@@ -85,27 +75,19 @@ func (c *provider) DeleteAllUserSessions(userID string) error {
// DeleteSessionForNamespace to delete session for a given namespace example google,github
func (c *provider) DeleteSessionForNamespace(namespace string) error {
var cursor uint64
for {
keys := []string{}
keys, cursor, err := c.store.Scan(c.ctx, cursor, namespace+":*", 0).Result()
res := c.store.Keys(c.ctx, fmt.Sprintf("%s:*", namespace))
if res.Err() != nil {
log.Debug("Error getting all user sessions from redis: ", res.Err())
return res.Err()
}
keys := res.Val()
for _, key := range keys {
err := c.store.Del(c.ctx, key).Err()
if err != nil {
log.Debugf("Error scanning keys for %s namespace: %s", namespace, err.Error())
return err
}
for _, key := range keys {
err := c.store.Del(c.ctx, key).Err()
if err != nil {
log.Debugf("Error deleting sessions for %s namespace: %s", namespace, err.Error())
return err
}
}
if cursor == 0 { // no more keys
break
log.Debug("Error deleting all user sessions from redis: ", err)
continue
}
}
return nil
}
@@ -161,7 +143,7 @@ func (c *provider) GetEnvStore() (map[string]interface{}, error) {
return nil, err
}
for key, value := range data {
if key == constants.EnvKeyDisableBasicAuthentication || key == constants.EnvKeyDisableEmailVerification || key == constants.EnvKeyDisableLoginPage || key == constants.EnvKeyDisableMagicLinkLogin || key == constants.EnvKeyDisableRedisForEnv || key == constants.EnvKeyDisableSignUp || key == constants.EnvKeyDisableStrongPassword || key == constants.EnvKeyIsEmailServiceEnabled || key == constants.EnvKeyEnforceMultiFactorAuthentication || key == constants.EnvKeyDisableMultiFactorAuthentication || key == constants.EnvKeyAppCookieSecure || key == constants.EnvKeyAdminCookieSecure {
if key == constants.EnvKeyDisableBasicAuthentication || key == constants.EnvKeyDisableMobileBasicAuthentication || key == constants.EnvKeyDisableEmailVerification || key == constants.EnvKeyDisableLoginPage || key == constants.EnvKeyDisableMagicLinkLogin || key == constants.EnvKeyDisableRedisForEnv || key == constants.EnvKeyDisableSignUp || key == constants.EnvKeyDisableStrongPassword || key == constants.EnvKeyIsEmailServiceEnabled || key == constants.EnvKeyEnforceMultiFactorAuthentication || key == constants.EnvKeyDisableMultiFactorAuthentication || key == constants.EnvKeyAppCookieSecure || key == constants.EnvKeyAdminCookieSecure {
boolValue, err := strconv.ParseBool(value)
if err != nil {
return res, err

View File

@@ -32,6 +32,10 @@ type RequiredEnv struct {
AwsRegion string `json:"AWS_REGION"`
AwsAccessKeyID string `json:"AWS_ACCESS_KEY_ID"`
AwsSecretAccessKey string `json:"AWS_SECRET_ACCESS_KEY"`
// Couchbase related envs
CouchbaseBucket string `json:"COUCHBASE_BUCKET"`
CouchbaseScope string `json:"COUCHBASE_SCOPE"`
CouchbaseBucketRAMQuotaMB string `json:"COUCHBASE_BUCKET_RAM_QUOTA"`
}
// RequiredEnvObj is a simple in-memory store for sessions.
@@ -93,6 +97,9 @@ func InitRequiredEnv() error {
awsRegion := os.Getenv(constants.EnvAwsRegion)
awsAccessKeyID := os.Getenv(constants.EnvAwsAccessKeyID)
awsSecretAccessKey := os.Getenv(constants.EnvAwsSecretAccessKey)
couchbaseBucket := os.Getenv(constants.EnvCouchbaseBucket)
couchbaseScope := os.Getenv(constants.EnvCouchbaseScope)
couchbaseBucketRAMQuotaMB := os.Getenv(constants.EnvCouchbaseBucketRAMQuotaMB)
if strings.TrimSpace(redisURL) == "" {
if cli.ARG_REDIS_URL != nil && *cli.ARG_REDIS_URL != "" {
@@ -135,22 +142,25 @@ func InitRequiredEnv() error {
}
requiredEnv := RequiredEnv{
EnvPath: envPath,
DatabaseURL: dbURL,
DatabaseType: dbType,
DatabaseName: dbName,
DatabaseHost: dbHost,
DatabasePort: dbPort,
DatabaseUsername: dbUsername,
DatabasePassword: dbPassword,
DatabaseCert: dbCert,
DatabaseCertKey: dbCertKey,
DatabaseCACert: dbCACert,
RedisURL: redisURL,
DisableRedisForEnv: disableRedisForEnv,
AwsRegion: awsRegion,
AwsAccessKeyID: awsAccessKeyID,
AwsSecretAccessKey: awsSecretAccessKey,
EnvPath: envPath,
DatabaseURL: dbURL,
DatabaseType: dbType,
DatabaseName: dbName,
DatabaseHost: dbHost,
DatabasePort: dbPort,
DatabaseUsername: dbUsername,
DatabasePassword: dbPassword,
DatabaseCert: dbCert,
DatabaseCertKey: dbCertKey,
DatabaseCACert: dbCACert,
RedisURL: redisURL,
DisableRedisForEnv: disableRedisForEnv,
AwsRegion: awsRegion,
AwsAccessKeyID: awsAccessKeyID,
AwsSecretAccessKey: awsSecretAccessKey,
CouchbaseBucket: couchbaseBucket,
CouchbaseScope: couchbaseScope,
CouchbaseBucketRAMQuotaMB: couchbaseBucketRAMQuotaMB,
}
RequiredEnvStoreObj = &RequiredEnvStore{

View File

@@ -2,12 +2,14 @@ package oauth
import (
"context"
"fmt"
"github.com/coreos/go-oidc/v3/oidc"
"golang.org/x/oauth2"
facebookOAuth2 "golang.org/x/oauth2/facebook"
githubOAuth2 "golang.org/x/oauth2/github"
linkedInOAuth2 "golang.org/x/oauth2/linkedin"
microsoftOAuth2 "golang.org/x/oauth2/microsoft"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/memorystore"
@@ -15,17 +17,19 @@ import (
// OAuthProviders is a struct that contains reference all the OAuth providers
type OAuthProvider struct {
GoogleConfig *oauth2.Config
GithubConfig *oauth2.Config
FacebookConfig *oauth2.Config
LinkedInConfig *oauth2.Config
AppleConfig *oauth2.Config
TwitterConfig *oauth2.Config
GoogleConfig *oauth2.Config
GithubConfig *oauth2.Config
FacebookConfig *oauth2.Config
LinkedInConfig *oauth2.Config
AppleConfig *oauth2.Config
TwitterConfig *oauth2.Config
MicrosoftConfig *oauth2.Config
}
// OIDCProviders is a struct that contains reference all the OpenID providers
type OIDCProvider struct {
GoogleOIDC *oidc.Provider
GoogleOIDC *oidc.Provider
MicrosoftOIDC *oidc.Provider
}
var (
@@ -158,5 +162,32 @@ func InitOAuth() error {
}
}
microsoftClientID, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyMicrosoftClientID)
if err != nil {
microsoftClientID = ""
}
microsoftClientSecret, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyMicrosoftClientSecret)
if err != nil {
microsoftClientSecret = ""
}
microsoftActiveDirTenantID, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyMicrosoftActiveDirectoryTenantID)
if err != nil {
microsoftActiveDirTenantID = ""
}
if microsoftClientID != "" && microsoftClientSecret != "" && microsoftActiveDirTenantID != "" {
p, err := oidc.NewProvider(ctx, fmt.Sprintf("https://login.microsoftonline.com/%s/v2.0", microsoftActiveDirTenantID))
if err != nil {
return err
}
OIDCProviders.MicrosoftOIDC = p
OAuthProviders.MicrosoftConfig = &oauth2.Config{
ClientID: microsoftClientID,
ClientSecret: microsoftClientSecret,
RedirectURL: "/oauth_callback/microsoft",
Endpoint: microsoftOAuth2.AzureADEndpoint(microsoftActiveDirTenantID),
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
}
}
return nil
}

View File

@@ -9,6 +9,7 @@ import (
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/refs"
"github.com/authorizerdev/authorizer/server/token"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/authorizerdev/authorizer/server/validators"
@@ -22,32 +23,32 @@ func AddWebhookResolver(ctx context.Context, params model.AddWebhookRequest) (*m
log.Debug("Failed to get GinContext: ", err)
return nil, err
}
if !token.IsSuperAdmin(gc) {
log.Debug("Not logged in as super admin")
return nil, fmt.Errorf("unauthorized")
}
if !validators.IsValidWebhookEventName(params.EventName) {
log.Debug("Invalid Event Name: ", params.EventName)
return nil, fmt.Errorf("invalid event name %s", params.EventName)
}
if strings.TrimSpace(params.Endpoint) == "" {
log.Debug("empty endpoint not allowed")
return nil, fmt.Errorf("empty endpoint not allowed")
}
headerBytes, err := json.Marshal(params.Headers)
if err != nil {
return nil, err
}
if params.EventDescription == nil {
params.EventDescription = refs.NewStringRef(strings.Join(strings.Split(params.EventName, "."), " "))
}
_, err = db.Provider.AddWebhook(ctx, models.Webhook{
EventName: params.EventName,
EndPoint: params.Endpoint,
Enabled: params.Enabled,
Headers: string(headerBytes),
EventDescription: refs.StringValue(params.EventDescription),
EventName: params.EventName,
EndPoint: params.Endpoint,
Enabled: params.Enabled,
Headers: string(headerBytes),
})
if err != nil {
log.Debug("Failed to add webhook: ", err)

View File

@@ -152,6 +152,15 @@ func EnvResolver(ctx context.Context) (*model.Env, error) {
if val, ok := store[constants.EnvKeyTwitterClientSecret]; ok {
res.TwitterClientSecret = refs.NewStringRef(val.(string))
}
if val, ok := store[constants.EnvKeyMicrosoftClientID]; ok {
res.MicrosoftClientID = refs.NewStringRef(val.(string))
}
if val, ok := store[constants.EnvKeyMicrosoftClientSecret]; ok {
res.MicrosoftClientSecret = refs.NewStringRef(val.(string))
}
if val, ok := store[constants.EnvKeyMicrosoftActiveDirectoryTenantID]; ok {
res.MicrosoftActiveDirectoryTenantID = refs.NewStringRef(val.(string))
}
if val, ok := store[constants.EnvKeyOrganizationName]; ok {
res.OrganizationName = refs.NewStringRef(val.(string))
@@ -159,6 +168,12 @@ func EnvResolver(ctx context.Context) (*model.Env, error) {
if val, ok := store[constants.EnvKeyOrganizationLogo]; ok {
res.OrganizationLogo = refs.NewStringRef(val.(string))
}
if val, ok := store[constants.EnvKeyDefaultAuthorizeResponseType]; ok {
res.DefaultAuthorizeResponseType = refs.NewStringRef(val.(string))
}
if val, ok := store[constants.EnvKeyDefaultAuthorizeResponseMode]; ok {
res.DefaultAuthorizeResponseMode = refs.NewStringRef(val.(string))
}
// string slice vars
res.AllowedOrigins = strings.Split(store[constants.EnvKeyAllowedOrigins].(string), ",")

View File

@@ -48,7 +48,15 @@ func InviteMembersResolver(ctx context.Context, params model.InviteMemberInput)
}
isBasicAuthDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableBasicAuthentication)
if err != nil {
log.Debug("Failed to get is basic auth disabled")
return nil, err
}
isMagicLinkLoginDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableMagicLinkLogin)
if err != nil {
log.Debug("Failed to get is magic link login disabled")
return nil, err
}
if isBasicAuthDisabled && isMagicLinkLoginDisabled {
log.Debug("Basic authentication and Magic link login is disabled.")
return nil, errors.New("either basic authentication or magic link login is required")

View File

@@ -193,12 +193,12 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes
cookie.SetSession(gc, authToken.FingerPrintHash)
sessionStoreKey := constants.AuthRecipeMethodBasicAuth + ":" + user.ID
memorystore.Provider.SetUserSession(sessionStoreKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
memorystore.Provider.SetUserSession(sessionStoreKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
memorystore.Provider.SetUserSession(sessionStoreKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash, authToken.SessionTokenExpiresAt)
memorystore.Provider.SetUserSession(sessionStoreKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token, authToken.AccessToken.ExpiresAt)
if authToken.RefreshToken != nil {
res.RefreshToken = &authToken.RefreshToken.Token
memorystore.Provider.SetUserSession(sessionStoreKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
memorystore.Provider.SetUserSession(sessionStoreKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token, authToken.RefreshToken.ExpiresAt)
}
go func() {

View File

@@ -89,6 +89,24 @@ func MetaResolver(ctx context.Context) (*model.Meta, error) {
twitterClientSecret = ""
}
microsoftClientID, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyMicrosoftClientID)
if err != nil {
log.Debug("Failed to get Microsoft Client ID from environment variable", err)
microsoftClientID = ""
}
microsoftClientSecret, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyMicrosoftClientSecret)
if err != nil {
log.Debug("Failed to get Microsoft Client Secret from environment variable", err)
microsoftClientSecret = ""
}
microsoftActiveDirTenantID, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyMicrosoftActiveDirectoryTenantID)
if err != nil {
log.Debug("Failed to get Microsoft Active Directory Tenant ID from environment variable", err)
microsoftActiveDirTenantID = ""
}
isBasicAuthDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableBasicAuthentication)
if err != nil {
log.Debug("Failed to get Disable Basic Authentication from environment variable", err)
@@ -134,6 +152,7 @@ func MetaResolver(ctx context.Context) (*model.Meta, error) {
IsLinkedinLoginEnabled: linkedClientID != "" && linkedInClientSecret != "",
IsAppleLoginEnabled: appleClientID != "" && appleClientSecret != "",
IsTwitterLoginEnabled: twitterClientID != "" && twitterClientSecret != "",
IsMicrosoftLoginEnabled: microsoftClientID != "" && microsoftClientSecret != "" && microsoftActiveDirTenantID != "",
IsBasicAuthenticationEnabled: !isBasicAuthDisabled,
IsEmailVerificationEnabled: !isEmailVerificationDisabled,
IsMagicLinkLoginEnabled: !isMagicLinkLoginDisabled,

View File

@@ -0,0 +1,216 @@
package resolvers
import (
"context"
"fmt"
"strings"
"time"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
"golang.org/x/crypto/bcrypt"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/cookie"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/memorystore"
"github.com/authorizerdev/authorizer/server/refs"
"github.com/authorizerdev/authorizer/server/token"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/authorizerdev/authorizer/server/validators"
)
// MobileLoginResolver is a resolver for mobile login mutation
func MobileLoginResolver(ctx context.Context, params model.MobileLoginInput) (*model.AuthResponse, error) {
var res *model.AuthResponse
gc, err := utils.GinContextFromContext(ctx)
if err != nil {
log.Debug("Failed to get GinContext: ", err)
return res, err
}
isBasiAuthDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableMobileBasicAuthentication)
if err != nil {
log.Debug("Error getting mobile basic auth disabled: ", err)
isBasiAuthDisabled = true
}
if isBasiAuthDisabled {
log.Debug("Basic authentication is disabled.")
return res, fmt.Errorf(`phone number based basic authentication is disabled for this instance`)
}
log := log.WithFields(log.Fields{
"phone_number": params.PhoneNumber,
})
user, err := db.Provider.GetUserByPhoneNumber(ctx, params.PhoneNumber)
if err != nil {
log.Debug("Failed to get user by phone number: ", err)
return res, fmt.Errorf(`bad user credentials`)
}
if user.RevokedTimestamp != nil {
log.Debug("User access is revoked")
return res, fmt.Errorf(`user access has been revoked`)
}
if !strings.Contains(user.SignupMethods, constants.AuthRecipeMethodMobileBasicAuth) {
log.Debug("User signup method is not mobile basic auth")
return res, fmt.Errorf(`user has not signed up with phone number & password`)
}
if user.PhoneNumberVerifiedAt == nil {
log.Debug("User phone number is not verified")
return res, fmt.Errorf(`phone number is not verified`)
}
err = bcrypt.CompareHashAndPassword([]byte(*user.Password), []byte(params.Password))
if err != nil {
log.Debug("Failed to compare password: ", err)
return res, fmt.Errorf(`bad user credentials`)
}
defaultRolesString, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyDefaultRoles)
roles := []string{}
if err != nil {
log.Debug("Error getting default roles: ", err)
defaultRolesString = ""
} else {
roles = strings.Split(defaultRolesString, ",")
}
currentRoles := strings.Split(user.Roles, ",")
if len(params.Roles) > 0 {
if !validators.IsValidRoles(params.Roles, currentRoles) {
log.Debug("Invalid roles: ", params.Roles)
return res, fmt.Errorf(`invalid roles`)
}
roles = params.Roles
}
scope := []string{"openid", "email", "profile"}
if params.Scope != nil && len(scope) > 0 {
scope = params.Scope
}
/*
// TODO use sms authentication for MFA
isEmailServiceEnabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyIsEmailServiceEnabled)
if err != nil || !isEmailServiceEnabled {
log.Debug("Email service not enabled: ", err)
}
isMFADisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableMultiFactorAuthentication)
if err != nil || !isEmailServiceEnabled {
log.Debug("MFA service not enabled: ", err)
}
// If email service is not enabled continue the process in any way
if refs.BoolValue(user.IsMultiFactorAuthEnabled) && isEmailServiceEnabled && !isMFADisabled {
otp := utils.GenerateOTP()
otpData, err := db.Provider.UpsertOTP(ctx, &models.OTP{
Email: user.Email,
Otp: otp,
ExpiresAt: time.Now().Add(1 * time.Minute).Unix(),
})
if err != nil {
log.Debug("Failed to add otp: ", err)
return nil, err
}
go func() {
// exec it as go routine so that we can reduce the api latency
go email.SendEmail([]string{params.PhoneNumber}, constants.VerificationTypeOTP, map[string]interface{}{
"user": user.ToMap(),
"organization": utils.GetOrganization(),
"otp": otpData.Otp,
})
if err != nil {
log.Debug("Failed to send otp email: ", err)
}
}()
return &model.AuthResponse{
Message: "Please check the OTP in your inbox",
ShouldShowOtpScreen: refs.NewBoolRef(true),
}, nil
}
*/
code := ""
codeChallenge := ""
nonce := ""
if params.State != nil {
// Get state from store
authorizeState, _ := memorystore.Provider.GetState(refs.StringValue(params.State))
if authorizeState != "" {
authorizeStateSplit := strings.Split(authorizeState, "@@")
if len(authorizeStateSplit) > 1 {
code = authorizeStateSplit[0]
codeChallenge = authorizeStateSplit[1]
} else {
nonce = authorizeState
}
go memorystore.Provider.RemoveState(refs.StringValue(params.State))
}
}
if nonce == "" {
nonce = uuid.New().String()
}
authToken, err := token.CreateAuthToken(gc, *user, roles, scope, constants.AuthRecipeMethodMobileBasicAuth, nonce, code)
if err != nil {
log.Debug("Failed to create auth token", err)
return res, err
}
// TODO add to other login options as well
// Code challenge could be optional if PKCE flow is not used
if code != "" {
if err := memorystore.Provider.SetState(code, codeChallenge+"@@"+authToken.FingerPrintHash); err != nil {
log.Debug("SetState failed: ", err)
return res, err
}
}
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
if expiresIn <= 0 {
expiresIn = 1
}
res = &model.AuthResponse{
Message: `Logged in successfully`,
AccessToken: &authToken.AccessToken.Token,
IDToken: &authToken.IDToken.Token,
ExpiresIn: &expiresIn,
User: user.AsAPIUser(),
}
cookie.SetSession(gc, authToken.FingerPrintHash)
sessionStoreKey := constants.AuthRecipeMethodMobileBasicAuth + ":" + user.ID
memorystore.Provider.SetUserSession(sessionStoreKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash, authToken.SessionTokenExpiresAt)
memorystore.Provider.SetUserSession(sessionStoreKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token, authToken.AccessToken.ExpiresAt)
if authToken.RefreshToken != nil {
res.RefreshToken = &authToken.RefreshToken.Token
memorystore.Provider.SetUserSession(sessionStoreKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token, authToken.RefreshToken.ExpiresAt)
}
go func() {
utils.RegisterEvent(ctx, constants.UserLoginWebhookEvent, constants.AuthRecipeMethodMobileBasicAuth, *user)
db.Provider.AddSession(ctx, models.Session{
UserID: user.ID,
UserAgent: utils.GetUserAgent(gc.Request),
IP: utils.GetIP(gc.Request),
})
}()
return res, nil
}

View File

@@ -0,0 +1,270 @@
package resolvers
import (
"context"
"fmt"
"strings"
"time"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/cookie"
"github.com/authorizerdev/authorizer/server/crypto"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/memorystore"
"github.com/authorizerdev/authorizer/server/refs"
"github.com/authorizerdev/authorizer/server/token"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/authorizerdev/authorizer/server/validators"
)
// MobileSignupResolver is a resolver for mobile_basic_auth_signup mutation
func MobileSignupResolver(ctx context.Context, params *model.MobileSignUpInput) (*model.AuthResponse, error) {
var res *model.AuthResponse
gc, err := utils.GinContextFromContext(ctx)
if err != nil {
log.Debug("Failed to get GinContext: ", err)
return res, err
}
isSignupDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableSignUp)
if err != nil {
log.Debug("Error getting signup disabled: ", err)
isSignupDisabled = true
}
if isSignupDisabled {
log.Debug("Signup is disabled")
return res, fmt.Errorf(`signup is disabled for this instance`)
}
isBasicAuthDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableMobileBasicAuthentication)
if err != nil {
log.Debug("Error getting basic auth disabled: ", err)
isBasicAuthDisabled = true
}
if isBasicAuthDisabled {
log.Debug("Mobile based Basic authentication is disabled")
return res, fmt.Errorf(`phone number based basic authentication is disabled for this instance`)
}
if params.ConfirmPassword != params.Password {
log.Debug("Passwords do not match")
return res, fmt.Errorf(`password and confirm password does not match`)
}
if err := validators.IsValidPassword(params.Password); err != nil {
log.Debug("Invalid password")
return res, err
}
mobile := strings.TrimSpace(params.PhoneNumber)
if mobile == "" || len(mobile) < 10 {
log.Debug("Invalid phone number")
return res, fmt.Errorf("invalid phone number")
}
emailInput := strings.ToLower(strings.TrimSpace(refs.StringValue(params.Email)))
// if email is null set random dummy email for db constraint
if emailInput != "" && !validators.IsValidEmail(emailInput) {
log.Debug("Invalid email: ", emailInput)
return res, fmt.Errorf(`invalid email address`)
}
if emailInput == "" {
emailInput = mobile + "@authorizer.dev"
}
log := log.WithFields(log.Fields{
"email": emailInput,
"phone_number": mobile,
})
// find user with email
existingUser, err := db.Provider.GetUserByPhoneNumber(ctx, mobile)
if err != nil {
log.Debug("Failed to get user by email: ", err)
}
if existingUser != nil {
if existingUser.PhoneNumberVerifiedAt != nil {
// email is verified
log.Debug("Phone number is already verified and signed up.")
return res, fmt.Errorf(`%s has already signed up`, mobile)
} else if existingUser.ID != "" && existingUser.PhoneNumberVerifiedAt == nil {
log.Debug("Phone number is already signed up. Verification pending...")
return res, fmt.Errorf("%s has already signed up. please complete the phone number verification process or reset the password", mobile)
}
}
inputRoles := []string{}
if len(params.Roles) > 0 {
// check if roles exists
rolesString, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyRoles)
roles := []string{}
if err != nil {
log.Debug("Error getting roles: ", err)
return res, err
} else {
roles = strings.Split(rolesString, ",")
}
if !validators.IsValidRoles(params.Roles, roles) {
log.Debug("Invalid roles: ", params.Roles)
return res, fmt.Errorf(`invalid roles`)
} else {
inputRoles = params.Roles
}
} else {
inputRolesString, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyDefaultRoles)
if err != nil {
log.Debug("Error getting default roles: ", err)
return res, err
} else {
inputRoles = strings.Split(inputRolesString, ",")
}
}
now := time.Now().Unix()
user := models.User{
Email: emailInput,
PhoneNumber: &mobile,
PhoneNumberVerifiedAt: &now,
}
user.Roles = strings.Join(inputRoles, ",")
password, _ := crypto.EncryptPassword(params.Password)
user.Password = &password
if params.GivenName != nil {
user.GivenName = params.GivenName
}
if params.FamilyName != nil {
user.FamilyName = params.FamilyName
}
if params.MiddleName != nil {
user.MiddleName = params.MiddleName
}
if params.Nickname != nil {
user.Nickname = params.Nickname
}
if params.Gender != nil {
user.Gender = params.Gender
}
if params.Birthdate != nil {
user.Birthdate = params.Birthdate
}
if params.Picture != nil {
user.Picture = params.Picture
}
if params.IsMultiFactorAuthEnabled != nil {
user.IsMultiFactorAuthEnabled = params.IsMultiFactorAuthEnabled
}
isMFAEnforced, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyEnforceMultiFactorAuthentication)
if err != nil {
log.Debug("MFA service not enabled: ", err)
isMFAEnforced = false
}
if isMFAEnforced {
user.IsMultiFactorAuthEnabled = refs.NewBoolRef(true)
}
user.SignupMethods = constants.AuthRecipeMethodMobileBasicAuth
user, err = db.Provider.AddUser(ctx, user)
if err != nil {
log.Debug("Failed to add user: ", err)
return res, err
}
roles := strings.Split(user.Roles, ",")
userToReturn := user.AsAPIUser()
scope := []string{"openid", "email", "profile"}
if params.Scope != nil && len(scope) > 0 {
scope = params.Scope
}
code := ""
codeChallenge := ""
nonce := ""
if params.State != nil {
// Get state from store
authorizeState, _ := memorystore.Provider.GetState(refs.StringValue(params.State))
if authorizeState != "" {
authorizeStateSplit := strings.Split(authorizeState, "@@")
if len(authorizeStateSplit) > 1 {
code = authorizeStateSplit[0]
codeChallenge = authorizeStateSplit[1]
} else {
nonce = authorizeState
}
go memorystore.Provider.RemoveState(refs.StringValue(params.State))
}
}
if nonce == "" {
nonce = uuid.New().String()
}
authToken, err := token.CreateAuthToken(gc, user, roles, scope, constants.AuthRecipeMethodMobileBasicAuth, nonce, code)
if err != nil {
log.Debug("Failed to create auth token: ", err)
return res, err
}
// Code challenge could be optional if PKCE flow is not used
if code != "" {
if err := memorystore.Provider.SetState(code, codeChallenge+"@@"+authToken.FingerPrintHash); err != nil {
log.Debug("SetState failed: ", err)
return res, err
}
}
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
if expiresIn <= 0 {
expiresIn = 1
}
res = &model.AuthResponse{
Message: `Signed up successfully.`,
AccessToken: &authToken.AccessToken.Token,
ExpiresIn: &expiresIn,
User: userToReturn,
}
sessionKey := constants.AuthRecipeMethodMobileBasicAuth + ":" + user.ID
cookie.SetSession(gc, authToken.FingerPrintHash)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash, authToken.SessionTokenExpiresAt)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token, authToken.AccessToken.ExpiresAt)
if authToken.RefreshToken != nil {
res.RefreshToken = &authToken.RefreshToken.Token
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token, authToken.RefreshToken.ExpiresAt)
}
go func() {
utils.RegisterEvent(ctx, constants.UserSignUpWebhookEvent, constants.AuthRecipeMethodMobileBasicAuth, user)
db.Provider.AddSession(ctx, models.Session{
UserID: user.ID,
UserAgent: utils.GetUserAgent(gc.Request),
IP: utils.GetIP(gc.Request),
})
}()
return res, nil
}

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