Compare commits
1 Commits
feat/invit
...
fauna-db
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e170569959 |
15
.env.sample
@@ -1,3 +1,16 @@
|
|||||||
|
ENV=production
|
||||||
DATABASE_URL=data.db
|
DATABASE_URL=data.db
|
||||||
DATABASE_TYPE=sqlite
|
DATABASE_TYPE=sqlite
|
||||||
CUSTOM_ACCESS_TOKEN_SCRIPT="function(user,tokenPayload){var data = tokenPayload;data.extra = {'x-extra-id': user.id};return data;}"
|
ADMIN_SECRET=admin
|
||||||
|
JWT_SECRET=random_string
|
||||||
|
SENDER_EMAIL=info@authorizer.dev
|
||||||
|
SMTP_USERNAME=username
|
||||||
|
SMTP_PASSWORD=password
|
||||||
|
SMTP_HOST=smtp.mailtrap.io
|
||||||
|
SMTP_PORT=2525
|
||||||
|
JWT_TYPE=HS256
|
||||||
|
ROLES=user
|
||||||
|
DEFAULT_ROLES=user
|
||||||
|
PROTECTED_ROLES=admin
|
||||||
|
JWT_ROLE_CLAIM=role
|
||||||
|
CUSTOM_ACCESS_TOKEN_SCRIPT=function(user,tokenPayload){var data = tokenPayload;data.extra = {'x-extra-id': user.id};return data;}
|
12
.github/CONTRIBUTING.md
vendored
@@ -43,14 +43,12 @@ Please ask as many questions as you need, either directly in the issue or on [Di
|
|||||||
### Project Setup for Authorizer core
|
### Project Setup for Authorizer core
|
||||||
|
|
||||||
1. Fork the [authorizer](https://github.com/authorizerdev/authorizer) repository (**Skip this step if you have access to repo**)
|
1. Fork the [authorizer](https://github.com/authorizerdev/authorizer) repository (**Skip this step if you have access to repo**)
|
||||||
2. Clone repo: `git clone https://github.com/authorizerdev/authorizer.git` or use the forked url from step 1
|
2. `git clone https://github.com/authorizerdev/authorizer.git`
|
||||||
3. Change directory to authorizer: `cd authorizer`
|
3. `cd authorizer`
|
||||||
5. Create Env file `cp .env.sample .env`. Check all the supported env [here](https://docs.authorizer.dev/core/env/)
|
4. `cp .env.sample .env`. Check all the supported env [here](https://docs.authorizer.dev/core/env/)
|
||||||
6. Build Dashboard `make build-dashboard`
|
5. Build the code `make clean && make`
|
||||||
7. Build App `make build-app`
|
|
||||||
8. Build Server `make clean && make`
|
|
||||||
> Note: if you don't have [`make`](https://www.ibm.com/docs/en/aix/7.2?topic=concepts-make-command), you can `cd` into `server` dir and build using the `go build` command
|
> Note: if you don't have [`make`](https://www.ibm.com/docs/en/aix/7.2?topic=concepts-make-command), you can `cd` into `server` dir and build using the `go build` command
|
||||||
9. Run binary `./build/server`
|
6. Run binary `./build/server`
|
||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
|
|
||||||
|
3
.gitignore
vendored
@@ -10,5 +10,4 @@ build
|
|||||||
data.db
|
data.db
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.env.local
|
.env.local
|
||||||
*.tar.gz
|
*.tar.gz
|
||||||
.vscode/
|
|
@@ -24,9 +24,7 @@ FROM alpine:latest
|
|||||||
WORKDIR /root/
|
WORKDIR /root/
|
||||||
RUN mkdir app dashboard
|
RUN mkdir app dashboard
|
||||||
COPY --from=node-builder /authorizer/app/build app/build
|
COPY --from=node-builder /authorizer/app/build app/build
|
||||||
COPY --from=node-builder /authorizer/app/favicon_io app/favicon_io
|
|
||||||
COPY --from=node-builder /authorizer/dashboard/build dashboard/build
|
COPY --from=node-builder /authorizer/dashboard/build dashboard/build
|
||||||
COPY --from=node-builder /authorizer/dashboard/favicon_io dashboard/favicon_io
|
|
||||||
COPY --from=go-builder /authorizer/build build
|
COPY --from=go-builder /authorizer/build build
|
||||||
COPY templates templates
|
COPY templates templates
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
|
3
Makefile
@@ -11,6 +11,3 @@ clean:
|
|||||||
rm -rf build
|
rm -rf build
|
||||||
test:
|
test:
|
||||||
cd server && go clean --testcache && go test -v ./test
|
cd server && go clean --testcache && go test -v ./test
|
||||||
generate:
|
|
||||||
cd server && go get github.com/99designs/gqlgen/cmd@v0.14.0 && go run github.com/99designs/gqlgen generate
|
|
||||||
|
|
33
README.md
@@ -1,13 +1,13 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://authorizer.dev">
|
<a href="https://authorizer.dev">
|
||||||
<img alt="Logo" src="https://authorizer.dev/images/logo.png" width="60" />
|
<img alt="Logo" src="https://github.com/authorizerdev/authorizer/blob/main/assets/logo.png" width="60" />
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
<h1 align="center">
|
<h1 align="center">
|
||||||
Authorizer
|
Authorizer
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
**Authorizer** is an open-source authentication and authorization solution for your applications. Bring your database and have complete control over the user information. You can self-host authorizer instances and connect to any database (Currently supports [Postgres](https://www.postgresql.org/), [MySQL](https://www.mysql.com/), [SQLite](https://www.sqlite.org/index.html), [SQLServer](https://www.microsoft.com/en-us/sql-server/), [MongoDB](https://mongodb.com/), [ArangoDB](https://www.arangodb.com/)).
|
**Authorizer** is an open-source authentication and authorization solution for your applications. Bring your database and have complete control over the user information. You can self-host authorizer instances and connect to any database (Currently supports [Postgres](https://www.postgresql.org/), [MySQL](https://www.mysql.com/), [SQLite](https://www.sqlite.org/index.html), [SQLServer](https://www.microsoft.com/en-us/sql-server/), [MongoDB](https://mongodb.com/),[ArangoDB](https://www.arangodb.com/)).
|
||||||
|
|
||||||
## Table of contents
|
## Table of contents
|
||||||
|
|
||||||
@@ -78,14 +78,12 @@ This guide helps you practice using Authorizer to evaluate it before you use it
|
|||||||
### Project Setup
|
### Project Setup
|
||||||
|
|
||||||
1. Fork the [authorizer](https://github.com/authorizerdev/authorizer) repository (**Skip this step if you have access to repo**)
|
1. Fork the [authorizer](https://github.com/authorizerdev/authorizer) repository (**Skip this step if you have access to repo**)
|
||||||
2. Clone repo: `git clone https://github.com/authorizerdev/authorizer.git` or use the forked url from step 1
|
2. `git clone https://github.com/authorizerdev/authorizer.git`
|
||||||
3. Change directory to authorizer: `cd authorizer`
|
3. `cd authorizer`
|
||||||
5. Create Env file `cp .env.sample .env`. Check all the supported env [here](https://docs.authorizer.dev/core/env/)
|
4. `cp .env.sample .env`. Check all the supported env [here](https://docs.authorizer.dev/core/env/)
|
||||||
6. Build Dashboard `make build-dashboard`
|
5. Build the code `make clean && make`
|
||||||
7. Build App `make build-app`
|
|
||||||
8. Build Server `make clean && make`
|
|
||||||
> Note: if you don't have [`make`](https://www.ibm.com/docs/en/aix/7.2?topic=concepts-make-command), you can `cd` into `server` dir and build using the `go build` command
|
> Note: if you don't have [`make`](https://www.ibm.com/docs/en/aix/7.2?topic=concepts-make-command), you can `cd` into `server` dir and build using the `go build` command
|
||||||
9. Run binary `./build/server`
|
6. Run binary `./build/server`
|
||||||
|
|
||||||
## Install using binaries
|
## Install using binaries
|
||||||
|
|
||||||
@@ -131,13 +129,18 @@ Required environment variables are pre-configured in `.env` file. But based on t
|
|||||||
|
|
||||||
> Note: For mac users, you might have to give binary the permission to execute. Here is the command you can use to grant permission `xattr -d com.apple.quarantine build/server`
|
> Note: For mac users, you might have to give binary the permission to execute. Here is the command you can use to grant permission `xattr -d com.apple.quarantine build/server`
|
||||||
|
|
||||||
Deploy production ready Authorizer instance using one click deployment options available below
|
## Install instance on Heroku
|
||||||
|
|
||||||
| **Infra provider** | **One-click link** | **Additional information** |
|
Deploy Authorizer using [heroku](https://github.com/authorizerdev/authorizer-heroku) and quickly play with it in 30seconds
|
||||||
| :----------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------: |
|
<br/><br/>
|
||||||
| Railway.app | <a href="https://railway.app/new/template?template=https://github.com/authorizerdev/authorizer-railway&plugins=postgresql,redis"><img src="https://railway.app/button.svg" style="height: 44px" alt="Deploy on Railway"></a> | [docs](https://docs.authorizer.dev/deployment/railway) |
|
[](https://heroku.com/deploy?template=https://github.com/authorizerdev/authorizer-heroku)
|
||||||
| Heroku | <a href="https://heroku.com/deploy?template=https://github.com/authorizerdev/authorizer-heroku"><img src="https://www.herokucdn.com/deploy/button.svg" alt="Deploy to Heroku" style="height: 44px;"></a> | [docs](https://docs.authorizer.dev/deployment/heroku) |
|
|
||||||
| Render | [](https://render.com/deploy?repo=https://github.com/authorizerdev/authorizer-render) | [docs](https://docs.authorizer.dev/deployment/render) |
|
# Install instance on railway
|
||||||
|
|
||||||
|
Deploy production ready Authorizer instance using [railway.app](https://github.com/authorizerdev/authorizer-railway) with postgres and redis for free and build with it in 30seconds
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
[](https://railway.app/new/template?template=https%3A%2F%2Fgithub.com%2Fauthorizerdev%2Fauthorizer-railway&plugins=postgresql%2Credis&envs=ENV%2CDATABASE_TYPE%2CADMIN_SECRET%2CCOOKIE_NAME%2CJWT_ROLE_CLAIM%2CJWT_TYPE%2CJWT_SECRET%2CFACEBOOK_CLIENT_ID%2CFACEBOOK_CLIENT_SECRET%2CGOOGLE_CLIENT_ID%2CGOOGLE_CLIENT_SECRET%2CGITHUB_CLIENT_ID%2CGITHUB_CLIENT_SECRET%2CALLOWED_ORIGINS%2CROLES%2CPROTECTED_ROLES%2CDEFAULT_ROLES&optionalEnvs=FACEBOOK_CLIENT_ID%2CFACEBOOK_CLIENT_SECRET%2CGOOGLE_CLIENT_ID%2CGOOGLE_CLIENT_SECRET%2CGITHUB_CLIENT_ID%2CGITHUB_CLIENT_SECRET%2CALLOWED_ORIGINS%2CROLES%2CPROTECTED_ROLES%2CDEFAULT_ROLES&ENVDesc=Deployment+environment&DATABASE_TYPEDesc=With+railway+we+are+deploying+postgres+db&ADMIN_SECRETDesc=Secret+to+access+the+admin+apis&COOKIE_NAMEDesc=Name+of+http+only+cookie+that+will+be+used+as+session&FACEBOOK_CLIENT_IDDesc=Facebook+client+ID+for+facebook+login&FACEBOOK_CLIENT_SECRETDesc=Facebook+client+secret+for+facebook+login&GOOGLE_CLIENT_IDDesc=Google+client+ID+for+google+login&GOOGLE_CLIENT_SECRETDesc=Google+client+secret+for+google+login&GITHUB_CLIENT_IDDesc=Github+client+ID+for+github+login&GITHUB_CLIENT_SECRETDesc=Github+client+secret+for+github+login&ALLOWED_ORIGINSDesc=Whitelist+the+URL+for+which+this+instance+of+authorizer+is+allowed&ROLESDesc=Comma+separated+list+of+roles+that+platform+supports.+Default+role+is+user&PROTECTED_ROLESDesc=Comma+separated+list+of+protected+roles+for+which+sign-up+is+disabled&DEFAULT_ROLESDesc=Default+role+that+should+be+assigned+to+user.+It+should+be+one+from+the+list+of+%60ROLES%60+env.+Default+role+is+user&JWT_ROLE_CLAIMDesc=JWT+key+to+be+used+to+validate+the+role+field.&JWT_TYPEDesc=JWT+encryption+type&JWT_SECRETDesc=Random+string+that+will+be+used+for+encrypting+the+JWT+token&ENVDefault=PRODUCTION&DATABASE_TYPEDefault=postgres&COOKIE_NAMEDefault=authorizer&JWT_TYPEDefault=HS256&JWT_ROLE_CLAIMDefault=role)
|
||||||
|
|
||||||
### Things to consider
|
### Things to consider
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 77 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 528 B |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 15 KiB |
840
app/package-lock.json
generated
@@ -1,847 +1,23 @@
|
|||||||
{
|
{
|
||||||
"name": "app",
|
"name": "app",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
|
||||||
"": {
|
|
||||||
"name": "app",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"@authorizerdev/authorizer-react": "latest",
|
|
||||||
"@types/react": "^17.0.15",
|
|
||||||
"@types/react-dom": "^17.0.9",
|
|
||||||
"esbuild": "^0.12.17",
|
|
||||||
"react": "^17.0.2",
|
|
||||||
"react-dom": "^17.0.2",
|
|
||||||
"react-is": "^17.0.2",
|
|
||||||
"react-router-dom": "^5.2.0",
|
|
||||||
"typescript": "^4.3.5"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/react-router-dom": "^5.1.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@authorizerdev/authorizer-js": {
|
|
||||||
"version": "0.4.0-beta.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.4.0-beta.3.tgz",
|
|
||||||
"integrity": "sha512-OGZc6I6cnpi/WkSotkjVIc3LEzl8pFeiohr8+Db9xWd75/oTfOZqWRuIHTnTc1FC+6Sv2EjTJ9Aa6lrloWG+NQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"node-fetch": "^2.6.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@authorizerdev/authorizer-react": {
|
|
||||||
"version": "0.9.0-beta.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.9.0-beta.7.tgz",
|
|
||||||
"integrity": "sha512-hCGsVionKMZNk+uD0CLtMIkUzhQqpHbVntko3rY+O7ouOrTrikY/WQVPbo1bqX1cu/6/cHE4RVU3cZ7V5xnxVg==",
|
|
||||||
"dependencies": {
|
|
||||||
"@authorizerdev/authorizer-js": "^0.4.0-beta.3",
|
|
||||||
"final-form": "^4.20.2",
|
|
||||||
"react-final-form": "^6.5.3",
|
|
||||||
"styled-components": "^5.3.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"react": ">=16"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@babel/code-frame": {
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@babel/generator": {
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@babel/helper-annotate-as-pure": {
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@babel/helper-environment-visitor": {
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@babel/helper-function-name": {
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@babel/helper-get-function-arity": {
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@babel/helper-hoist-variables": {
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@babel/helper-module-imports": {
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@babel/helper-split-export-declaration": {
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@babel/helper-validator-identifier": {
|
|
||||||
"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==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@babel/highlight": {
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@babel/parser": {
|
|
||||||
"version": "7.16.12",
|
|
||||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.12.tgz",
|
|
||||||
"integrity": "sha512-VfaV15po8RiZssrkPweyvbGVSe4x2y+aciFCgn0n0/SJMR22cwofRV1mtnJQYcSB1wUTaA/X1LnA3es66MCO5A==",
|
|
||||||
"bin": {
|
|
||||||
"parser": "bin/babel-parser.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@babel/runtime": {
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@babel/template": {
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@babel/traverse": {
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@babel/types": {
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@emotion/is-prop-valid": {
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@emotion/memoize": {
|
|
||||||
"version": "0.7.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz",
|
|
||||||
"integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw=="
|
|
||||||
},
|
|
||||||
"node_modules/@emotion/stylis": {
|
|
||||||
"version": "0.8.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz",
|
|
||||||
"integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ=="
|
|
||||||
},
|
|
||||||
"node_modules/@emotion/unitless": {
|
|
||||||
"version": "0.7.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
|
|
||||||
"integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="
|
|
||||||
},
|
|
||||||
"node_modules/@types/history": {
|
|
||||||
"version": "4.7.9",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.9.tgz",
|
|
||||||
"integrity": "sha512-MUc6zSmU3tEVnkQ78q0peeEjKWPUADMlC/t++2bI8WnAG2tvYRPIgHG8lWkXwqc8MsUF6Z2MOf+Mh5sazOmhiQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/@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=="
|
|
||||||
},
|
|
||||||
"node_modules/@types/react": {
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/react-dom": {
|
|
||||||
"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": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@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==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@types/history": "*",
|
|
||||||
"@types/react": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/react-router-dom": {
|
|
||||||
"version": "5.1.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.8.tgz",
|
|
||||||
"integrity": "sha512-03xHyncBzG0PmDmf8pf3rehtjY0NpUj7TIN46FrT5n1ZWHPZvXz32gUyNboJ+xsL8cpg8bQVLcllptcQHvocrw==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@types/history": "*",
|
|
||||||
"@types/react": "*",
|
|
||||||
"@types/react-router": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/scheduler": {
|
|
||||||
"version": "0.16.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
|
|
||||||
"integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew=="
|
|
||||||
},
|
|
||||||
"node_modules/ansi-styles": {
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/babel-plugin-styled-components": {
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"styled-components": ">= 2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/babel-plugin-syntax-jsx": {
|
|
||||||
"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="
|
|
||||||
},
|
|
||||||
"node_modules/camelize": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz",
|
|
||||||
"integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs="
|
|
||||||
},
|
|
||||||
"node_modules/chalk": {
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/color-convert": {
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/color-name": {
|
|
||||||
"version": "1.1.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
|
||||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
|
||||||
},
|
|
||||||
"node_modules/css-color-keywords": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
|
|
||||||
"integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU=",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/css-to-react-native": {
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/csstype": {
|
|
||||||
"version": "3.0.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.8.tgz",
|
|
||||||
"integrity": "sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw=="
|
|
||||||
},
|
|
||||||
"node_modules/debug": {
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"supports-color": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/esbuild": {
|
|
||||||
"version": "0.12.17",
|
|
||||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.17.tgz",
|
|
||||||
"integrity": "sha512-GshKJyVYUnlSXIZj/NheC2O0Kblh42CS7P1wJyTbbIHevTG4jYMS9NNw8EOd8dDWD0dzydYHS01MpZoUcQXB4g==",
|
|
||||||
"hasInstallScript": true,
|
|
||||||
"bin": {
|
|
||||||
"esbuild": "bin/esbuild"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/escape-string-regexp": {
|
|
||||||
"version": "1.0.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
|
||||||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.8.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/final-form": {
|
|
||||||
"version": "4.20.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/final-form/-/final-form-4.20.6.tgz",
|
|
||||||
"integrity": "sha512-fCdwIj49KOaFfDRlXB57Eo+GghIMZQWrA9TakQI3C9uQxHwaFHXqZSNRlUdfnQmNNeySwGOaGPZCvjy58hyv4w==",
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/runtime": "^7.10.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/final-form"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/globals": {
|
|
||||||
"version": "11.12.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
|
||||||
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/has-flag": {
|
|
||||||
"version": "3.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
|
||||||
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/history": {
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/hoist-non-react-statics": {
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/hoist-non-react-statics/node_modules/react-is": {
|
|
||||||
"version": "16.13.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
|
||||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
|
||||||
},
|
|
||||||
"node_modules/isarray": {
|
|
||||||
"version": "0.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
|
||||||
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
|
|
||||||
},
|
|
||||||
"node_modules/js-tokens": {
|
|
||||||
"version": "4.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
|
||||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
|
||||||
},
|
|
||||||
"node_modules/jsesc": {
|
|
||||||
"version": "2.5.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
|
|
||||||
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
|
|
||||||
"bin": {
|
|
||||||
"jsesc": "bin/jsesc"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/lodash": {
|
|
||||||
"version": "4.17.21",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
|
||||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
|
||||||
},
|
|
||||||
"node_modules/loose-envify": {
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"loose-envify": "cli.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mini-create-react-context": {
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"prop-types": "^15.0.0",
|
|
||||||
"react": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ms": {
|
|
||||||
"version": "2.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
|
||||||
},
|
|
||||||
"node_modules/node-fetch": {
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "4.x || >=6.0.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"encoding": "^0.1.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"encoding": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/object-assign": {
|
|
||||||
"version": "4.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
|
||||||
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/path-to-regexp": {
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/postcss-value-parser": {
|
|
||||||
"version": "4.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
|
||||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
|
|
||||||
},
|
|
||||||
"node_modules/prop-types": {
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/prop-types/node_modules/react-is": {
|
|
||||||
"version": "16.13.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
|
||||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
|
||||||
},
|
|
||||||
"node_modules/react": {
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-dom": {
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"react": "17.0.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-final-form": {
|
|
||||||
"version": "6.5.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-final-form/-/react-final-form-6.5.7.tgz",
|
|
||||||
"integrity": "sha512-o7tvJXB+McGiXOILqIC8lnOcX4aLhIBiF/Xi9Qet35b7XOS8R7KL8HLRKTfnZWQJm6MCE15v1U0SFive0NcxyA==",
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/runtime": "^7.15.4"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/final-form"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"final-form": "4.20.4",
|
|
||||||
"react": "^16.8.0 || ^17.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-final-form/node_modules/@babel/runtime": {
|
|
||||||
"version": "7.16.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.7.tgz",
|
|
||||||
"integrity": "sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"regenerator-runtime": "^0.13.4"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-is": {
|
|
||||||
"version": "17.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
|
||||||
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
|
|
||||||
},
|
|
||||||
"node_modules/react-router": {
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"react": ">=15"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-router-dom": {
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"react": ">=15"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-router/node_modules/react-is": {
|
|
||||||
"version": "16.13.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
|
||||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
|
||||||
},
|
|
||||||
"node_modules/regenerator-runtime": {
|
|
||||||
"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=="
|
|
||||||
},
|
|
||||||
"node_modules/resolve-pathname": {
|
|
||||||
"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=="
|
|
||||||
},
|
|
||||||
"node_modules/scheduler": {
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/shallowequal": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
|
|
||||||
},
|
|
||||||
"node_modules/source-map": {
|
|
||||||
"version": "0.5.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
|
||||||
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/styled-components": {
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/styled-components"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"react": ">= 16.8.0",
|
|
||||||
"react-dom": ">= 16.8.0",
|
|
||||||
"react-is": ">= 16.8.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/supports-color": {
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/tiny-invariant": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw=="
|
|
||||||
},
|
|
||||||
"node_modules/tiny-warning": {
|
|
||||||
"version": "1.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
|
|
||||||
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
|
|
||||||
},
|
|
||||||
"node_modules/to-fast-properties": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
|
||||||
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/tr46": {
|
|
||||||
"version": "0.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
|
||||||
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
|
|
||||||
},
|
|
||||||
"node_modules/typescript": {
|
|
||||||
"version": "4.3.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz",
|
|
||||||
"integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==",
|
|
||||||
"bin": {
|
|
||||||
"tsc": "bin/tsc",
|
|
||||||
"tsserver": "bin/tsserver"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4.2.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/value-equal": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw=="
|
|
||||||
},
|
|
||||||
"node_modules/webidl-conversions": {
|
|
||||||
"version": "3.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
|
||||||
"integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
|
|
||||||
},
|
|
||||||
"node_modules/whatwg-url": {
|
|
||||||
"version": "5.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
|
||||||
"integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
|
|
||||||
"dependencies": {
|
|
||||||
"tr46": "~0.0.3",
|
|
||||||
"webidl-conversions": "^3.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@authorizerdev/authorizer-js": {
|
"@authorizerdev/authorizer-js": {
|
||||||
"version": "0.4.0-beta.3",
|
"version": "0.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.4.0-beta.3.tgz",
|
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.2.1.tgz",
|
||||||
"integrity": "sha512-OGZc6I6cnpi/WkSotkjVIc3LEzl8pFeiohr8+Db9xWd75/oTfOZqWRuIHTnTc1FC+6Sv2EjTJ9Aa6lrloWG+NQ==",
|
"integrity": "sha512-5lQlh+nc5xTsPongfTyCSX24A1WESu/BjhmZwUNuScEOGady0qPoDHE3RBf46dpi5v05wbHCDN1IFEalX5zssQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"node-fetch": "^2.6.1"
|
"node-fetch": "^2.6.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@authorizerdev/authorizer-react": {
|
"@authorizerdev/authorizer-react": {
|
||||||
"version": "0.9.0-beta.7",
|
"version": "0.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.9.0-beta.7.tgz",
|
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.4.3.tgz",
|
||||||
"integrity": "sha512-hCGsVionKMZNk+uD0CLtMIkUzhQqpHbVntko3rY+O7ouOrTrikY/WQVPbo1bqX1cu/6/cHE4RVU3cZ7V5xnxVg==",
|
"integrity": "sha512-o/wWe9zZ3ARYdjbDfhGfvOxe1YQrE1YQ+UN9pcq85YSDkbfBkOfcnJ4YxlxWdL0Obd/ErDIeQ3vskyrfvRf3sA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@authorizerdev/authorizer-js": "^0.4.0-beta.3",
|
"@authorizerdev/authorizer-js": "^0.2.1",
|
||||||
"final-form": "^4.20.2",
|
"final-form": "^4.20.2",
|
||||||
"react-final-form": "^6.5.3",
|
"react-final-form": "^6.5.3",
|
||||||
"styled-components": "^5.3.0"
|
"styled-components": "^5.3.0"
|
||||||
|
@@ -2,33 +2,10 @@ import React from 'react';
|
|||||||
import { BrowserRouter } from 'react-router-dom';
|
import { BrowserRouter } from 'react-router-dom';
|
||||||
import { AuthorizerProvider } from '@authorizerdev/authorizer-react';
|
import { AuthorizerProvider } from '@authorizerdev/authorizer-react';
|
||||||
import Root from './Root';
|
import Root from './Root';
|
||||||
import { createRandomString } from './utils/common';
|
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const searchParams = new URLSearchParams(window.location.search);
|
// @ts-ignore
|
||||||
const state = searchParams.get('state') || createRandomString();
|
const globalState: Record<string, string> = window['__authorizer__'];
|
||||||
const scope = searchParams.get('scope')
|
|
||||||
? searchParams.get('scope')?.toString().split(' ')
|
|
||||||
: `openid profile email`;
|
|
||||||
|
|
||||||
const urlProps: Record<string, any> = {
|
|
||||||
state,
|
|
||||||
scope,
|
|
||||||
};
|
|
||||||
|
|
||||||
const redirectURL =
|
|
||||||
searchParams.get('redirect_uri') || searchParams.get('redirectURL');
|
|
||||||
if (redirectURL) {
|
|
||||||
urlProps.redirectURL = redirectURL;
|
|
||||||
} else {
|
|
||||||
urlProps.redirectURL = window.location.origin + '/app';
|
|
||||||
}
|
|
||||||
const globalState: Record<string, string> = {
|
|
||||||
// @ts-ignore
|
|
||||||
...window['__authorizer__'],
|
|
||||||
...urlProps,
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@@ -53,7 +30,15 @@ export default function App() {
|
|||||||
/>
|
/>
|
||||||
<h1>{globalState.organizationName}</h1>
|
<h1>{globalState.organizationName}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div className="container">
|
<div
|
||||||
|
style={{
|
||||||
|
width: 400,
|
||||||
|
margin: `10px auto`,
|
||||||
|
border: `1px solid #D1D5DB`,
|
||||||
|
padding: `25px 20px`,
|
||||||
|
borderRadius: 5,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<AuthorizerProvider
|
<AuthorizerProvider
|
||||||
config={{
|
config={{
|
||||||
@@ -61,7 +46,7 @@ export default function App() {
|
|||||||
redirectURL: globalState.redirectURL,
|
redirectURL: globalState.redirectURL,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Root globalState={globalState} />
|
<Root />
|
||||||
</AuthorizerProvider>
|
</AuthorizerProvider>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,36 +1,19 @@
|
|||||||
import React, { useEffect, lazy, Suspense } from 'react';
|
import React, { useEffect, lazy, Suspense } from 'react';
|
||||||
import { Switch, Route } from 'react-router-dom';
|
import { Switch, Route } from 'react-router-dom';
|
||||||
import { useAuthorizer } from '@authorizerdev/authorizer-react';
|
import { useAuthorizer } from '@authorizerdev/authorizer-react';
|
||||||
import SetupPassword from './pages/setup-password';
|
|
||||||
|
|
||||||
const ResetPassword = lazy(() => import('./pages/rest-password'));
|
const ResetPassword = lazy(() => import('./pages/rest-password'));
|
||||||
const Login = lazy(() => import('./pages/login'));
|
const Login = lazy(() => import('./pages/login'));
|
||||||
const Dashboard = lazy(() => import('./pages/dashboard'));
|
const Dashboard = lazy(() => import('./pages/dashboard'));
|
||||||
|
|
||||||
export default function Root({
|
export default function Root() {
|
||||||
globalState,
|
|
||||||
}: {
|
|
||||||
globalState: Record<string, string>;
|
|
||||||
}) {
|
|
||||||
const { token, loading, config } = useAuthorizer();
|
const { token, loading, config } = useAuthorizer();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (token) {
|
if (token) {
|
||||||
let redirectURL = config.redirectURL || '/app';
|
const url = new URL(config.redirectURL || '/app');
|
||||||
let params = `access_token=${token.access_token}&id_token=${token.id_token}&expires_in=${token.expires_in}&state=${globalState.state}`;
|
|
||||||
if (token.refresh_token) {
|
|
||||||
params += `&refresh_token=${token.refresh_token}`;
|
|
||||||
}
|
|
||||||
const url = new URL(redirectURL);
|
|
||||||
if (redirectURL.includes('?')) {
|
|
||||||
redirectURL = `${redirectURL}&${params}`;
|
|
||||||
} else {
|
|
||||||
redirectURL = `${redirectURL}?${params}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (url.origin !== window.location.origin) {
|
if (url.origin !== window.location.origin) {
|
||||||
sessionStorage.removeItem('authorizer_state');
|
window.location.href = config.redirectURL || '/app';
|
||||||
window.location.replace(redirectURL);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return () => {};
|
return () => {};
|
||||||
@@ -61,9 +44,6 @@ export default function Root({
|
|||||||
<Route path="/app/reset-password">
|
<Route path="/app/reset-password">
|
||||||
<ResetPassword />
|
<ResetPassword />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/app/setup-password">
|
|
||||||
<SetupPassword />
|
|
||||||
</Route>
|
|
||||||
</Switch>
|
</Switch>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
);
|
);
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
body {
|
body {
|
||||||
margin: 10;
|
margin: 0;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||||
sans-serif;
|
sans-serif;
|
||||||
@@ -14,17 +14,3 @@ body {
|
|||||||
*:after {
|
*:after {
|
||||||
box-sizing: inherit;
|
box-sizing: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
|
||||||
box-sizing: content-box;
|
|
||||||
border: 1px solid #d1d5db;
|
|
||||||
padding: 25px 20px;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (min-width: 768px) {
|
|
||||||
.container {
|
|
||||||
width: 400px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -15,7 +15,7 @@ export default function Dashboard() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1>Hey 👋,</h1>
|
<h1>Hey 👋,</h1>
|
||||||
<p>Thank you for using authorizer.</p>
|
<p>Thank you for joining authorizer demo app.</p>
|
||||||
<p>
|
<p>
|
||||||
Your email address is{' '}
|
Your email address is{' '}
|
||||||
<a href={`mailto:${user?.email}`} style={{ color: '#3B82F6' }}>
|
<a href={`mailto:${user?.email}`} style={{ color: '#3B82F6' }}>
|
||||||
|
@@ -1,12 +0,0 @@
|
|||||||
import React, { Fragment } from 'react';
|
|
||||||
import { AuthorizerResetPassword } from '@authorizerdev/authorizer-react';
|
|
||||||
|
|
||||||
export default function SetupPassword() {
|
|
||||||
return (
|
|
||||||
<Fragment>
|
|
||||||
<h1 style={{ textAlign: 'center' }}>Setup new Password</h1>
|
|
||||||
<br />
|
|
||||||
<AuthorizerResetPassword />
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
@@ -1,22 +0,0 @@
|
|||||||
export const getCrypto = () => {
|
|
||||||
//ie 11.x uses msCrypto
|
|
||||||
return (window.crypto || (window as any).msCrypto) as Crypto;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const createRandomString = () => {
|
|
||||||
const charset =
|
|
||||||
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_~.';
|
|
||||||
let random = '';
|
|
||||||
const randomValues = Array.from(
|
|
||||||
getCrypto().getRandomValues(new Uint8Array(43))
|
|
||||||
);
|
|
||||||
randomValues.forEach((v) => (random += charset[v % charset.length]));
|
|
||||||
return random;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const createQueryParams = (params: any) => {
|
|
||||||
return Object.keys(params)
|
|
||||||
.filter((k) => typeof params[k] !== 'undefined')
|
|
||||||
.map((k) => encodeURIComponent(k) + '=' + encodeURIComponent(params[k]))
|
|
||||||
.join('&');
|
|
||||||
};
|
|
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 77 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 528 B |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 15 KiB |
2320
dashboard/package-lock.json
generated
@@ -17,11 +17,9 @@
|
|||||||
"@types/react": "^17.0.38",
|
"@types/react": "^17.0.38",
|
||||||
"@types/react-dom": "^17.0.11",
|
"@types/react-dom": "^17.0.11",
|
||||||
"@types/react-router-dom": "^5.3.2",
|
"@types/react-router-dom": "^5.3.2",
|
||||||
"dayjs": "^1.10.7",
|
|
||||||
"esbuild": "^0.14.9",
|
"esbuild": "^0.14.9",
|
||||||
"framer-motion": "^5.5.5",
|
"framer-motion": "^5.5.5",
|
||||||
"graphql": "^16.2.0",
|
"graphql": "^16.2.0",
|
||||||
"lodash": "^4.17.21",
|
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-icons": "^4.3.1",
|
"react-icons": "^4.3.1",
|
||||||
|
@@ -12,7 +12,6 @@ const queryClient = createClient({
|
|||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
requestPolicy: 'network-only',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const theme = extendTheme({
|
const theme = extendTheme({
|
||||||
|
0
dashboard/src/Router.tsx
Normal file
@@ -1,112 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
Center,
|
|
||||||
Flex,
|
|
||||||
MenuItem,
|
|
||||||
Modal,
|
|
||||||
ModalBody,
|
|
||||||
ModalCloseButton,
|
|
||||||
ModalContent,
|
|
||||||
ModalFooter,
|
|
||||||
ModalHeader,
|
|
||||||
ModalOverlay,
|
|
||||||
useDisclosure,
|
|
||||||
Text,
|
|
||||||
useToast,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { useClient } from 'urql';
|
|
||||||
import { FaRegTrashAlt } from 'react-icons/fa';
|
|
||||||
import { DeleteUser } from '../graphql/mutation';
|
|
||||||
import { capitalizeFirstLetter } from '../utils';
|
|
||||||
|
|
||||||
interface userDataTypes {
|
|
||||||
id: string;
|
|
||||||
email: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const DeleteUserModal = ({
|
|
||||||
user,
|
|
||||||
updateUserList,
|
|
||||||
}: {
|
|
||||||
user: userDataTypes;
|
|
||||||
updateUserList: Function;
|
|
||||||
}) => {
|
|
||||||
const client = useClient();
|
|
||||||
const toast = useToast();
|
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
|
||||||
const [userData, setUserData] = React.useState<userDataTypes>({
|
|
||||||
id: '',
|
|
||||||
email: '',
|
|
||||||
});
|
|
||||||
React.useEffect(() => {
|
|
||||||
setUserData(user);
|
|
||||||
}, []);
|
|
||||||
const deleteHandler = async () => {
|
|
||||||
const res = await client
|
|
||||||
.mutation(DeleteUser, { params: { email: userData.email } })
|
|
||||||
.toPromise();
|
|
||||||
if (res.error) {
|
|
||||||
toast({
|
|
||||||
title: capitalizeFirstLetter(res.error.message),
|
|
||||||
isClosable: true,
|
|
||||||
status: 'error',
|
|
||||||
position: 'bottom-right',
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
|
||||||
} else if (res.data?._delete_user) {
|
|
||||||
toast({
|
|
||||||
title: capitalizeFirstLetter(res.data?._delete_user.message),
|
|
||||||
isClosable: true,
|
|
||||||
status: 'success',
|
|
||||||
position: 'bottom-right',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
onClose();
|
|
||||||
updateUserList();
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<MenuItem onClick={onOpen}>Delete User</MenuItem>
|
|
||||||
<Modal isOpen={isOpen} onClose={onClose}>
|
|
||||||
<ModalOverlay />
|
|
||||||
<ModalContent>
|
|
||||||
<ModalHeader>Delete User</ModalHeader>
|
|
||||||
<ModalCloseButton />
|
|
||||||
<ModalBody>
|
|
||||||
<Text fontSize="md">Are you sure?</Text>
|
|
||||||
<Flex
|
|
||||||
padding="5%"
|
|
||||||
marginTop="5%"
|
|
||||||
marginBottom="2%"
|
|
||||||
border="1px solid #ff7875"
|
|
||||||
borderRadius="5px"
|
|
||||||
flexDirection="column"
|
|
||||||
>
|
|
||||||
<Text fontSize="sm">
|
|
||||||
User <b>{user.email}</b> will be deleted permanently!
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</ModalBody>
|
|
||||||
|
|
||||||
<ModalFooter>
|
|
||||||
<Button
|
|
||||||
leftIcon={<FaRegTrashAlt />}
|
|
||||||
colorScheme="red"
|
|
||||||
variant="solid"
|
|
||||||
onClick={deleteHandler}
|
|
||||||
isDisabled={false}
|
|
||||||
>
|
|
||||||
<Center h="100%" pt="5%">
|
|
||||||
Delete
|
|
||||||
</Center>
|
|
||||||
</Button>
|
|
||||||
</ModalFooter>
|
|
||||||
</ModalContent>
|
|
||||||
</Modal>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DeleteUserModal;
|
|
@@ -1,250 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
Center,
|
|
||||||
Flex,
|
|
||||||
MenuItem,
|
|
||||||
Modal,
|
|
||||||
ModalBody,
|
|
||||||
ModalCloseButton,
|
|
||||||
ModalContent,
|
|
||||||
ModalFooter,
|
|
||||||
ModalHeader,
|
|
||||||
ModalOverlay,
|
|
||||||
Stack,
|
|
||||||
useDisclosure,
|
|
||||||
Text,
|
|
||||||
useToast,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { useClient } from 'urql';
|
|
||||||
import { FaSave } from 'react-icons/fa';
|
|
||||||
import InputField from './InputField';
|
|
||||||
import {
|
|
||||||
ArrayInputType,
|
|
||||||
DateInputType,
|
|
||||||
SelectInputType,
|
|
||||||
TextInputType,
|
|
||||||
} from '../constants';
|
|
||||||
import { getObjectDiff } from '../utils';
|
|
||||||
import { UpdateUser } from '../graphql/mutation';
|
|
||||||
|
|
||||||
const GenderTypes = {
|
|
||||||
Undisclosed: null,
|
|
||||||
Male: 'Male',
|
|
||||||
Female: 'Female',
|
|
||||||
};
|
|
||||||
|
|
||||||
interface userDataTypes {
|
|
||||||
id: string;
|
|
||||||
email: string;
|
|
||||||
given_name: string;
|
|
||||||
family_name: string;
|
|
||||||
middle_name: string;
|
|
||||||
nickname: string;
|
|
||||||
gender: string;
|
|
||||||
birthdate: string;
|
|
||||||
phone_number: string;
|
|
||||||
picture: string;
|
|
||||||
roles: [string] | [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const EditUserModal = ({
|
|
||||||
user,
|
|
||||||
updateUserList,
|
|
||||||
}: {
|
|
||||||
user: userDataTypes;
|
|
||||||
updateUserList: Function;
|
|
||||||
}) => {
|
|
||||||
const client = useClient();
|
|
||||||
const toast = useToast();
|
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
|
||||||
const [userData, setUserData] = React.useState<userDataTypes>({
|
|
||||||
id: '',
|
|
||||||
email: '',
|
|
||||||
given_name: '',
|
|
||||||
family_name: '',
|
|
||||||
middle_name: '',
|
|
||||||
nickname: '',
|
|
||||||
gender: '',
|
|
||||||
birthdate: '',
|
|
||||||
phone_number: '',
|
|
||||||
picture: '',
|
|
||||||
roles: [],
|
|
||||||
});
|
|
||||||
React.useEffect(() => {
|
|
||||||
setUserData(user);
|
|
||||||
}, []);
|
|
||||||
const saveHandler = async () => {
|
|
||||||
const diff = getObjectDiff(user, userData);
|
|
||||||
const updatedUserData = diff.reduce(
|
|
||||||
(acc: any, property: string) => ({
|
|
||||||
...acc,
|
|
||||||
// @ts-ignore
|
|
||||||
[property]: userData[property],
|
|
||||||
}),
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
const res = await client
|
|
||||||
.mutation(UpdateUser, { params: { ...updatedUserData, id: userData.id } })
|
|
||||||
.toPromise();
|
|
||||||
if (res.error) {
|
|
||||||
toast({
|
|
||||||
title: 'User data update failed',
|
|
||||||
isClosable: true,
|
|
||||||
status: 'error',
|
|
||||||
position: 'bottom-right',
|
|
||||||
});
|
|
||||||
} else if (res.data?._update_user?.id) {
|
|
||||||
toast({
|
|
||||||
title: 'User data update successful',
|
|
||||||
isClosable: true,
|
|
||||||
status: 'success',
|
|
||||||
position: 'bottom-right',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
onClose();
|
|
||||||
updateUserList();
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<MenuItem onClick={onOpen}>Edit User Details</MenuItem>
|
|
||||||
<Modal isOpen={isOpen} onClose={onClose}>
|
|
||||||
<ModalOverlay />
|
|
||||||
<ModalContent>
|
|
||||||
<ModalHeader>Edit User Details</ModalHeader>
|
|
||||||
<ModalCloseButton />
|
|
||||||
<ModalBody>
|
|
||||||
<Stack>
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">Given Name:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={userData}
|
|
||||||
setVariables={setUserData}
|
|
||||||
inputType={TextInputType.GIVEN_NAME}
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">Middle Name:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={userData}
|
|
||||||
setVariables={setUserData}
|
|
||||||
inputType={TextInputType.MIDDLE_NAME}
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">Family Name:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={userData}
|
|
||||||
setVariables={setUserData}
|
|
||||||
inputType={TextInputType.FAMILY_NAME}
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">Birth Date:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={userData}
|
|
||||||
setVariables={setUserData}
|
|
||||||
inputType={DateInputType.BIRTHDATE}
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">Nickname:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={userData}
|
|
||||||
setVariables={setUserData}
|
|
||||||
inputType={TextInputType.NICKNAME}
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">Gender:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={userData}
|
|
||||||
setVariables={setUserData}
|
|
||||||
inputType={SelectInputType.GENDER}
|
|
||||||
value={userData.gender}
|
|
||||||
options={GenderTypes}
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">Phone Number:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={userData}
|
|
||||||
setVariables={setUserData}
|
|
||||||
inputType={TextInputType.PHONE_NUMBER}
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">Picture:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={userData}
|
|
||||||
setVariables={setUserData}
|
|
||||||
inputType={TextInputType.PICTURE}
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">Roles:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={userData}
|
|
||||||
setVariables={setUserData}
|
|
||||||
inputType={ArrayInputType.USER_ROLES}
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
</Stack>
|
|
||||||
</ModalBody>
|
|
||||||
|
|
||||||
<ModalFooter>
|
|
||||||
<Button
|
|
||||||
leftIcon={<FaSave />}
|
|
||||||
colorScheme="blue"
|
|
||||||
variant="solid"
|
|
||||||
onClick={saveHandler}
|
|
||||||
isDisabled={false}
|
|
||||||
>
|
|
||||||
<Center h="100%" pt="5%">
|
|
||||||
Save
|
|
||||||
</Center>
|
|
||||||
</Button>
|
|
||||||
</ModalFooter>
|
|
||||||
</ModalContent>
|
|
||||||
</Modal>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EditUserModal;
|
|
@@ -1,335 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import {
|
|
||||||
Box,
|
|
||||||
Flex,
|
|
||||||
Input,
|
|
||||||
Center,
|
|
||||||
InputGroup,
|
|
||||||
InputRightElement,
|
|
||||||
Tag,
|
|
||||||
TagLabel,
|
|
||||||
TagRightIcon,
|
|
||||||
Select,
|
|
||||||
Textarea,
|
|
||||||
Switch,
|
|
||||||
Code,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import {
|
|
||||||
FaRegClone,
|
|
||||||
FaRegEye,
|
|
||||||
FaRegEyeSlash,
|
|
||||||
FaPlus,
|
|
||||||
FaTimes,
|
|
||||||
} from 'react-icons/fa';
|
|
||||||
import {
|
|
||||||
ArrayInputOperations,
|
|
||||||
ArrayInputType,
|
|
||||||
SelectInputType,
|
|
||||||
HiddenInputType,
|
|
||||||
TextInputType,
|
|
||||||
TextAreaInputType,
|
|
||||||
SwitchInputType,
|
|
||||||
DateInputType,
|
|
||||||
} from '../constants';
|
|
||||||
import { copyTextToClipboard } from '../utils';
|
|
||||||
|
|
||||||
const InputField = ({
|
|
||||||
inputType,
|
|
||||||
variables,
|
|
||||||
setVariables,
|
|
||||||
fieldVisibility,
|
|
||||||
setFieldVisibility,
|
|
||||||
...downshiftProps
|
|
||||||
}: any) => {
|
|
||||||
const props = {
|
|
||||||
size: 'sm',
|
|
||||||
...downshiftProps,
|
|
||||||
};
|
|
||||||
const [inputFieldVisibility, setInputFieldVisibility] = React.useState<
|
|
||||||
Record<string, boolean>
|
|
||||||
>({
|
|
||||||
ROLES: false,
|
|
||||||
DEFAULT_ROLES: false,
|
|
||||||
PROTECTED_ROLES: false,
|
|
||||||
ALLOWED_ORIGINS: false,
|
|
||||||
roles: false,
|
|
||||||
});
|
|
||||||
const [inputData, setInputData] = React.useState<Record<string, string>>({
|
|
||||||
ROLES: '',
|
|
||||||
DEFAULT_ROLES: '',
|
|
||||||
PROTECTED_ROLES: '',
|
|
||||||
ALLOWED_ORIGINS: '',
|
|
||||||
roles: '',
|
|
||||||
});
|
|
||||||
const updateInputHandler = (
|
|
||||||
type: string,
|
|
||||||
operation: any,
|
|
||||||
role: string = ''
|
|
||||||
) => {
|
|
||||||
if (operation === ArrayInputOperations.APPEND) {
|
|
||||||
if (inputData[type] !== '') {
|
|
||||||
setVariables({
|
|
||||||
...variables,
|
|
||||||
[type]: [...variables[type], inputData[type]],
|
|
||||||
});
|
|
||||||
setInputData({ ...inputData, [type]: '' });
|
|
||||||
}
|
|
||||||
setInputFieldVisibility({ ...inputFieldVisibility, [type]: false });
|
|
||||||
}
|
|
||||||
if (operation === ArrayInputOperations.REMOVE) {
|
|
||||||
let updatedEnvVars = variables[type].filter(
|
|
||||||
(item: string) => item !== role
|
|
||||||
);
|
|
||||||
setVariables({
|
|
||||||
...variables,
|
|
||||||
[type]: updatedEnvVars,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (Object.values(TextInputType).includes(inputType)) {
|
|
||||||
return (
|
|
||||||
<InputGroup size="sm">
|
|
||||||
<Input
|
|
||||||
{...props}
|
|
||||||
value={variables[inputType] ? variables[inputType] : ''}
|
|
||||||
onChange={(
|
|
||||||
event: Event & {
|
|
||||||
target: HTMLInputElement;
|
|
||||||
}
|
|
||||||
) =>
|
|
||||||
setVariables({
|
|
||||||
...variables,
|
|
||||||
[inputType]: event.target.value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<InputRightElement
|
|
||||||
children={<FaRegClone color="#bfbfbf" />}
|
|
||||||
cursor="pointer"
|
|
||||||
onClick={() => copyTextToClipboard(variables[inputType])}
|
|
||||||
/>
|
|
||||||
</InputGroup>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (Object.values(HiddenInputType).includes(inputType)) {
|
|
||||||
return (
|
|
||||||
<InputGroup size="sm">
|
|
||||||
<Input
|
|
||||||
{...props}
|
|
||||||
value={variables[inputType]}
|
|
||||||
onChange={(
|
|
||||||
event: Event & {
|
|
||||||
target: HTMLInputElement;
|
|
||||||
}
|
|
||||||
) =>
|
|
||||||
setVariables({
|
|
||||||
...variables,
|
|
||||||
[inputType]: event.target.value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
type={!fieldVisibility[inputType] ? 'password' : 'text'}
|
|
||||||
/>
|
|
||||||
<InputRightElement
|
|
||||||
right="15px"
|
|
||||||
children={
|
|
||||||
<Flex>
|
|
||||||
{fieldVisibility[inputType] ? (
|
|
||||||
<Center
|
|
||||||
w="25px"
|
|
||||||
margin="0 1.5%"
|
|
||||||
cursor="pointer"
|
|
||||||
onClick={() =>
|
|
||||||
setFieldVisibility({
|
|
||||||
...fieldVisibility,
|
|
||||||
[inputType]: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<FaRegEyeSlash color="#bfbfbf" />
|
|
||||||
</Center>
|
|
||||||
) : (
|
|
||||||
<Center
|
|
||||||
w="25px"
|
|
||||||
margin="0 1.5%"
|
|
||||||
cursor="pointer"
|
|
||||||
onClick={() =>
|
|
||||||
setFieldVisibility({
|
|
||||||
...fieldVisibility,
|
|
||||||
[inputType]: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<FaRegEye color="#bfbfbf" />
|
|
||||||
</Center>
|
|
||||||
)}
|
|
||||||
<Center
|
|
||||||
w="25px"
|
|
||||||
margin="0 1.5%"
|
|
||||||
cursor="pointer"
|
|
||||||
onClick={() => copyTextToClipboard(variables[inputType])}
|
|
||||||
>
|
|
||||||
<FaRegClone color="#bfbfbf" />
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</InputGroup>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (Object.values(ArrayInputType).includes(inputType)) {
|
|
||||||
return (
|
|
||||||
<Flex
|
|
||||||
border="1px solid #e2e8f0"
|
|
||||||
w="100%"
|
|
||||||
paddingTop="0.5%"
|
|
||||||
overflowX="scroll"
|
|
||||||
overflowY="hidden"
|
|
||||||
justifyContent="start"
|
|
||||||
alignItems="center"
|
|
||||||
>
|
|
||||||
{variables[inputType].map((role: string, index: number) => (
|
|
||||||
<Box key={index} margin="0.5%" role="group">
|
|
||||||
<Tag
|
|
||||||
size="sm"
|
|
||||||
variant="outline"
|
|
||||||
colorScheme="gray"
|
|
||||||
minW="fit-content"
|
|
||||||
>
|
|
||||||
<TagLabel cursor="default">{role}</TagLabel>
|
|
||||||
<TagRightIcon
|
|
||||||
boxSize="12px"
|
|
||||||
as={FaTimes}
|
|
||||||
display="none"
|
|
||||||
cursor="pointer"
|
|
||||||
_groupHover={{ display: 'block' }}
|
|
||||||
onClick={() =>
|
|
||||||
updateInputHandler(
|
|
||||||
inputType,
|
|
||||||
ArrayInputOperations.REMOVE,
|
|
||||||
role
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Tag>
|
|
||||||
</Box>
|
|
||||||
))}
|
|
||||||
{inputFieldVisibility[inputType] ? (
|
|
||||||
<Box ml="1%" mb="0.75%">
|
|
||||||
<Input
|
|
||||||
type="text"
|
|
||||||
size="xs"
|
|
||||||
minW="150px"
|
|
||||||
placeholder="add a new value"
|
|
||||||
value={inputData[inputType]}
|
|
||||||
onChange={(e: any) => {
|
|
||||||
setInputData({ ...inputData, [inputType]: e.target.value });
|
|
||||||
}}
|
|
||||||
onBlur={() =>
|
|
||||||
updateInputHandler(inputType, ArrayInputOperations.APPEND)
|
|
||||||
}
|
|
||||||
onKeyPress={(event) => {
|
|
||||||
if (event.key === 'Enter') {
|
|
||||||
updateInputHandler(inputType, ArrayInputOperations.APPEND);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
) : (
|
|
||||||
<Box
|
|
||||||
marginLeft="0.5%"
|
|
||||||
cursor="pointer"
|
|
||||||
onClick={() =>
|
|
||||||
setInputFieldVisibility({
|
|
||||||
...inputFieldVisibility,
|
|
||||||
[inputType]: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Tag
|
|
||||||
size="sm"
|
|
||||||
variant="outline"
|
|
||||||
colorScheme="gray"
|
|
||||||
minW="fit-content"
|
|
||||||
>
|
|
||||||
<FaPlus />
|
|
||||||
</Tag>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (Object.values(SelectInputType).includes(inputType)) {
|
|
||||||
const { options, ...rest } = props;
|
|
||||||
return (
|
|
||||||
<Select
|
|
||||||
size="sm"
|
|
||||||
{...rest}
|
|
||||||
value={variables[inputType] ? variables[inputType] : ''}
|
|
||||||
onChange={(e) =>
|
|
||||||
setVariables({ ...variables, [inputType]: e.target.value })
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{Object.entries(options).map(([key, value]: any) => (
|
|
||||||
<option value={value} key={key}>
|
|
||||||
{key}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (Object.values(TextAreaInputType).includes(inputType)) {
|
|
||||||
return (
|
|
||||||
<Textarea
|
|
||||||
{...props}
|
|
||||||
size="lg"
|
|
||||||
fontSize={14}
|
|
||||||
value={variables[inputType] ? variables[inputType] : ''}
|
|
||||||
onChange={(
|
|
||||||
event: Event & {
|
|
||||||
target: HTMLInputElement;
|
|
||||||
}
|
|
||||||
) =>
|
|
||||||
setVariables({
|
|
||||||
...variables,
|
|
||||||
[inputType]: event.target.value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (Object.values(SwitchInputType).includes(inputType)) {
|
|
||||||
return (
|
|
||||||
<Flex w="25%" justifyContent="space-between">
|
|
||||||
<Code h="75%">Off</Code>
|
|
||||||
<Switch
|
|
||||||
size="md"
|
|
||||||
isChecked={variables[inputType]}
|
|
||||||
onChange={() => {
|
|
||||||
setVariables({
|
|
||||||
...variables,
|
|
||||||
[inputType]: !variables[inputType],
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Code h="75%">On</Code>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (Object.values(DateInputType).includes(inputType)) {
|
|
||||||
return (
|
|
||||||
<Flex border="1px solid #e2e8f0" w="100%" h="33px" padding="1%">
|
|
||||||
<input
|
|
||||||
type="date"
|
|
||||||
style={{ width: '100%', paddingLeft: '2.5%' }}
|
|
||||||
value={variables[inputType] ? variables[inputType] : ''}
|
|
||||||
onChange={(e) =>
|
|
||||||
setVariables({ ...variables, [inputType]: e.target.value })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default InputField;
|
|
@@ -1,6 +1,7 @@
|
|||||||
import React, { ReactNode } from 'react';
|
import React, { ReactNode } from 'react';
|
||||||
import {
|
import {
|
||||||
IconButton,
|
IconButton,
|
||||||
|
Avatar,
|
||||||
Box,
|
Box,
|
||||||
CloseButton,
|
CloseButton,
|
||||||
Flex,
|
Flex,
|
||||||
@@ -20,7 +21,9 @@ import {
|
|||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import {
|
import {
|
||||||
FiHome,
|
FiHome,
|
||||||
FiCode,
|
FiTrendingUp,
|
||||||
|
FiCompass,
|
||||||
|
FiStar,
|
||||||
FiSettings,
|
FiSettings,
|
||||||
FiMenu,
|
FiMenu,
|
||||||
FiUser,
|
FiUser,
|
||||||
@@ -29,11 +32,10 @@ import {
|
|||||||
} from 'react-icons/fi';
|
} from 'react-icons/fi';
|
||||||
import { IconType } from 'react-icons';
|
import { IconType } from 'react-icons';
|
||||||
import { ReactText } from 'react';
|
import { ReactText } from 'react';
|
||||||
import { useMutation, useQuery } from 'urql';
|
import { useMutation } from 'urql';
|
||||||
import { NavLink, useNavigate, useLocation } from 'react-router-dom';
|
import { NavLink, useNavigate, useLocation } from 'react-router-dom';
|
||||||
import { useAuthContext } from '../contexts/AuthContext';
|
import { useAuthContext } from '../contexts/AuthContext';
|
||||||
import { AdminLogout } from '../graphql/mutation';
|
import { AdminLogout } from '../graphql/mutation';
|
||||||
import { MetaQuery } from '../graphql/queries';
|
|
||||||
|
|
||||||
interface LinkItemProps {
|
interface LinkItemProps {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -41,9 +43,9 @@ interface LinkItemProps {
|
|||||||
route: string;
|
route: string;
|
||||||
}
|
}
|
||||||
const LinkItems: Array<LinkItemProps> = [
|
const LinkItems: Array<LinkItemProps> = [
|
||||||
// { name: 'Home', icon: FiHome, route: '/' },
|
{ name: 'Home', icon: FiHome, route: '/' },
|
||||||
{ name: 'Environment Variables', icon: FiSettings, route: '/' },
|
|
||||||
{ name: 'Users', icon: FiUsers, route: '/users' },
|
{ name: 'Users', icon: FiUsers, route: '/users' },
|
||||||
|
{ name: 'Environment Variables', icon: FiSettings, route: '/environment' },
|
||||||
];
|
];
|
||||||
|
|
||||||
interface SidebarProps extends BoxProps {
|
interface SidebarProps extends BoxProps {
|
||||||
@@ -52,7 +54,6 @@ interface SidebarProps extends BoxProps {
|
|||||||
|
|
||||||
export const Sidebar = ({ onClose, ...rest }: SidebarProps) => {
|
export const Sidebar = ({ onClose, ...rest }: SidebarProps) => {
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
const [{ fetching, data }] = useQuery({ query: MetaQuery });
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
transition="3s ease"
|
transition="3s ease"
|
||||||
@@ -89,30 +90,6 @@ export const Sidebar = ({ onClose, ...rest }: SidebarProps) => {
|
|||||||
</NavItem>
|
</NavItem>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<Link
|
|
||||||
href="/playground"
|
|
||||||
target="_blank"
|
|
||||||
style={{
|
|
||||||
textDecoration: 'none',
|
|
||||||
}}
|
|
||||||
_focus={{ _boxShadow: 'none' }}
|
|
||||||
>
|
|
||||||
<NavItem icon={FiCode}>API Playground</NavItem>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
{data?.meta?.version && (
|
|
||||||
<Text
|
|
||||||
color="gray.600"
|
|
||||||
fontSize="sm"
|
|
||||||
textAlign="center"
|
|
||||||
position="absolute"
|
|
||||||
bottom="5"
|
|
||||||
left="7"
|
|
||||||
>
|
|
||||||
Current Version: {data.meta.version}
|
|
||||||
</Text>
|
|
||||||
)}
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -123,31 +100,37 @@ interface NavItemProps extends FlexProps {
|
|||||||
}
|
}
|
||||||
export const NavItem = ({ icon, children, ...rest }: NavItemProps) => {
|
export const NavItem = ({ icon, children, ...rest }: NavItemProps) => {
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Link
|
||||||
align="center"
|
href="#"
|
||||||
p="3"
|
style={{ textDecoration: 'none' }}
|
||||||
mx="3"
|
_focus={{ boxShadow: 'none' }}
|
||||||
borderRadius="md"
|
|
||||||
role="group"
|
|
||||||
cursor="pointer"
|
|
||||||
_hover={{
|
|
||||||
bg: 'blue.500',
|
|
||||||
color: 'white',
|
|
||||||
}}
|
|
||||||
{...rest}
|
|
||||||
>
|
>
|
||||||
{icon && (
|
<Flex
|
||||||
<Icon
|
align="center"
|
||||||
mr="4"
|
p="3"
|
||||||
fontSize="16"
|
mx="3"
|
||||||
_groupHover={{
|
borderRadius="md"
|
||||||
color: 'white',
|
role="group"
|
||||||
}}
|
cursor="pointer"
|
||||||
as={icon}
|
_hover={{
|
||||||
/>
|
bg: 'blue.500',
|
||||||
)}
|
color: 'white',
|
||||||
{children}
|
}}
|
||||||
</Flex>
|
{...rest}
|
||||||
|
>
|
||||||
|
{icon && (
|
||||||
|
<Icon
|
||||||
|
mr="4"
|
||||||
|
fontSize="16"
|
||||||
|
_groupHover={{
|
||||||
|
color: 'white',
|
||||||
|
}}
|
||||||
|
as={icon}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{children}
|
||||||
|
</Flex>
|
||||||
|
</Link>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -178,7 +161,6 @@ export const MobileNav = ({ onOpen, ...rest }: MobileProps) => {
|
|||||||
borderBottomWidth="1px"
|
borderBottomWidth="1px"
|
||||||
borderBottomColor={useColorModeValue('gray.200', 'gray.700')}
|
borderBottomColor={useColorModeValue('gray.200', 'gray.700')}
|
||||||
justifyContent={{ base: 'space-between', md: 'flex-end' }}
|
justifyContent={{ base: 'space-between', md: 'flex-end' }}
|
||||||
zIndex={99}
|
|
||||||
{...rest}
|
{...rest}
|
||||||
>
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
@@ -1,90 +1 @@
|
|||||||
export const LOGO_URL =
|
export const LOGO_URL = "https://user-images.githubusercontent.com/6964334/147834043-fc384cab-e7ca-40f8-9663-38fc25fd5f3a.png"
|
||||||
'https://user-images.githubusercontent.com/6964334/147834043-fc384cab-e7ca-40f8-9663-38fc25fd5f3a.png';
|
|
||||||
|
|
||||||
export const TextInputType = {
|
|
||||||
CLIENT_ID: 'CLIENT_ID',
|
|
||||||
GOOGLE_CLIENT_ID: 'GOOGLE_CLIENT_ID',
|
|
||||||
GITHUB_CLIENT_ID: 'GITHUB_CLIENT_ID',
|
|
||||||
FACEBOOK_CLIENT_ID: 'FACEBOOK_CLIENT_ID',
|
|
||||||
JWT_ROLE_CLAIM: 'JWT_ROLE_CLAIM',
|
|
||||||
REDIS_URL: 'REDIS_URL',
|
|
||||||
SMTP_HOST: 'SMTP_HOST',
|
|
||||||
SMTP_PORT: 'SMTP_PORT',
|
|
||||||
SMTP_USERNAME: 'SMTP_USERNAME',
|
|
||||||
SENDER_EMAIL: 'SENDER_EMAIL',
|
|
||||||
ORGANIZATION_NAME: 'ORGANIZATION_NAME',
|
|
||||||
ORGANIZATION_LOGO: 'ORGANIZATION_LOGO',
|
|
||||||
DATABASE_NAME: 'DATABASE_NAME',
|
|
||||||
DATABASE_TYPE: 'DATABASE_TYPE',
|
|
||||||
DATABASE_URL: 'DATABASE_URL',
|
|
||||||
GIVEN_NAME: 'given_name',
|
|
||||||
MIDDLE_NAME: 'middle_name',
|
|
||||||
FAMILY_NAME: 'family_name',
|
|
||||||
NICKNAME: 'nickname',
|
|
||||||
PHONE_NUMBER: 'phone_number',
|
|
||||||
PICTURE: 'picture',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const HiddenInputType = {
|
|
||||||
CLIENT_SECRET: 'CLIENT_SECRET',
|
|
||||||
GOOGLE_CLIENT_SECRET: 'GOOGLE_CLIENT_SECRET',
|
|
||||||
GITHUB_CLIENT_SECRET: 'GITHUB_CLIENT_SECRET',
|
|
||||||
FACEBOOK_CLIENT_SECRET: 'FACEBOOK_CLIENT_SECRET',
|
|
||||||
JWT_SECRET: 'JWT_SECRET',
|
|
||||||
SMTP_PASSWORD: 'SMTP_PASSWORD',
|
|
||||||
ADMIN_SECRET: 'ADMIN_SECRET',
|
|
||||||
OLD_ADMIN_SECRET: 'OLD_ADMIN_SECRET',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ArrayInputType = {
|
|
||||||
ROLES: 'ROLES',
|
|
||||||
DEFAULT_ROLES: 'DEFAULT_ROLES',
|
|
||||||
PROTECTED_ROLES: 'PROTECTED_ROLES',
|
|
||||||
ALLOWED_ORIGINS: 'ALLOWED_ORIGINS',
|
|
||||||
USER_ROLES: 'roles',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const SelectInputType = {
|
|
||||||
JWT_TYPE: 'JWT_TYPE',
|
|
||||||
GENDER: 'gender',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const TextAreaInputType = {
|
|
||||||
CUSTOM_ACCESS_TOKEN_SCRIPT: 'CUSTOM_ACCESS_TOKEN_SCRIPT',
|
|
||||||
JWT_PRIVATE_KEY: 'JWT_PRIVATE_KEY',
|
|
||||||
JWT_PUBLIC_KEY: 'JWT_PUBLIC_KEY',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const SwitchInputType = {
|
|
||||||
DISABLE_LOGIN_PAGE: 'DISABLE_LOGIN_PAGE',
|
|
||||||
DISABLE_MAGIC_LINK_LOGIN: 'DISABLE_MAGIC_LINK_LOGIN',
|
|
||||||
DISABLE_EMAIL_VERIFICATION: 'DISABLE_EMAIL_VERIFICATION',
|
|
||||||
DISABLE_BASIC_AUTHENTICATION: 'DISABLE_BASIC_AUTHENTICATION',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const DateInputType = {
|
|
||||||
BIRTHDATE: 'birthdate',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ArrayInputOperations = {
|
|
||||||
APPEND: 'APPEND',
|
|
||||||
REMOVE: 'REMOVE',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const HMACEncryptionType = {
|
|
||||||
HS256: 'HS256',
|
|
||||||
HS384: 'HS384',
|
|
||||||
HS512: 'HS512',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const RSAEncryptionType = {
|
|
||||||
RS256: 'RS256',
|
|
||||||
RS384: 'RS384',
|
|
||||||
RS512: 'RS512',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ECDSAEncryptionType = {
|
|
||||||
ES256: 'ES256',
|
|
||||||
ES384: 'ES384',
|
|
||||||
ES512: 'ES512',
|
|
||||||
};
|
|
@@ -32,7 +32,7 @@ export const AuthContextProvider = ({ children }: { children: any }) => {
|
|||||||
|
|
||||||
if (fetching) {
|
if (fetching) {
|
||||||
return (
|
return (
|
||||||
<Center h="100%">
|
<Center>
|
||||||
<Spinner />
|
<Spinner />
|
||||||
</Center>
|
</Center>
|
||||||
);
|
);
|
||||||
|
@@ -21,27 +21,3 @@ export const AdminLogout = `
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const UpdateEnvVariables = `
|
|
||||||
mutation updateEnvVariables($params: UpdateEnvInput!) {
|
|
||||||
_update_env(params: $params) {
|
|
||||||
message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const UpdateUser = `
|
|
||||||
mutation updateUser($params: UpdateUserInput!) {
|
|
||||||
_update_user(params: $params) {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const DeleteUser = `
|
|
||||||
mutation deleteUser($params: DeleteUserInput!) {
|
|
||||||
_delete_user(params: $params) {
|
|
||||||
message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
@@ -1,12 +1,3 @@
|
|||||||
export const MetaQuery = `
|
|
||||||
query MetaQuery {
|
|
||||||
meta {
|
|
||||||
version
|
|
||||||
client_id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const AdminSessionQuery = `
|
export const AdminSessionQuery = `
|
||||||
query {
|
query {
|
||||||
_admin_session{
|
_admin_session{
|
||||||
@@ -14,73 +5,3 @@ export const AdminSessionQuery = `
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const EnvVariablesQuery = `
|
|
||||||
query {
|
|
||||||
_env{
|
|
||||||
CLIENT_ID,
|
|
||||||
CLIENT_SECRET,
|
|
||||||
GOOGLE_CLIENT_ID,
|
|
||||||
GOOGLE_CLIENT_SECRET,
|
|
||||||
GITHUB_CLIENT_ID,
|
|
||||||
GITHUB_CLIENT_SECRET,
|
|
||||||
FACEBOOK_CLIENT_ID,
|
|
||||||
FACEBOOK_CLIENT_SECRET,
|
|
||||||
ROLES,
|
|
||||||
DEFAULT_ROLES,
|
|
||||||
PROTECTED_ROLES,
|
|
||||||
JWT_TYPE,
|
|
||||||
JWT_SECRET,
|
|
||||||
JWT_ROLE_CLAIM,
|
|
||||||
JWT_PRIVATE_KEY,
|
|
||||||
JWT_PUBLIC_KEY,
|
|
||||||
REDIS_URL,
|
|
||||||
SMTP_HOST,
|
|
||||||
SMTP_PORT,
|
|
||||||
SMTP_USERNAME,
|
|
||||||
SMTP_PASSWORD,
|
|
||||||
SENDER_EMAIL,
|
|
||||||
ALLOWED_ORIGINS,
|
|
||||||
ORGANIZATION_NAME,
|
|
||||||
ORGANIZATION_LOGO,
|
|
||||||
ADMIN_SECRET,
|
|
||||||
DISABLE_LOGIN_PAGE,
|
|
||||||
DISABLE_MAGIC_LINK_LOGIN,
|
|
||||||
DISABLE_EMAIL_VERIFICATION,
|
|
||||||
DISABLE_BASIC_AUTHENTICATION,
|
|
||||||
CUSTOM_ACCESS_TOKEN_SCRIPT,
|
|
||||||
DATABASE_NAME,
|
|
||||||
DATABASE_TYPE,
|
|
||||||
DATABASE_URL,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const UserDetailsQuery = `
|
|
||||||
query($params: PaginatedInput) {
|
|
||||||
_users(params: $params) {
|
|
||||||
pagination {
|
|
||||||
limit
|
|
||||||
page
|
|
||||||
offset
|
|
||||||
total
|
|
||||||
}
|
|
||||||
users {
|
|
||||||
id
|
|
||||||
email
|
|
||||||
email_verified
|
|
||||||
given_name
|
|
||||||
family_name
|
|
||||||
middle_name
|
|
||||||
nickname
|
|
||||||
gender
|
|
||||||
birthdate
|
|
||||||
phone_number
|
|
||||||
picture
|
|
||||||
signup_methods
|
|
||||||
roles
|
|
||||||
created_at
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
@@ -1,10 +1,8 @@
|
|||||||
import { Box, Flex, Image, Text, Spinner } from '@chakra-ui/react';
|
import { Box, Center, Flex, Image, Text } from '@chakra-ui/react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useQuery } from 'urql';
|
import { LOGO_URL } from '../constants';
|
||||||
import { MetaQuery } from '../graphql/queries';
|
|
||||||
|
|
||||||
export function AuthLayout({ children }: { children: React.ReactNode }) {
|
export function AuthLayout({ children }: { children: React.ReactNode }) {
|
||||||
const [{ fetching, data }] = useQuery({ query: MetaQuery });
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
flexWrap="wrap"
|
flexWrap="wrap"
|
||||||
@@ -25,18 +23,9 @@ export function AuthLayout({ children }: { children: React.ReactNode }) {
|
|||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
{fetching ? (
|
<Box p="6" m="5" rounded="5" bg="white" w="500px" shadow="xl">
|
||||||
<Spinner />
|
{children}
|
||||||
) : (
|
</Box>
|
||||||
<>
|
|
||||||
<Box p="6" m="5" rounded="5" bg="white" w="500px" shadow="xl">
|
|
||||||
{children}
|
|
||||||
</Box>
|
|
||||||
<Text color="gray.600" fontSize="sm">
|
|
||||||
Current Version: {data.meta.version}
|
|
||||||
</Text>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,7 @@ import {
|
|||||||
useToast,
|
useToast,
|
||||||
VStack,
|
VStack,
|
||||||
Text,
|
Text,
|
||||||
|
Divider,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { useMutation } from 'urql';
|
import { useMutation } from 'urql';
|
||||||
@@ -72,25 +73,17 @@ export default function Auth() {
|
|||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
mb="2"
|
mb="2"
|
||||||
>
|
>
|
||||||
Hello Admin 👋 <br />
|
Hi there 👋 <br />
|
||||||
</Text>
|
</Text>
|
||||||
<Text fontSize="large" textAlign="center" color="gray.500" mb="8">
|
<Text fontSize="large" textAlign="center" color="gray.500" mb="8">
|
||||||
Welcome to Admin Dashboard
|
Welcome to Authorizer Administrative Dashboard
|
||||||
</Text>
|
</Text>
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<VStack spacing="5" justify="space-between">
|
<VStack spacing="5" justify="space-between">
|
||||||
<FormControl isRequired>
|
<FormControl isRequired>
|
||||||
<FormLabel htmlFor="admin-username">Username</FormLabel>
|
{/* <FormLabel htmlFor="admin-secret">
|
||||||
<Input
|
{isLogin ? 'Enter' : 'Configure'} Admin Secret
|
||||||
size="lg"
|
</FormLabel> */}
|
||||||
id="admin-username"
|
|
||||||
placeholder="Username"
|
|
||||||
disabled
|
|
||||||
value="admin"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormControl isRequired>
|
|
||||||
<FormLabel htmlFor="admin-secret">Password</FormLabel>
|
|
||||||
<Input
|
<Input
|
||||||
size="lg"
|
size="lg"
|
||||||
id="admin-secret"
|
id="admin-secret"
|
||||||
@@ -118,7 +111,10 @@ export default function Auth() {
|
|||||||
</Text>
|
</Text>
|
||||||
) : (
|
) : (
|
||||||
<Text color="gray.600" fontSize="sm">
|
<Text color="gray.600" fontSize="sm">
|
||||||
<b>Note:</b> Configure the password to start using your dashboard.
|
<b>Note:</b> You can also configure admin secret by setting{' '}
|
||||||
|
<code>ADMIN_SECRET</code> environment variable. For more
|
||||||
|
information, please refer to the{' '}
|
||||||
|
<a href="https://docs.authorizer.dev/core/env/">documentation</a>.
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</VStack>
|
</VStack>
|
||||||
|
@@ -1,843 +1,35 @@
|
|||||||
import React, { useEffect } from 'react';
|
import { Box, Divider, Flex } from '@chakra-ui/react';
|
||||||
import {
|
import React from 'react';
|
||||||
Box,
|
|
||||||
Divider,
|
|
||||||
Flex,
|
|
||||||
Stack,
|
|
||||||
Center,
|
|
||||||
Text,
|
|
||||||
Button,
|
|
||||||
Input,
|
|
||||||
InputGroup,
|
|
||||||
InputRightElement,
|
|
||||||
useToast,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { useClient } from 'urql';
|
|
||||||
import {
|
|
||||||
FaGoogle,
|
|
||||||
FaGithub,
|
|
||||||
FaFacebookF,
|
|
||||||
FaSave,
|
|
||||||
FaRegEyeSlash,
|
|
||||||
FaRegEye,
|
|
||||||
} from 'react-icons/fa';
|
|
||||||
import _ from 'lodash';
|
|
||||||
import InputField from '../components/InputField';
|
|
||||||
import { EnvVariablesQuery } from '../graphql/queries';
|
|
||||||
import {
|
|
||||||
ArrayInputType,
|
|
||||||
SelectInputType,
|
|
||||||
HiddenInputType,
|
|
||||||
TextInputType,
|
|
||||||
TextAreaInputType,
|
|
||||||
SwitchInputType,
|
|
||||||
HMACEncryptionType,
|
|
||||||
RSAEncryptionType,
|
|
||||||
ECDSAEncryptionType,
|
|
||||||
} from '../constants';
|
|
||||||
import { UpdateEnvVariables } from '../graphql/mutation';
|
|
||||||
import { getObjectDiff, capitalizeFirstLetter } from '../utils';
|
|
||||||
|
|
||||||
interface envVarTypes {
|
|
||||||
GOOGLE_CLIENT_ID: string;
|
|
||||||
GOOGLE_CLIENT_SECRET: string;
|
|
||||||
GITHUB_CLIENT_ID: string;
|
|
||||||
GITHUB_CLIENT_SECRET: string;
|
|
||||||
FACEBOOK_CLIENT_ID: string;
|
|
||||||
FACEBOOK_CLIENT_SECRET: string;
|
|
||||||
ROLES: [string] | [];
|
|
||||||
DEFAULT_ROLES: [string] | [];
|
|
||||||
PROTECTED_ROLES: [string] | [];
|
|
||||||
JWT_TYPE: string;
|
|
||||||
JWT_SECRET: string;
|
|
||||||
JWT_ROLE_CLAIM: string;
|
|
||||||
JWT_PRIVATE_KEY: string;
|
|
||||||
JWT_PUBLIC_KEY: string;
|
|
||||||
REDIS_URL: string;
|
|
||||||
SMTP_HOST: string;
|
|
||||||
SMTP_PORT: string;
|
|
||||||
SMTP_USERNAME: string;
|
|
||||||
SMTP_PASSWORD: string;
|
|
||||||
SENDER_EMAIL: string;
|
|
||||||
ALLOWED_ORIGINS: [string] | [];
|
|
||||||
ORGANIZATION_NAME: string;
|
|
||||||
ORGANIZATION_LOGO: string;
|
|
||||||
CUSTOM_ACCESS_TOKEN_SCRIPT: string;
|
|
||||||
ADMIN_SECRET: string;
|
|
||||||
DISABLE_LOGIN_PAGE: boolean;
|
|
||||||
DISABLE_MAGIC_LINK_LOGIN: boolean;
|
|
||||||
DISABLE_EMAIL_VERIFICATION: boolean;
|
|
||||||
DISABLE_BASIC_AUTHENTICATION: boolean;
|
|
||||||
OLD_ADMIN_SECRET: string;
|
|
||||||
DATABASE_NAME: string;
|
|
||||||
DATABASE_TYPE: string;
|
|
||||||
DATABASE_URL: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Don't allow changing database from here as it can cause persistence issues
|
||||||
export default function Environment() {
|
export default function Environment() {
|
||||||
const client = useClient();
|
|
||||||
const toast = useToast();
|
|
||||||
const [adminSecret, setAdminSecret] = React.useState<
|
|
||||||
Record<string, string | boolean>
|
|
||||||
>({
|
|
||||||
value: '',
|
|
||||||
disableInputField: true,
|
|
||||||
});
|
|
||||||
const [loading, setLoading] = React.useState<boolean>(true);
|
|
||||||
const [envVariables, setEnvVariables] = React.useState<envVarTypes>({
|
|
||||||
GOOGLE_CLIENT_ID: '',
|
|
||||||
GOOGLE_CLIENT_SECRET: '',
|
|
||||||
GITHUB_CLIENT_ID: '',
|
|
||||||
GITHUB_CLIENT_SECRET: '',
|
|
||||||
FACEBOOK_CLIENT_ID: '',
|
|
||||||
FACEBOOK_CLIENT_SECRET: '',
|
|
||||||
ROLES: [],
|
|
||||||
DEFAULT_ROLES: [],
|
|
||||||
PROTECTED_ROLES: [],
|
|
||||||
JWT_TYPE: '',
|
|
||||||
JWT_SECRET: '',
|
|
||||||
JWT_ROLE_CLAIM: '',
|
|
||||||
JWT_PRIVATE_KEY: '',
|
|
||||||
JWT_PUBLIC_KEY: '',
|
|
||||||
REDIS_URL: '',
|
|
||||||
SMTP_HOST: '',
|
|
||||||
SMTP_PORT: '',
|
|
||||||
SMTP_USERNAME: '',
|
|
||||||
SMTP_PASSWORD: '',
|
|
||||||
SENDER_EMAIL: '',
|
|
||||||
ALLOWED_ORIGINS: [],
|
|
||||||
ORGANIZATION_NAME: '',
|
|
||||||
ORGANIZATION_LOGO: '',
|
|
||||||
CUSTOM_ACCESS_TOKEN_SCRIPT: '',
|
|
||||||
ADMIN_SECRET: '',
|
|
||||||
DISABLE_LOGIN_PAGE: false,
|
|
||||||
DISABLE_MAGIC_LINK_LOGIN: false,
|
|
||||||
DISABLE_EMAIL_VERIFICATION: false,
|
|
||||||
DISABLE_BASIC_AUTHENTICATION: false,
|
|
||||||
OLD_ADMIN_SECRET: '',
|
|
||||||
DATABASE_NAME: '',
|
|
||||||
DATABASE_TYPE: '',
|
|
||||||
DATABASE_URL: '',
|
|
||||||
});
|
|
||||||
|
|
||||||
const [fieldVisibility, setFieldVisibility] = React.useState<
|
|
||||||
Record<string, boolean>
|
|
||||||
>({
|
|
||||||
GOOGLE_CLIENT_SECRET: false,
|
|
||||||
GITHUB_CLIENT_SECRET: false,
|
|
||||||
FACEBOOK_CLIENT_SECRET: false,
|
|
||||||
JWT_SECRET: false,
|
|
||||||
SMTP_PASSWORD: false,
|
|
||||||
ADMIN_SECRET: false,
|
|
||||||
OLD_ADMIN_SECRET: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
let isMounted = true;
|
|
||||||
async function getData() {
|
|
||||||
const {
|
|
||||||
data: { _env: envData },
|
|
||||||
} = await client.query(EnvVariablesQuery).toPromise();
|
|
||||||
|
|
||||||
if (isMounted) {
|
|
||||||
setLoading(false);
|
|
||||||
setEnvVariables({
|
|
||||||
...envData,
|
|
||||||
OLD_ADMIN_SECRET: envData.ADMIN_SECRET,
|
|
||||||
ADMIN_SECRET: '',
|
|
||||||
});
|
|
||||||
setAdminSecret({
|
|
||||||
value: '',
|
|
||||||
disableInputField: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getData();
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
isMounted = false;
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const validateAdminSecretHandler = (event: any) => {
|
|
||||||
if (envVariables.OLD_ADMIN_SECRET === event.target.value) {
|
|
||||||
setAdminSecret({
|
|
||||||
...adminSecret,
|
|
||||||
value: event.target.value,
|
|
||||||
disableInputField: false,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
setAdminSecret({
|
|
||||||
...adminSecret,
|
|
||||||
value: event.target.value,
|
|
||||||
disableInputField: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (envVariables.ADMIN_SECRET !== '') {
|
|
||||||
setEnvVariables({ ...envVariables, ADMIN_SECRET: '' });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const saveHandler = async () => {
|
|
||||||
setLoading(true);
|
|
||||||
const {
|
|
||||||
data: { _env: envData },
|
|
||||||
} = await client.query(EnvVariablesQuery).toPromise();
|
|
||||||
const diff = getObjectDiff(envVariables, envData);
|
|
||||||
const updatedEnvVariables = diff.reduce(
|
|
||||||
(acc: any, property: string) => ({
|
|
||||||
...acc,
|
|
||||||
// @ts-ignore
|
|
||||||
[property]: envVariables[property],
|
|
||||||
}),
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
if (
|
|
||||||
updatedEnvVariables[HiddenInputType.ADMIN_SECRET] === '' ||
|
|
||||||
updatedEnvVariables[HiddenInputType.OLD_ADMIN_SECRET] !==
|
|
||||||
envData.ADMIN_SECRET
|
|
||||||
) {
|
|
||||||
delete updatedEnvVariables.OLD_ADMIN_SECRET;
|
|
||||||
delete updatedEnvVariables.ADMIN_SECRET;
|
|
||||||
}
|
|
||||||
|
|
||||||
delete updatedEnvVariables.DATABASE_URL;
|
|
||||||
delete updatedEnvVariables.DATABASE_TYPE;
|
|
||||||
delete updatedEnvVariables.DATABASE_NAME;
|
|
||||||
|
|
||||||
const res = await client
|
|
||||||
.mutation(UpdateEnvVariables, { params: updatedEnvVariables })
|
|
||||||
.toPromise();
|
|
||||||
|
|
||||||
setLoading(false);
|
|
||||||
|
|
||||||
if (res.error) {
|
|
||||||
toast({
|
|
||||||
title: capitalizeFirstLetter(res.error.message),
|
|
||||||
isClosable: true,
|
|
||||||
status: 'error',
|
|
||||||
position: 'bottom-right',
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setAdminSecret({
|
|
||||||
value: '',
|
|
||||||
disableInputField: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
toast({
|
|
||||||
title: `Successfully updated ${
|
|
||||||
Object.keys(updatedEnvVariables).length
|
|
||||||
} variables`,
|
|
||||||
isClosable: true,
|
|
||||||
status: 'success',
|
|
||||||
position: 'bottom-right',
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box m="5" py="5" px="10" bg="white" rounded="md">
|
<Box m="5" p="5" bg="white" rounded="md">
|
||||||
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
|
<h1>Social Media Logins</h1>
|
||||||
Your instance information
|
<Divider />- Add horizontal input for clientID and secret for - Google -
|
||||||
</Text>
|
Github - Facebook
|
||||||
<Stack spacing={6} padding="2% 0%">
|
<h1>Roles</h1>
|
||||||
<Flex>
|
<Divider />- Add tagged input for roles, default roles, and protected
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
roles
|
||||||
<Text fontSize="sm">Client ID</Text>
|
<h1>JWT Configurations</h1>
|
||||||
</Flex>
|
<Divider />- Add input for JWT Type (keep this disabled for now with
|
||||||
<Center w="70%">
|
notice saying, "More JWT types will be enabled in upcoming releases"),JWT
|
||||||
<InputField
|
secret, JWT role claim
|
||||||
variables={envVariables}
|
<h1>Session Storage</h1>
|
||||||
setVariables={() => {}}
|
<Divider />- Add input for redis url
|
||||||
inputType={TextInputType.CLIENT_ID}
|
<h1>Email Configurations</h1>
|
||||||
placeholder="Client ID"
|
<Divider />- Add input for SMTP Host, PORT, Username, Password, From
|
||||||
isDisabled={true}
|
Email,
|
||||||
/>
|
<h1>White Listing</h1>
|
||||||
</Center>
|
<Divider />- Add input for allowed origins
|
||||||
</Flex>
|
<h1>Organization Information</h1>
|
||||||
<Flex>
|
<Divider />- Add input for organization name, and logo
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
<h1>Custom Scripts</h1>
|
||||||
<Text fontSize="sm">Client Secret</Text>
|
<Divider />- For now add text area input for CUSTOM_ACCESS_TOKEN_SCRIPT
|
||||||
</Flex>
|
<h1>Disable Features</h1>
|
||||||
<Center w="70%">
|
<Divider />
|
||||||
<InputField
|
<h1>Danger</h1>
|
||||||
variables={envVariables}
|
<Divider />- Include changing admin secret
|
||||||
setVariables={setEnvVariables}
|
|
||||||
fieldVisibility={fieldVisibility}
|
|
||||||
setFieldVisibility={setFieldVisibility}
|
|
||||||
inputType={HiddenInputType.CLIENT_SECRET}
|
|
||||||
placeholder="Client Secret"
|
|
||||||
isDisabled={true}
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
</Stack>
|
|
||||||
<Divider marginTop="2%" marginBottom="2%" />
|
|
||||||
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
|
|
||||||
Social Media Logins
|
|
||||||
</Text>
|
|
||||||
<Stack spacing={6} padding="2% 0%">
|
|
||||||
<Flex>
|
|
||||||
<Center
|
|
||||||
w="50px"
|
|
||||||
marginRight="1.5%"
|
|
||||||
border="1px solid #e2e8f0"
|
|
||||||
borderRadius="5px"
|
|
||||||
>
|
|
||||||
<FaGoogle style={{ color: '#8c8c8c' }} />
|
|
||||||
</Center>
|
|
||||||
<Center w="45%" marginRight="1.5%">
|
|
||||||
<InputField
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setEnvVariables}
|
|
||||||
inputType={TextInputType.GOOGLE_CLIENT_ID}
|
|
||||||
placeholder="Google Client ID"
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
<Center w="45%">
|
|
||||||
<InputField
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setEnvVariables}
|
|
||||||
fieldVisibility={fieldVisibility}
|
|
||||||
setFieldVisibility={setFieldVisibility}
|
|
||||||
inputType={HiddenInputType.GOOGLE_CLIENT_SECRET}
|
|
||||||
placeholder="Google Secret"
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
<Flex>
|
|
||||||
<Center
|
|
||||||
w="50px"
|
|
||||||
marginRight="1.5%"
|
|
||||||
border="1px solid #e2e8f0"
|
|
||||||
borderRadius="5px"
|
|
||||||
>
|
|
||||||
<FaGithub style={{ color: '#8c8c8c' }} />
|
|
||||||
</Center>
|
|
||||||
<Center w="45%" marginRight="1.5%">
|
|
||||||
<InputField
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setEnvVariables}
|
|
||||||
inputType={TextInputType.GITHUB_CLIENT_ID}
|
|
||||||
placeholder="Github Client ID"
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
<Center w="45%">
|
|
||||||
<InputField
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setEnvVariables}
|
|
||||||
fieldVisibility={fieldVisibility}
|
|
||||||
setFieldVisibility={setFieldVisibility}
|
|
||||||
inputType={HiddenInputType.GITHUB_CLIENT_SECRET}
|
|
||||||
placeholder="Github Secret"
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
<Flex>
|
|
||||||
<Center
|
|
||||||
w="50px"
|
|
||||||
marginRight="1.5%"
|
|
||||||
border="1px solid #e2e8f0"
|
|
||||||
borderRadius="5px"
|
|
||||||
>
|
|
||||||
<FaFacebookF style={{ color: '#8c8c8c' }} />
|
|
||||||
</Center>
|
|
||||||
<Center w="45%" marginRight="1.5%">
|
|
||||||
<InputField
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setEnvVariables}
|
|
||||||
inputType={TextInputType.FACEBOOK_CLIENT_ID}
|
|
||||||
placeholder="Facebook Client ID"
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
<Center w="45%">
|
|
||||||
<InputField
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setEnvVariables}
|
|
||||||
fieldVisibility={fieldVisibility}
|
|
||||||
setFieldVisibility={setFieldVisibility}
|
|
||||||
inputType={HiddenInputType.FACEBOOK_CLIENT_SECRET}
|
|
||||||
placeholder="Facebook Secret"
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
</Stack>
|
|
||||||
<Divider marginTop="2%" marginBottom="2%" />
|
|
||||||
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
|
|
||||||
Roles
|
|
||||||
</Text>
|
|
||||||
<Stack spacing={6} padding="2% 0%">
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">Roles:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setEnvVariables}
|
|
||||||
inputType={ArrayInputType.ROLES}
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">Default Roles:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setEnvVariables}
|
|
||||||
inputType={ArrayInputType.DEFAULT_ROLES}
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">Protected Roles:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setEnvVariables}
|
|
||||||
inputType={ArrayInputType.PROTECTED_ROLES}
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
</Stack>
|
|
||||||
<Divider marginTop="2%" marginBottom="2%" />
|
|
||||||
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
|
|
||||||
JWT (JSON Web Tokens) Configurations
|
|
||||||
</Text>
|
|
||||||
<Stack spacing={6} padding="2% 0%">
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">JWT Type:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setEnvVariables}
|
|
||||||
inputType={SelectInputType.JWT_TYPE}
|
|
||||||
value={SelectInputType.JWT_TYPE}
|
|
||||||
options={{
|
|
||||||
...HMACEncryptionType,
|
|
||||||
...RSAEncryptionType,
|
|
||||||
...ECDSAEncryptionType,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
{Object.values(HMACEncryptionType).includes(envVariables.JWT_TYPE) ? (
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">JWT Secret</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setEnvVariables}
|
|
||||||
fieldVisibility={fieldVisibility}
|
|
||||||
setFieldVisibility={setFieldVisibility}
|
|
||||||
inputType={HiddenInputType.JWT_SECRET}
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">Public Key</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setEnvVariables}
|
|
||||||
inputType={TextAreaInputType.JWT_PUBLIC_KEY}
|
|
||||||
placeholder="Add public key here"
|
|
||||||
minH="25vh"
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">Private Key</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setEnvVariables}
|
|
||||||
inputType={TextAreaInputType.JWT_PRIVATE_KEY}
|
|
||||||
placeholder="Add private key here"
|
|
||||||
minH="25vh"
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">JWT Role Claim:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setEnvVariables}
|
|
||||||
inputType={TextInputType.JWT_ROLE_CLAIM}
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
</Stack>
|
|
||||||
<Divider marginTop="2%" marginBottom="2%" />
|
|
||||||
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
|
|
||||||
Session Storage
|
|
||||||
</Text>
|
|
||||||
<Stack spacing={6} padding="2% 0%">
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">Redis URL:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setEnvVariables}
|
|
||||||
inputType={TextInputType.REDIS_URL}
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
</Stack>
|
|
||||||
<Divider marginTop="2%" marginBottom="2%" />
|
|
||||||
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
|
|
||||||
Email Configurations
|
|
||||||
</Text>
|
|
||||||
<Stack spacing={6} padding="2% 0%">
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">SMTP Host:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setEnvVariables}
|
|
||||||
inputType={TextInputType.SMTP_HOST}
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">SMTP Port:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setEnvVariables}
|
|
||||||
inputType={TextInputType.SMTP_PORT}
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">SMTP Username:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setEnvVariables}
|
|
||||||
inputType={TextInputType.SMTP_USERNAME}
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">SMTP Password:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setEnvVariables}
|
|
||||||
fieldVisibility={fieldVisibility}
|
|
||||||
setFieldVisibility={setFieldVisibility}
|
|
||||||
inputType={HiddenInputType.SMTP_PASSWORD}
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">From Email:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setEnvVariables}
|
|
||||||
inputType={TextInputType.SENDER_EMAIL}
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
</Stack>
|
|
||||||
<Divider marginTop="2%" marginBottom="2%" />
|
|
||||||
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
|
|
||||||
White Listing
|
|
||||||
</Text>
|
|
||||||
<Stack spacing={6} padding="2% 0%">
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">Allowed Origins:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setEnvVariables}
|
|
||||||
inputType={ArrayInputType.ALLOWED_ORIGINS}
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
</Stack>
|
|
||||||
<Divider marginTop="2%" marginBottom="2%" />
|
|
||||||
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
|
|
||||||
Organization Information
|
|
||||||
</Text>
|
|
||||||
<Stack spacing={6} padding="2% 0%">
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">Organization Name:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setEnvVariables}
|
|
||||||
inputType={TextInputType.ORGANIZATION_NAME}
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">Organization Logo:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setEnvVariables}
|
|
||||||
inputType={TextInputType.ORGANIZATION_LOGO}
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
</Stack>
|
|
||||||
<Divider marginTop="2%" marginBottom="2%" />
|
|
||||||
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
|
|
||||||
Custom Access Token Scripts
|
|
||||||
</Text>
|
|
||||||
<Stack spacing={6} padding="2% 0%">
|
|
||||||
<Flex>
|
|
||||||
<Center w="100%">
|
|
||||||
<InputField
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setEnvVariables}
|
|
||||||
inputType={TextAreaInputType.CUSTOM_ACCESS_TOKEN_SCRIPT}
|
|
||||||
placeholder="Add script here"
|
|
||||||
minH="25vh"
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
</Stack>
|
|
||||||
<Divider marginTop="2%" marginBottom="2%" />
|
|
||||||
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
|
|
||||||
Disable Features
|
|
||||||
</Text>
|
|
||||||
<Stack spacing={6} padding="2% 0%">
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">Disable Login Page:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex justifyContent="start" w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setEnvVariables}
|
|
||||||
inputType={SwitchInputType.DISABLE_LOGIN_PAGE}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">Disable Email Verification:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex justifyContent="start" w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setEnvVariables}
|
|
||||||
inputType={SwitchInputType.DISABLE_EMAIL_VERIFICATION}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">Disable Magic Login Link:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex justifyContent="start" w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setEnvVariables}
|
|
||||||
inputType={SwitchInputType.DISABLE_MAGIC_LINK_LOGIN}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">Disable Basic Authentication:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex justifyContent="start" w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setEnvVariables}
|
|
||||||
inputType={SwitchInputType.DISABLE_BASIC_AUTHENTICATION}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Stack>
|
|
||||||
<Divider marginTop="2%" marginBottom="2%" />
|
|
||||||
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
|
|
||||||
Danger
|
|
||||||
</Text>
|
|
||||||
<Stack
|
|
||||||
spacing={6}
|
|
||||||
padding="0 5%"
|
|
||||||
marginTop="3%"
|
|
||||||
border="1px solid #ff7875"
|
|
||||||
borderRadius="5px"
|
|
||||||
>
|
|
||||||
<Stack spacing={6} padding="3% 0">
|
|
||||||
<Text fontStyle="italic" fontSize="sm" color="gray.600">
|
|
||||||
Note: Database related environment variables cannot be updated from
|
|
||||||
dashboard :(
|
|
||||||
</Text>
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">DataBase Name:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setEnvVariables}
|
|
||||||
inputType={TextInputType.DATABASE_NAME}
|
|
||||||
isDisabled={true}
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">DataBase Type:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setEnvVariables}
|
|
||||||
inputType={TextInputType.DATABASE_TYPE}
|
|
||||||
isDisabled={true}
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
<Flex>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">DataBase URL:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setEnvVariables}
|
|
||||||
inputType={TextInputType.DATABASE_URL}
|
|
||||||
isDisabled={true}
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
</Stack>
|
|
||||||
<Flex marginTop="3%">
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">Old Admin Secret:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center w="70%">
|
|
||||||
<InputGroup size="sm">
|
|
||||||
<Input
|
|
||||||
size="sm"
|
|
||||||
placeholder="Enter Old Admin Secret"
|
|
||||||
value={adminSecret.value as string}
|
|
||||||
onChange={(event: any) => validateAdminSecretHandler(event)}
|
|
||||||
type={
|
|
||||||
!fieldVisibility[HiddenInputType.OLD_ADMIN_SECRET]
|
|
||||||
? 'password'
|
|
||||||
: 'text'
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<InputRightElement
|
|
||||||
right="5px"
|
|
||||||
children={
|
|
||||||
<Flex>
|
|
||||||
{fieldVisibility[HiddenInputType.OLD_ADMIN_SECRET] ? (
|
|
||||||
<Center
|
|
||||||
w="25px"
|
|
||||||
margin="0 1.5%"
|
|
||||||
cursor="pointer"
|
|
||||||
onClick={() =>
|
|
||||||
setFieldVisibility({
|
|
||||||
...fieldVisibility,
|
|
||||||
[HiddenInputType.OLD_ADMIN_SECRET]: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<FaRegEyeSlash color="#bfbfbf" />
|
|
||||||
</Center>
|
|
||||||
) : (
|
|
||||||
<Center
|
|
||||||
w="25px"
|
|
||||||
margin="0 1.5%"
|
|
||||||
cursor="pointer"
|
|
||||||
onClick={() =>
|
|
||||||
setFieldVisibility({
|
|
||||||
...fieldVisibility,
|
|
||||||
[HiddenInputType.OLD_ADMIN_SECRET]: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<FaRegEye color="#bfbfbf" />
|
|
||||||
</Center>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</InputGroup>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
<Flex paddingBottom="3%">
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">New Admin Secret:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center w="70%">
|
|
||||||
<InputField
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setEnvVariables}
|
|
||||||
inputType={HiddenInputType.ADMIN_SECRET}
|
|
||||||
fieldVisibility={fieldVisibility}
|
|
||||||
setFieldVisibility={setFieldVisibility}
|
|
||||||
isDisabled={adminSecret.disableInputField}
|
|
||||||
placeholder="Enter New Admin Secret"
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
</Stack>
|
|
||||||
<Divider marginTop="5%" marginBottom="2%" />
|
|
||||||
<Stack spacing={6} padding="1% 0">
|
|
||||||
<Flex justifyContent="end" alignItems="center">
|
|
||||||
<Button
|
|
||||||
leftIcon={<FaSave />}
|
|
||||||
colorScheme="blue"
|
|
||||||
variant="solid"
|
|
||||||
onClick={saveHandler}
|
|
||||||
isDisabled={loading}
|
|
||||||
>
|
|
||||||
Save
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
</Stack>
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -1,400 +1,6 @@
|
|||||||
|
import { Box } from '@chakra-ui/react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useClient } from 'urql';
|
|
||||||
import dayjs from 'dayjs';
|
|
||||||
import {
|
|
||||||
Box,
|
|
||||||
Flex,
|
|
||||||
IconButton,
|
|
||||||
NumberDecrementStepper,
|
|
||||||
NumberIncrementStepper,
|
|
||||||
NumberInput,
|
|
||||||
NumberInputField,
|
|
||||||
NumberInputStepper,
|
|
||||||
Select,
|
|
||||||
Table,
|
|
||||||
Tag,
|
|
||||||
Tbody,
|
|
||||||
Td,
|
|
||||||
Text,
|
|
||||||
TableCaption,
|
|
||||||
Th,
|
|
||||||
Thead,
|
|
||||||
Tooltip,
|
|
||||||
Tr,
|
|
||||||
Button,
|
|
||||||
Center,
|
|
||||||
Menu,
|
|
||||||
MenuButton,
|
|
||||||
MenuList,
|
|
||||||
MenuItem,
|
|
||||||
useToast,
|
|
||||||
Spinner,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import {
|
|
||||||
FaAngleLeft,
|
|
||||||
FaAngleRight,
|
|
||||||
FaAngleDoubleLeft,
|
|
||||||
FaAngleDoubleRight,
|
|
||||||
FaExclamationCircle,
|
|
||||||
FaAngleDown,
|
|
||||||
} from 'react-icons/fa';
|
|
||||||
import { UserDetailsQuery } from '../graphql/queries';
|
|
||||||
import { UpdateUser } from '../graphql/mutation';
|
|
||||||
import EditUserModal from '../components/EditUserModal';
|
|
||||||
import DeleteUserModal from '../components/DeleteUserModal';
|
|
||||||
|
|
||||||
interface paginationPropTypes {
|
|
||||||
limit: number;
|
|
||||||
page: number;
|
|
||||||
offset: number;
|
|
||||||
total: number;
|
|
||||||
maxPages: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface userDataTypes {
|
|
||||||
id: string;
|
|
||||||
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;
|
|
||||||
signup_methods: string;
|
|
||||||
roles: [string];
|
|
||||||
created_at: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const getMaxPages = (pagination: paginationPropTypes) => {
|
|
||||||
const { limit, total } = pagination;
|
|
||||||
if (total > 1) {
|
|
||||||
return total % limit === 0
|
|
||||||
? total / limit
|
|
||||||
: parseInt(`${total / limit}`) + 1;
|
|
||||||
} else return 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getLimits = (pagination: paginationPropTypes) => {
|
|
||||||
const { total } = pagination;
|
|
||||||
const limits = [5];
|
|
||||||
if (total > 10) {
|
|
||||||
for (let i = 10; i <= total && limits.length <= 10; i += 5) {
|
|
||||||
limits.push(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return limits;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function Users() {
|
export default function Users() {
|
||||||
const client = useClient();
|
return <Box>Welcome to Users Page</Box>;
|
||||||
const toast = useToast();
|
|
||||||
const [paginationProps, setPaginationProps] =
|
|
||||||
React.useState<paginationPropTypes>({
|
|
||||||
limit: 5,
|
|
||||||
page: 1,
|
|
||||||
offset: 0,
|
|
||||||
total: 0,
|
|
||||||
maxPages: 1,
|
|
||||||
});
|
|
||||||
const [userList, setUserList] = React.useState<userDataTypes[]>([]);
|
|
||||||
const [loading, setLoading] = React.useState<boolean>(false);
|
|
||||||
const updateUserList = async () => {
|
|
||||||
setLoading(true);
|
|
||||||
const { data } = await client
|
|
||||||
.query(UserDetailsQuery, {
|
|
||||||
params: {
|
|
||||||
pagination: {
|
|
||||||
limit: paginationProps.limit,
|
|
||||||
page: paginationProps.page,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.toPromise();
|
|
||||||
if (data?._users) {
|
|
||||||
const { pagination, users } = data._users;
|
|
||||||
const maxPages = getMaxPages(pagination);
|
|
||||||
if (users && users.length > 0) {
|
|
||||||
setPaginationProps({ ...paginationProps, ...pagination, maxPages });
|
|
||||||
setUserList(users);
|
|
||||||
} else {
|
|
||||||
if (paginationProps.page !== 1) {
|
|
||||||
setPaginationProps({
|
|
||||||
...paginationProps,
|
|
||||||
...pagination,
|
|
||||||
maxPages,
|
|
||||||
page: 1,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setLoading(false);
|
|
||||||
};
|
|
||||||
React.useEffect(() => {
|
|
||||||
updateUserList();
|
|
||||||
}, []);
|
|
||||||
React.useEffect(() => {
|
|
||||||
updateUserList();
|
|
||||||
}, [paginationProps.page, paginationProps.limit]);
|
|
||||||
|
|
||||||
const paginationHandler = (value: Record<string, number>) => {
|
|
||||||
setPaginationProps({ ...paginationProps, ...value });
|
|
||||||
};
|
|
||||||
|
|
||||||
const userVerificationHandler = async (user: userDataTypes) => {
|
|
||||||
const { id, email } = user;
|
|
||||||
const res = await client
|
|
||||||
.mutation(UpdateUser, {
|
|
||||||
params: {
|
|
||||||
id,
|
|
||||||
email,
|
|
||||||
email_verified: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.toPromise();
|
|
||||||
if (res.error) {
|
|
||||||
toast({
|
|
||||||
title: 'User verification failed',
|
|
||||||
isClosable: true,
|
|
||||||
status: 'error',
|
|
||||||
position: 'bottom-right',
|
|
||||||
});
|
|
||||||
} else if (res.data?._update_user?.id) {
|
|
||||||
toast({
|
|
||||||
title: 'User verification successful',
|
|
||||||
isClosable: true,
|
|
||||||
status: 'success',
|
|
||||||
position: 'bottom-right',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
updateUserList();
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<Box m="5" py="5" px="10" bg="white" rounded="md">
|
|
||||||
<Flex margin="2% 0" justifyContent="space-between" alignItems="center">
|
|
||||||
<Text fontSize="md" fontWeight="bold">
|
|
||||||
Users
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
{!loading ? (
|
|
||||||
userList.length > 0 ? (
|
|
||||||
<Table variant="simple">
|
|
||||||
<Thead>
|
|
||||||
<Tr>
|
|
||||||
<Th>Email</Th>
|
|
||||||
<Th>Created At</Th>
|
|
||||||
<Th>Signup Methods</Th>
|
|
||||||
<Th>Roles</Th>
|
|
||||||
<Th>Verified</Th>
|
|
||||||
<Th>Actions</Th>
|
|
||||||
</Tr>
|
|
||||||
</Thead>
|
|
||||||
<Tbody>
|
|
||||||
{userList.map((user: userDataTypes) => {
|
|
||||||
const { email_verified, created_at, ...rest }: any = user;
|
|
||||||
return (
|
|
||||||
<Tr key={user.id} style={{ fontSize: 14 }}>
|
|
||||||
<Td>{user.email}</Td>
|
|
||||||
<Td>
|
|
||||||
{dayjs(user.created_at * 1000).format('MMM DD, YYYY')}
|
|
||||||
</Td>
|
|
||||||
<Td>{user.signup_methods}</Td>
|
|
||||||
<Td>{user.roles.join(', ')}</Td>
|
|
||||||
<Td>
|
|
||||||
<Tag
|
|
||||||
size="sm"
|
|
||||||
variant="outline"
|
|
||||||
colorScheme={user.email_verified ? 'green' : 'yellow'}
|
|
||||||
>
|
|
||||||
{user.email_verified.toString()}
|
|
||||||
</Tag>
|
|
||||||
</Td>
|
|
||||||
<Td>
|
|
||||||
<Menu>
|
|
||||||
<MenuButton as={Button} variant="unstyled" size="sm">
|
|
||||||
<Flex
|
|
||||||
justifyContent="space-between"
|
|
||||||
alignItems="center"
|
|
||||||
>
|
|
||||||
<Text fontSize="sm" fontWeight="light">
|
|
||||||
Menu
|
|
||||||
</Text>
|
|
||||||
<FaAngleDown style={{ marginLeft: 10 }} />
|
|
||||||
</Flex>
|
|
||||||
</MenuButton>
|
|
||||||
<MenuList>
|
|
||||||
{!user.email_verified && (
|
|
||||||
<MenuItem
|
|
||||||
onClick={() => userVerificationHandler(user)}
|
|
||||||
>
|
|
||||||
Verify User
|
|
||||||
</MenuItem>
|
|
||||||
)}
|
|
||||||
<EditUserModal
|
|
||||||
user={rest}
|
|
||||||
updateUserList={updateUserList}
|
|
||||||
/>
|
|
||||||
<DeleteUserModal
|
|
||||||
user={rest}
|
|
||||||
updateUserList={updateUserList}
|
|
||||||
/>
|
|
||||||
</MenuList>
|
|
||||||
</Menu>
|
|
||||||
</Td>
|
|
||||||
</Tr>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Tbody>
|
|
||||||
{(paginationProps.maxPages > 1 || paginationProps.total >= 5) && (
|
|
||||||
<TableCaption>
|
|
||||||
<Flex
|
|
||||||
justifyContent="space-between"
|
|
||||||
alignItems="center"
|
|
||||||
m="2% 0"
|
|
||||||
>
|
|
||||||
<Flex flex="1">
|
|
||||||
<Tooltip label="First Page">
|
|
||||||
<IconButton
|
|
||||||
aria-label="icon button"
|
|
||||||
onClick={() =>
|
|
||||||
paginationHandler({
|
|
||||||
page: 1,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
isDisabled={paginationProps.page <= 1}
|
|
||||||
mr={4}
|
|
||||||
icon={<FaAngleDoubleLeft />}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip label="Previous Page">
|
|
||||||
<IconButton
|
|
||||||
aria-label="icon button"
|
|
||||||
onClick={() =>
|
|
||||||
paginationHandler({
|
|
||||||
page: paginationProps.page - 1,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
isDisabled={paginationProps.page <= 1}
|
|
||||||
icon={<FaAngleLeft />}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</Flex>
|
|
||||||
<Flex
|
|
||||||
flex="8"
|
|
||||||
justifyContent="space-evenly"
|
|
||||||
alignItems="center"
|
|
||||||
>
|
|
||||||
<Text mr={8}>
|
|
||||||
Page{' '}
|
|
||||||
<Text fontWeight="bold" as="span">
|
|
||||||
{paginationProps.page}
|
|
||||||
</Text>{' '}
|
|
||||||
of{' '}
|
|
||||||
<Text fontWeight="bold" as="span">
|
|
||||||
{paginationProps.maxPages}
|
|
||||||
</Text>
|
|
||||||
</Text>
|
|
||||||
<Flex alignItems="center">
|
|
||||||
<Text flexShrink="0">Go to page:</Text>{' '}
|
|
||||||
<NumberInput
|
|
||||||
ml={2}
|
|
||||||
mr={8}
|
|
||||||
w={28}
|
|
||||||
min={1}
|
|
||||||
max={paginationProps.maxPages}
|
|
||||||
onChange={(value) =>
|
|
||||||
paginationHandler({
|
|
||||||
page: parseInt(value),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
value={paginationProps.page}
|
|
||||||
>
|
|
||||||
<NumberInputField />
|
|
||||||
<NumberInputStepper>
|
|
||||||
<NumberIncrementStepper />
|
|
||||||
<NumberDecrementStepper />
|
|
||||||
</NumberInputStepper>
|
|
||||||
</NumberInput>
|
|
||||||
</Flex>
|
|
||||||
<Select
|
|
||||||
w={32}
|
|
||||||
value={paginationProps.limit}
|
|
||||||
onChange={(e) =>
|
|
||||||
paginationHandler({
|
|
||||||
page: 1,
|
|
||||||
limit: parseInt(e.target.value),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{getLimits(paginationProps).map((pageSize) => (
|
|
||||||
<option key={pageSize} value={pageSize}>
|
|
||||||
Show {pageSize}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</Flex>
|
|
||||||
<Flex flex="1">
|
|
||||||
<Tooltip label="Next Page">
|
|
||||||
<IconButton
|
|
||||||
aria-label="icon button"
|
|
||||||
onClick={() =>
|
|
||||||
paginationHandler({
|
|
||||||
page: paginationProps.page + 1,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
isDisabled={
|
|
||||||
paginationProps.page >= paginationProps.maxPages
|
|
||||||
}
|
|
||||||
icon={<FaAngleRight />}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip label="Last Page">
|
|
||||||
<IconButton
|
|
||||||
aria-label="icon button"
|
|
||||||
onClick={() =>
|
|
||||||
paginationHandler({
|
|
||||||
page: paginationProps.maxPages,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
isDisabled={
|
|
||||||
paginationProps.page >= paginationProps.maxPages
|
|
||||||
}
|
|
||||||
ml={4}
|
|
||||||
icon={<FaAngleDoubleRight />}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</TableCaption>
|
|
||||||
)}
|
|
||||||
</Table>
|
|
||||||
) : (
|
|
||||||
<Flex
|
|
||||||
flexDirection="column"
|
|
||||||
minH="25vh"
|
|
||||||
justifyContent="center"
|
|
||||||
alignItems="center"
|
|
||||||
>
|
|
||||||
<Center w="50px" marginRight="1.5%">
|
|
||||||
<FaExclamationCircle style={{ color: '#f0f0f0', fontSize: 70 }} />
|
|
||||||
</Center>
|
|
||||||
<Text
|
|
||||||
fontSize="2xl"
|
|
||||||
paddingRight="1%"
|
|
||||||
fontWeight="bold"
|
|
||||||
color="#d9d9d9"
|
|
||||||
>
|
|
||||||
No Data
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
)
|
|
||||||
) : (
|
|
||||||
<Center minH="25vh">
|
|
||||||
<Spinner />
|
|
||||||
</Center>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
@@ -23,10 +23,9 @@ export const AppRoutes = () => {
|
|||||||
</DashboardLayout>
|
</DashboardLayout>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Route path="/" element={<Environment />} />
|
<Route path="/" element={<Home />} />
|
||||||
<Route path="users" element={<Users />} />
|
<Route path="users" element={<Users />} />
|
||||||
<Route path="environment" element={<Environment />} />
|
<Route path="environment" element={<Environment />} />
|
||||||
<Route path="*" element={<Home />} />
|
|
||||||
</Route>
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
@@ -36,7 +35,6 @@ export const AppRoutes = () => {
|
|||||||
<Suspense fallback={<></>}>
|
<Suspense fallback={<></>}>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<Auth />} />
|
<Route path="/" element={<Auth />} />
|
||||||
<Route path="*" element={<Auth />} />
|
|
||||||
</Routes>
|
</Routes>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
);
|
);
|
||||||
|
@@ -1,66 +1,6 @@
|
|||||||
import _ from 'lodash';
|
|
||||||
|
|
||||||
export const hasAdminSecret = () => {
|
export const hasAdminSecret = () => {
|
||||||
return (<any>window)['__authorizer__'].isOnboardingCompleted === true;
|
return (<any>window)['__authorizer__'].isOnboardingCompleted === true;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const capitalizeFirstLetter = (data: string): string =>
|
export const capitalizeFirstLetter = (data: string): string =>
|
||||||
data.charAt(0).toUpperCase() + data.slice(1);
|
data.charAt(0).toUpperCase() + data.slice(1);
|
||||||
|
|
||||||
const fallbackCopyTextToClipboard = (text: string) => {
|
|
||||||
const textArea = document.createElement('textarea');
|
|
||||||
|
|
||||||
textArea.value = text;
|
|
||||||
textArea.style.top = '0';
|
|
||||||
textArea.style.left = '0';
|
|
||||||
textArea.style.position = 'fixed';
|
|
||||||
|
|
||||||
document.body.appendChild(textArea);
|
|
||||||
textArea.focus();
|
|
||||||
textArea.select();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const successful = document.execCommand('copy');
|
|
||||||
const msg = successful ? 'successful' : 'unsuccessful';
|
|
||||||
console.log('Fallback: Copying text command was ' + msg);
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Fallback: Oops, unable to copy', err);
|
|
||||||
}
|
|
||||||
document.body.removeChild(textArea);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const copyTextToClipboard = (text: string) => {
|
|
||||||
if (!navigator.clipboard) {
|
|
||||||
fallbackCopyTextToClipboard(text);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
navigator.clipboard.writeText(text).then(
|
|
||||||
() => {
|
|
||||||
console.log('Async: Copying to clipboard was successful!');
|
|
||||||
},
|
|
||||||
(err) => {
|
|
||||||
console.error('Async: Could not copy text: ', err);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getObjectDiff = (obj1: any, obj2: any) => {
|
|
||||||
const diff = Object.keys(obj1).reduce((result, key) => {
|
|
||||||
if (!obj2.hasOwnProperty(key)) {
|
|
||||||
result.push(key);
|
|
||||||
} else if (
|
|
||||||
_.isEqual(obj1[key], obj2[key]) ||
|
|
||||||
(obj1[key] === null && obj2[key] === '') ||
|
|
||||||
(obj1[key] &&
|
|
||||||
Array.isArray(obj1[key]) &&
|
|
||||||
obj1[key].length === 0 &&
|
|
||||||
obj2[key] === null)
|
|
||||||
) {
|
|
||||||
const resultKeyIndex = result.indexOf(key);
|
|
||||||
result.splice(resultKeyIndex, 1);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}, Object.keys(obj2));
|
|
||||||
|
|
||||||
return diff;
|
|
||||||
};
|
|
||||||
|
@@ -67,7 +67,6 @@
|
|||||||
|
|
||||||
/* Advanced Options */
|
/* Advanced Options */
|
||||||
"skipLibCheck": true /* Skip type checking of declaration files. */,
|
"skipLibCheck": true /* Skip type checking of declaration files. */,
|
||||||
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */,
|
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
|
||||||
"lib": ["esnext", "dom"]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -13,8 +13,6 @@ const (
|
|||||||
DbTypeArangodb = "arangodb"
|
DbTypeArangodb = "arangodb"
|
||||||
// DbTypeMongodb is the mongodb database type
|
// DbTypeMongodb is the mongodb database type
|
||||||
DbTypeMongodb = "mongodb"
|
DbTypeMongodb = "mongodb"
|
||||||
// DbTypeYugabyte is the yugabyte database type
|
// DbTypeFaunadb is the faunadb database type
|
||||||
DbTypeYugabyte = "yugabyte"
|
DbTypeFaunadb = "faunadb"
|
||||||
// DbTypeMariaDB is the mariadb database type
|
|
||||||
DbTypeMariaDB = "mariadb"
|
|
||||||
)
|
)
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
package constants
|
package constants
|
||||||
|
|
||||||
var VERSION = "0.0.1"
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Envstore identifier
|
// Envstore identifier
|
||||||
// StringStore string store identifier
|
// StringStore string store identifier
|
||||||
@@ -15,8 +13,9 @@ const (
|
|||||||
EnvKeyEnv = "ENV"
|
EnvKeyEnv = "ENV"
|
||||||
// EnvKeyEnvPath key for cli arg variable ENV_PATH
|
// EnvKeyEnvPath key for cli arg variable ENV_PATH
|
||||||
EnvKeyEnvPath = "ENV_PATH"
|
EnvKeyEnvPath = "ENV_PATH"
|
||||||
|
// EnvKeyVersion key for build arg version
|
||||||
|
EnvKeyVersion = "VERSION"
|
||||||
// EnvKeyAuthorizerURL key for env variable AUTHORIZER_URL
|
// EnvKeyAuthorizerURL key for env variable AUTHORIZER_URL
|
||||||
// TODO: remove support AUTHORIZER_URL env
|
|
||||||
EnvKeyAuthorizerURL = "AUTHORIZER_URL"
|
EnvKeyAuthorizerURL = "AUTHORIZER_URL"
|
||||||
// EnvKeyPort key for env variable PORT
|
// EnvKeyPort key for env variable PORT
|
||||||
EnvKeyPort = "PORT"
|
EnvKeyPort = "PORT"
|
||||||
@@ -43,10 +42,6 @@ const (
|
|||||||
EnvKeyJwtType = "JWT_TYPE"
|
EnvKeyJwtType = "JWT_TYPE"
|
||||||
// EnvKeyJwtSecret key for env variable JWT_SECRET
|
// EnvKeyJwtSecret key for env variable JWT_SECRET
|
||||||
EnvKeyJwtSecret = "JWT_SECRET"
|
EnvKeyJwtSecret = "JWT_SECRET"
|
||||||
// EnvKeyJwtPrivateKey key for env variable JWT_PRIVATE_KEY
|
|
||||||
EnvKeyJwtPrivateKey = "JWT_PRIVATE_KEY"
|
|
||||||
// EnvKeyJwtPublicKey key for env variable JWT_PUBLIC_KEY
|
|
||||||
EnvKeyJwtPublicKey = "JWT_PUBLIC_KEY"
|
|
||||||
// EnvKeyAllowedOrigins key for env variable ALLOWED_ORIGINS
|
// EnvKeyAllowedOrigins key for env variable ALLOWED_ORIGINS
|
||||||
EnvKeyAllowedOrigins = "ALLOWED_ORIGINS"
|
EnvKeyAllowedOrigins = "ALLOWED_ORIGINS"
|
||||||
// EnvKeyAppURL key for env variable APP_URL
|
// EnvKeyAppURL key for env variable APP_URL
|
||||||
@@ -59,6 +54,8 @@ const (
|
|||||||
EnvKeyAdminCookieName = "ADMIN_COOKIE_NAME"
|
EnvKeyAdminCookieName = "ADMIN_COOKIE_NAME"
|
||||||
// EnvKeyResetPasswordURL key for env variable RESET_PASSWORD_URL
|
// EnvKeyResetPasswordURL key for env variable RESET_PASSWORD_URL
|
||||||
EnvKeyResetPasswordURL = "RESET_PASSWORD_URL"
|
EnvKeyResetPasswordURL = "RESET_PASSWORD_URL"
|
||||||
|
// EnvKeyEncryptionKey key for env variable ENCRYPTION_KEY
|
||||||
|
EnvKeyEncryptionKey = "ENCRYPTION_KEY"
|
||||||
// EnvKeyDisableEmailVerification key for env variable DISABLE_EMAIL_VERIFICATION
|
// EnvKeyDisableEmailVerification key for env variable DISABLE_EMAIL_VERIFICATION
|
||||||
EnvKeyDisableEmailVerification = "DISABLE_EMAIL_VERIFICATION"
|
EnvKeyDisableEmailVerification = "DISABLE_EMAIL_VERIFICATION"
|
||||||
// EnvKeyDisableBasicAuthentication key for env variable DISABLE_BASIC_AUTH
|
// EnvKeyDisableBasicAuthentication key for env variable DISABLE_BASIC_AUTH
|
||||||
@@ -91,18 +88,8 @@ const (
|
|||||||
EnvKeyOrganizationName = "ORGANIZATION_NAME"
|
EnvKeyOrganizationName = "ORGANIZATION_NAME"
|
||||||
// EnvKeyOrganizationLogo key for env variable ORGANIZATION_LOGO
|
// EnvKeyOrganizationLogo key for env variable ORGANIZATION_LOGO
|
||||||
EnvKeyOrganizationLogo = "ORGANIZATION_LOGO"
|
EnvKeyOrganizationLogo = "ORGANIZATION_LOGO"
|
||||||
// EnvKeyCustomAccessTokenScript key for env variable CUSTOM_ACCESS_TOKEN_SCRIPT
|
|
||||||
EnvKeyCustomAccessTokenScript = "CUSTOM_ACCESS_TOKEN_SCRIPT"
|
|
||||||
|
|
||||||
// Not Exposed Keys
|
|
||||||
// EnvKeyClientID key for env variable CLIENT_ID
|
|
||||||
EnvKeyClientID = "CLIENT_ID"
|
|
||||||
// EnvKeyClientSecret key for env variable CLIENT_SECRET
|
|
||||||
EnvKeyClientSecret = "CLIENT_SECRET"
|
|
||||||
// EnvKeyEncryptionKey key for env variable ENCRYPTION_KEY
|
|
||||||
EnvKeyEncryptionKey = "ENCRYPTION_KEY"
|
|
||||||
// EnvKeyJWK key for env variable JWK
|
|
||||||
EnvKeyJWK = "JWK"
|
|
||||||
// EnvKeyIsProd key for env variable IS_PROD
|
// EnvKeyIsProd key for env variable IS_PROD
|
||||||
EnvKeyIsProd = "IS_PROD"
|
EnvKeyIsProd = "IS_PROD"
|
||||||
|
// EnvKeyCustomAccessTokenScript key for env variable CUSTOM_ACCESS_TOKEN_SCRIPT
|
||||||
|
EnvKeyCustomAccessTokenScript = "CUSTOM_ACCESS_TOKEN_SCRIPT"
|
||||||
)
|
)
|
||||||
|
@@ -1,4 +0,0 @@
|
|||||||
package constants
|
|
||||||
|
|
||||||
// DefaultLimit is the default limit for pagination
|
|
||||||
var DefaultLimit = 10
|
|
@@ -5,6 +5,4 @@ const (
|
|||||||
TokenTypeRefreshToken = "refresh_token"
|
TokenTypeRefreshToken = "refresh_token"
|
||||||
// TokenTypeAccessToken is the access_token token type
|
// TokenTypeAccessToken is the access_token token type
|
||||||
TokenTypeAccessToken = "access_token"
|
TokenTypeAccessToken = "access_token"
|
||||||
// TokenTypeIdentityToken is the identity_token token type
|
|
||||||
TokenTypeIdentityToken = "id_token"
|
|
||||||
)
|
)
|
||||||
|
@@ -13,15 +13,14 @@ import (
|
|||||||
func SetAdminCookie(gc *gin.Context, token string) {
|
func SetAdminCookie(gc *gin.Context, token string) {
|
||||||
secure := true
|
secure := true
|
||||||
httpOnly := true
|
httpOnly := true
|
||||||
hostname := utils.GetHost(gc)
|
host, _ := utils.GetHostParts(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL))
|
||||||
host, _ := utils.GetHostParts(hostname)
|
|
||||||
|
|
||||||
gc.SetCookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminCookieName), token, 3600, "/", host, secure, httpOnly)
|
gc.SetCookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminCookieName), token, 3600, "/", host, secure, httpOnly)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAdminCookie gets the admin cookie from the request
|
// GetAdminCookie gets the admin cookie from the request
|
||||||
func GetAdminCookie(gc *gin.Context) (string, error) {
|
func GetAdminCookie(gc *gin.Context) (string, error) {
|
||||||
cookie, err := gc.Request.Cookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminCookieName))
|
cookie, err := gc.Request.Cookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminCookieName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -39,8 +38,7 @@ func GetAdminCookie(gc *gin.Context) (string, error) {
|
|||||||
func DeleteAdminCookie(gc *gin.Context) {
|
func DeleteAdminCookie(gc *gin.Context) {
|
||||||
secure := true
|
secure := true
|
||||||
httpOnly := true
|
httpOnly := true
|
||||||
hostname := utils.GetHost(gc)
|
host, _ := utils.GetHostParts(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL))
|
||||||
host, _ := utils.GetHostParts(hostname)
|
|
||||||
|
|
||||||
gc.SetCookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminCookieName), "", -1, "/", host, secure, httpOnly)
|
gc.SetCookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminCookieName), "", -1, "/", host, secure, httpOnly)
|
||||||
}
|
}
|
||||||
|
@@ -10,56 +10,92 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SetSession sets the session cookie in the response
|
// SetCookie sets the cookie in the response. It sets 4 cookies
|
||||||
func SetSession(gc *gin.Context, sessionID string) {
|
// 1 COOKIE_NAME.access_token jwt token for the host (temp.abc.com)
|
||||||
|
// 2 COOKIE_NAME.access_token.domain jwt token for the domain (abc.com).
|
||||||
|
// 3 COOKIE_NAME.fingerprint fingerprint hash for the refresh token verification.
|
||||||
|
// 4 COOKIE_NAME.refresh_token refresh token
|
||||||
|
// Note all sites don't allow 2nd type of cookie
|
||||||
|
func SetCookie(gc *gin.Context, accessToken, refreshToken, fingerprintHash string) {
|
||||||
secure := true
|
secure := true
|
||||||
httpOnly := true
|
httpOnly := true
|
||||||
hostname := utils.GetHost(gc)
|
host, _ := utils.GetHostParts(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL))
|
||||||
host, _ := utils.GetHostParts(hostname)
|
domain := utils.GetDomainName(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL))
|
||||||
domain := utils.GetDomainName(hostname)
|
|
||||||
if domain != "localhost" {
|
if domain != "localhost" {
|
||||||
domain = "." + domain
|
domain = "." + domain
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO allow configuring from dashboard
|
|
||||||
year := 60 * 60 * 24 * 365
|
year := 60 * 60 * 24 * 365
|
||||||
|
thirtyMin := 60 * 30
|
||||||
|
|
||||||
gc.SetSameSite(http.SameSiteNoneMode)
|
gc.SetSameSite(http.SameSiteNoneMode)
|
||||||
gc.SetCookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+"_session", sessionID, year, "/", host, secure, httpOnly)
|
// set cookie for host
|
||||||
gc.SetCookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+"_session_domain", sessionID, year, "/", domain, secure, httpOnly)
|
gc.SetCookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".access_token", accessToken, thirtyMin, "/", host, secure, httpOnly)
|
||||||
|
|
||||||
|
// in case of subdomain, set cookie for domain
|
||||||
|
gc.SetCookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".access_token.domain", accessToken, thirtyMin, "/", domain, secure, httpOnly)
|
||||||
|
|
||||||
|
// set finger print cookie (this should be accessed via cookie only)
|
||||||
|
gc.SetCookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".fingerprint", fingerprintHash, year, "/", host, secure, httpOnly)
|
||||||
|
|
||||||
|
// set refresh token cookie (this should be accessed via cookie only)
|
||||||
|
gc.SetCookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".refresh_token", refreshToken, year, "/", host, secure, httpOnly)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteSession sets session cookies to expire
|
// GetAccessTokenCookie to get access token cookie from the request
|
||||||
func DeleteSession(gc *gin.Context) {
|
func GetAccessTokenCookie(gc *gin.Context) (string, error) {
|
||||||
secure := true
|
cookie, err := gc.Request.Cookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName) + ".access_token")
|
||||||
httpOnly := true
|
|
||||||
hostname := utils.GetHost(gc)
|
|
||||||
host, _ := utils.GetHostParts(hostname)
|
|
||||||
domain := utils.GetDomainName(hostname)
|
|
||||||
if domain != "localhost" {
|
|
||||||
domain = "." + domain
|
|
||||||
}
|
|
||||||
|
|
||||||
gc.SetSameSite(http.SameSiteNoneMode)
|
|
||||||
gc.SetCookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+"_session", "", -1, "/", host, secure, httpOnly)
|
|
||||||
gc.SetCookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+"_session_domain", "", -1, "/", domain, secure, httpOnly)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSession gets the session cookie from context
|
|
||||||
func GetSession(gc *gin.Context) (string, error) {
|
|
||||||
var cookie *http.Cookie
|
|
||||||
var err error
|
|
||||||
cookie, err = gc.Request.Cookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName) + "_session")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cookie, err = gc.Request.Cookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName) + "_session_domain")
|
cookie, err = gc.Request.Cookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName) + ".access_token.domain")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
decodedValue, err := url.PathUnescape(cookie.Value)
|
return cookie.Value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRefreshTokenCookie to get refresh token cookie
|
||||||
|
func GetRefreshTokenCookie(gc *gin.Context) (string, error) {
|
||||||
|
cookie, err := gc.Request.Cookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName) + ".refresh_token")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return cookie.Value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFingerPrintCookie to get fingerprint cookie
|
||||||
|
func GetFingerPrintCookie(gc *gin.Context) (string, error) {
|
||||||
|
cookie, err := gc.Request.Cookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName) + ".fingerprint")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// cookie escapes special characters like $
|
||||||
|
// hence we need to unescape before comparing
|
||||||
|
decodedValue, err := url.QueryUnescape(cookie.Value)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
return decodedValue, nil
|
return decodedValue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteCookie sets response cookies to expire
|
||||||
|
func DeleteCookie(gc *gin.Context) {
|
||||||
|
secure := true
|
||||||
|
httpOnly := true
|
||||||
|
|
||||||
|
host, _ := utils.GetHostParts(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL))
|
||||||
|
domain := utils.GetDomainName(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL))
|
||||||
|
if domain != "localhost" {
|
||||||
|
domain = "." + domain
|
||||||
|
}
|
||||||
|
|
||||||
|
gc.SetSameSite(http.SameSiteNoneMode)
|
||||||
|
gc.SetCookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".access_token", "", -1, "/", host, secure, httpOnly)
|
||||||
|
gc.SetCookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".access_token.domain", "", -1, "/", domain, secure, httpOnly)
|
||||||
|
gc.SetCookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".fingerprint", "", -1, "/", host, secure, httpOnly)
|
||||||
|
gc.SetCookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".refresh_token", "", -1, "/", host, secure, httpOnly)
|
||||||
|
}
|
||||||
|
@@ -1,17 +0,0 @@
|
|||||||
package crypto
|
|
||||||
|
|
||||||
import "encoding/base64"
|
|
||||||
|
|
||||||
// EncryptB64 encrypts data into base64 string
|
|
||||||
func EncryptB64(text string) string {
|
|
||||||
return base64.StdEncoding.EncodeToString([]byte(text))
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecryptB64 decrypts from base64 string to readable string
|
|
||||||
func DecryptB64(s string) (string, error) {
|
|
||||||
data, err := base64.StdEncoding.DecodeString(s)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return string(data), nil
|
|
||||||
}
|
|
@@ -1,114 +0,0 @@
|
|||||||
package crypto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/x509"
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
|
||||||
"github.com/authorizerdev/authorizer/server/envstore"
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
|
||||||
"gopkg.in/square/go-jose.v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetPubJWK returns JWK for given keys
|
|
||||||
func GetPubJWK(algo, keyID string, publicKey interface{}) (string, error) {
|
|
||||||
jwk := &jose.JSONWebKeySet{
|
|
||||||
Keys: []jose.JSONWebKey{
|
|
||||||
{
|
|
||||||
Algorithm: algo,
|
|
||||||
Key: publicKey,
|
|
||||||
Use: "sig",
|
|
||||||
KeyID: keyID,
|
|
||||||
Certificates: []*x509.Certificate{},
|
|
||||||
CertificateThumbprintSHA1: []uint8{},
|
|
||||||
CertificateThumbprintSHA256: []uint8{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
jwkPublicKey, err := jwk.Keys[0].MarshalJSON()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return string(jwkPublicKey), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenerateJWKBasedOnEnv generates JWK based on env
|
|
||||||
// make sure clientID, jwtType, jwtSecret / public & private key pair is set
|
|
||||||
// this is called while initializing app / when env is updated
|
|
||||||
func GenerateJWKBasedOnEnv() (string, error) {
|
|
||||||
jwk := ""
|
|
||||||
algo := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtType)
|
|
||||||
clientID := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyClientID)
|
|
||||||
|
|
||||||
var err error
|
|
||||||
// check if jwt secret is provided
|
|
||||||
if IsHMACA(algo) {
|
|
||||||
jwk, err = GetPubJWK(algo, clientID, []byte(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtSecret)))
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if IsRSA(algo) {
|
|
||||||
publicKeyInstance, err := ParseRsaPublicKeyFromPemStr(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtPublicKey))
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
jwk, err = GetPubJWK(algo, clientID, publicKeyInstance)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if IsECDSA(algo) {
|
|
||||||
publicKeyInstance, err := ParseEcdsaPublicKeyFromPemStr(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtPublicKey))
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
jwk, err = GetPubJWK(algo, clientID, publicKeyInstance)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return jwk, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncryptEnvData is used to encrypt the env data
|
|
||||||
func EncryptEnvData(data envstore.Store) (string, error) {
|
|
||||||
jsonBytes, err := json.Marshal(data)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
storeData := envstore.EnvStoreObj.GetEnvStoreClone()
|
|
||||||
|
|
||||||
err = json.Unmarshal(jsonBytes, &storeData)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
configData, err := json.Marshal(storeData)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
encryptedConfig, err := EncryptAESEnv(configData)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return EncryptB64(string(encryptedConfig)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncryptPassword is used for encrypting password
|
|
||||||
func EncryptPassword(password string) (string, error) {
|
|
||||||
pw, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(pw), nil
|
|
||||||
}
|
|
@@ -1,154 +0,0 @@
|
|||||||
package crypto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/elliptic"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/x509"
|
|
||||||
"encoding/pem"
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewECDSAKey to generate new ECDSA Key if env is not set
|
|
||||||
// returns key instance, private key string, public key string, jwk string, error
|
|
||||||
func NewECDSAKey(algo, keyID string) (*ecdsa.PrivateKey, string, string, string, error) {
|
|
||||||
var curve elliptic.Curve
|
|
||||||
switch algo {
|
|
||||||
case "ES256":
|
|
||||||
curve = elliptic.P256()
|
|
||||||
case "ES384":
|
|
||||||
curve = elliptic.P384()
|
|
||||||
case "ES512":
|
|
||||||
curve = elliptic.P521()
|
|
||||||
default:
|
|
||||||
return nil, "", "", "", errors.New("Invalid algo")
|
|
||||||
}
|
|
||||||
key, err := ecdsa.GenerateKey(curve, rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
privateKey, publicKey, err := AsECDSAStr(key, &key.PublicKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
jwkPublicKey, err := GetPubJWK(algo, keyID, &key.PublicKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return key, privateKey, publicKey, string(jwkPublicKey), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsECDSA checks if given string is valid ECDSA algo
|
|
||||||
func IsECDSA(algo string) bool {
|
|
||||||
switch algo {
|
|
||||||
case "ES256", "ES384", "ES512":
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExportEcdsaPrivateKeyAsPemStr to get ECDSA private key as pem string
|
|
||||||
func ExportEcdsaPrivateKeyAsPemStr(privkey *ecdsa.PrivateKey) (string, error) {
|
|
||||||
privkeyBytes, err := x509.MarshalECPrivateKey(privkey)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
privkeyPem := pem.EncodeToMemory(
|
|
||||||
&pem.Block{
|
|
||||||
Type: "ECDSA PRIVATE KEY",
|
|
||||||
Bytes: privkeyBytes,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
return string(privkeyPem), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExportEcdsaPublicKeyAsPemStr to get ECDSA public key as pem string
|
|
||||||
func ExportEcdsaPublicKeyAsPemStr(pubkey *ecdsa.PublicKey) (string, error) {
|
|
||||||
pubkeyBytes, err := x509.MarshalPKIXPublicKey(pubkey)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
pubkeyPem := pem.EncodeToMemory(
|
|
||||||
&pem.Block{
|
|
||||||
Type: "ECDSA PUBLIC KEY",
|
|
||||||
Bytes: pubkeyBytes,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
return string(pubkeyPem), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseEcdsaPrivateKeyFromPemStr to parse ECDSA private key from pem string
|
|
||||||
func ParseEcdsaPrivateKeyFromPemStr(privPEM string) (*ecdsa.PrivateKey, error) {
|
|
||||||
block, _ := pem.Decode([]byte(privPEM))
|
|
||||||
if block == nil {
|
|
||||||
return nil, errors.New("failed to parse PEM block containing the key")
|
|
||||||
}
|
|
||||||
|
|
||||||
priv, err := x509.ParseECPrivateKey(block.Bytes)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return priv, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseEcdsaPublicKeyFromPemStr to parse ECDSA public key from pem string
|
|
||||||
func ParseEcdsaPublicKeyFromPemStr(pubPEM string) (*ecdsa.PublicKey, error) {
|
|
||||||
block, _ := pem.Decode([]byte(pubPEM))
|
|
||||||
if block == nil {
|
|
||||||
return nil, errors.New("failed to parse PEM block containing the key")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch pub := pub.(type) {
|
|
||||||
case *ecdsa.PublicKey:
|
|
||||||
return pub, nil
|
|
||||||
default:
|
|
||||||
break // fall through
|
|
||||||
}
|
|
||||||
return nil, errors.New("Key type is not ECDSA")
|
|
||||||
}
|
|
||||||
|
|
||||||
// AsECDSAStr returns private, public key string or error
|
|
||||||
func AsECDSAStr(privateKey *ecdsa.PrivateKey, publicKey *ecdsa.PublicKey) (string, string, error) {
|
|
||||||
// Export the keys to pem string
|
|
||||||
privPem, err := ExportEcdsaPrivateKeyAsPemStr(privateKey)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
pubPem, err := ExportEcdsaPublicKeyAsPemStr(publicKey)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Import the keys from pem string
|
|
||||||
privParsed, err := ParseEcdsaPrivateKeyFromPemStr(privPem)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
pubParsed, err := ParseEcdsaPublicKeyFromPemStr(pubPem)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export the newly imported keys
|
|
||||||
privParsedPem, err := ExportEcdsaPrivateKeyAsPemStr(privParsed)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
pubParsedPem, err := ExportEcdsaPublicKeyAsPemStr(pubParsed)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return privParsedPem, pubParsedPem, nil
|
|
||||||
}
|
|
@@ -1,26 +0,0 @@
|
|||||||
package crypto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewHMAC key returns new key that can be used to ecnrypt data using HMAC algo
|
|
||||||
// returns key, string, error
|
|
||||||
func NewHMACKey(algo, keyID string) (string, string, error) {
|
|
||||||
key := uuid.New().String()
|
|
||||||
jwkPublicKey, err := GetPubJWK(algo, keyID, []byte(key))
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
return key, string(jwkPublicKey), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsHMACValid checks if given string is valid HMCA algo
|
|
||||||
func IsHMACA(algo string) bool {
|
|
||||||
switch algo {
|
|
||||||
case "HS256", "HS384", "HS512":
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,118 +0,0 @@
|
|||||||
package crypto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/rsa"
|
|
||||||
"crypto/x509"
|
|
||||||
"encoding/pem"
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewRSAKey to generate new RSA Key if env is not set
|
|
||||||
// returns key instance, private key string, public key string, jwk string, error
|
|
||||||
func NewRSAKey(algo, keyID string) (*rsa.PrivateKey, string, string, string, error) {
|
|
||||||
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
privateKey, publicKey, err := AsRSAStr(key, &key.PublicKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
jwkPublicKey, err := GetPubJWK(algo, keyID, &key.PublicKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return key, privateKey, publicKey, string(jwkPublicKey), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsRSA checks if given string is valid RSA algo
|
|
||||||
func IsRSA(algo string) bool {
|
|
||||||
switch algo {
|
|
||||||
case "RS256", "RS384", "RS512":
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExportRsaPrivateKeyAsPemStr to get RSA private key as pem string
|
|
||||||
func ExportRsaPrivateKeyAsPemStr(privkey *rsa.PrivateKey) string {
|
|
||||||
privkeyBytes := x509.MarshalPKCS1PrivateKey(privkey)
|
|
||||||
privkeyPem := pem.EncodeToMemory(
|
|
||||||
&pem.Block{
|
|
||||||
Type: "RSA PRIVATE KEY",
|
|
||||||
Bytes: privkeyBytes,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
return string(privkeyPem)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExportRsaPublicKeyAsPemStr to get RSA public key as pem string
|
|
||||||
func ExportRsaPublicKeyAsPemStr(pubkey *rsa.PublicKey) string {
|
|
||||||
pubkeyBytes := x509.MarshalPKCS1PublicKey(pubkey)
|
|
||||||
pubkeyPem := pem.EncodeToMemory(
|
|
||||||
&pem.Block{
|
|
||||||
Type: "RSA PUBLIC KEY",
|
|
||||||
Bytes: pubkeyBytes,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
return string(pubkeyPem)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseRsaPrivateKeyFromPemStr to parse RSA private key from pem string
|
|
||||||
func ParseRsaPrivateKeyFromPemStr(privPEM string) (*rsa.PrivateKey, error) {
|
|
||||||
block, _ := pem.Decode([]byte(privPEM))
|
|
||||||
if block == nil {
|
|
||||||
return nil, errors.New("failed to parse PEM block containing the key")
|
|
||||||
}
|
|
||||||
|
|
||||||
priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return priv, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseRsaPublicKeyFromPemStr to parse RSA public key from pem string
|
|
||||||
func ParseRsaPublicKeyFromPemStr(pubPEM string) (*rsa.PublicKey, error) {
|
|
||||||
block, _ := pem.Decode([]byte(pubPEM))
|
|
||||||
if block == nil {
|
|
||||||
return nil, errors.New("failed to parse PEM block containing the key")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub, err := x509.ParsePKCS1PublicKey(block.Bytes)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return pub, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AsRSAStr returns private, public key string or error
|
|
||||||
func AsRSAStr(privateKey *rsa.PrivateKey, publickKey *rsa.PublicKey) (string, string, error) {
|
|
||||||
// Export the keys to pem string
|
|
||||||
privPem := ExportRsaPrivateKeyAsPemStr(privateKey)
|
|
||||||
pubPem := ExportRsaPublicKeyAsPemStr(publickKey)
|
|
||||||
|
|
||||||
// Import the keys from pem string
|
|
||||||
privParsed, err := ParseRsaPrivateKeyFromPemStr(privPem)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
pubParsed, err := ParseRsaPublicKeyFromPemStr(pubPem)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export the newly imported keys
|
|
||||||
privParsedPem := ExportRsaPrivateKeyAsPemStr(privParsed)
|
|
||||||
pubParsedPem := ExportRsaPublicKeyAsPemStr(pubParsed)
|
|
||||||
|
|
||||||
return privParsedPem, pubParsedPem, nil
|
|
||||||
}
|
|
@@ -1,9 +1,12 @@
|
|||||||
package db
|
package db
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/db/providers"
|
"github.com/authorizerdev/authorizer/server/db/providers"
|
||||||
"github.com/authorizerdev/authorizer/server/db/providers/arangodb"
|
"github.com/authorizerdev/authorizer/server/db/providers/arangodb"
|
||||||
|
"github.com/authorizerdev/authorizer/server/db/providers/faunadb"
|
||||||
"github.com/authorizerdev/authorizer/server/db/providers/mongodb"
|
"github.com/authorizerdev/authorizer/server/db/providers/mongodb"
|
||||||
"github.com/authorizerdev/authorizer/server/db/providers/sql"
|
"github.com/authorizerdev/authorizer/server/db/providers/sql"
|
||||||
"github.com/authorizerdev/authorizer/server/envstore"
|
"github.com/authorizerdev/authorizer/server/envstore"
|
||||||
@@ -12,33 +15,39 @@ import (
|
|||||||
// Provider returns the current database provider
|
// Provider returns the current database provider
|
||||||
var Provider providers.Provider
|
var Provider providers.Provider
|
||||||
|
|
||||||
func InitDB() error {
|
func InitDB() {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
isSQL := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) != constants.DbTypeArangodb && envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) != constants.DbTypeMongodb
|
isSQL := envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) != constants.DbTypeArangodb && envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) != constants.DbTypeMongodb && envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) != constants.DbTypeFaunadb
|
||||||
isArangoDB := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) == constants.DbTypeArangodb
|
isArangoDB := envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) == constants.DbTypeArangodb
|
||||||
isMongoDB := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) == constants.DbTypeMongodb
|
isMongoDB := envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) == constants.DbTypeMongodb
|
||||||
|
isFaunaDB := envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) == constants.DbTypeFaunadb
|
||||||
|
|
||||||
if isSQL {
|
if isSQL {
|
||||||
Provider, err = sql.NewProvider()
|
Provider, err = sql.NewProvider()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
log.Fatal("=> error setting sql provider:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if isArangoDB {
|
if isArangoDB {
|
||||||
Provider, err = arangodb.NewProvider()
|
Provider, err = arangodb.NewProvider()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
log.Fatal("=> error setting arangodb provider:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if isMongoDB {
|
if isMongoDB {
|
||||||
Provider, err = mongodb.NewProvider()
|
Provider, err = mongodb.NewProvider()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
log.Fatal("=> error setting arangodb provider:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
if isFaunaDB {
|
||||||
|
Provider, err = faunadb.NewProvider()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("=> error setting arangodb provider:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,8 +4,8 @@ package models
|
|||||||
type Env struct {
|
type Env struct {
|
||||||
Key string `json:"_key,omitempty" bson:"_key"` // for arangodb
|
Key string `json:"_key,omitempty" bson:"_key"` // for arangodb
|
||||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id"`
|
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id"`
|
||||||
EnvData string `gorm:"type:text" json:"env" bson:"env"`
|
EnvData []byte `gorm:"type:text" json:"env" bson:"env"`
|
||||||
Hash string `gorm:"type:text" json:"hash" bson:"hash"`
|
Hash string `gorm:"type:hash" json:"hash" bson:"hash"`
|
||||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at"`
|
UpdatedAt int64 `gorm:"autoUpdateTime" json:"updated_at" bson:"updated_at"`
|
||||||
CreatedAt int64 `json:"created_at" bson:"created_at"`
|
CreatedAt int64 `gorm:"autoCreateTime" json:"created_at" bson:"created_at"`
|
||||||
}
|
}
|
||||||
|
@@ -5,9 +5,9 @@ type Session struct {
|
|||||||
Key string `json:"_key,omitempty" bson:"_key,omitempty"` // for arangodb
|
Key string `json:"_key,omitempty" bson:"_key,omitempty"` // for arangodb
|
||||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id"`
|
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id"`
|
||||||
UserID string `gorm:"type:char(36),index:" json:"user_id" bson:"user_id"`
|
UserID string `gorm:"type:char(36),index:" json:"user_id" bson:"user_id"`
|
||||||
User User `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" bson:"-"`
|
User User `json:"-" bson:"-"`
|
||||||
UserAgent string `json:"user_agent" bson:"user_agent"`
|
UserAgent string `json:"user_agent" bson:"user_agent"`
|
||||||
IP string `json:"ip" bson:"ip"`
|
IP string `json:"ip" bson:"ip"`
|
||||||
CreatedAt int64 `json:"created_at" bson:"created_at"`
|
CreatedAt int64 `gorm:"autoCreateTime" json:"created_at" bson:"created_at"`
|
||||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at"`
|
UpdatedAt int64 `gorm:"autoUpdateTime" json:"updated_at" bson:"updated_at"`
|
||||||
}
|
}
|
||||||
|
@@ -25,8 +25,8 @@ type User struct {
|
|||||||
PhoneNumberVerifiedAt *int64 `json:"phone_number_verified_at" bson:"phone_number_verified_at"`
|
PhoneNumberVerifiedAt *int64 `json:"phone_number_verified_at" bson:"phone_number_verified_at"`
|
||||||
Picture *string `gorm:"type:text" json:"picture" bson:"picture"`
|
Picture *string `gorm:"type:text" json:"picture" bson:"picture"`
|
||||||
Roles string `json:"roles" bson:"roles"`
|
Roles string `json:"roles" bson:"roles"`
|
||||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at"`
|
UpdatedAt int64 `gorm:"autoUpdateTime" json:"updated_at" bson:"updated_at"`
|
||||||
CreatedAt int64 `json:"created_at" bson:"created_at"`
|
CreatedAt int64 `gorm:"autoCreateTime" json:"created_at" bson:"created_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) AsAPIUser() *model.User {
|
func (user *User) AsAPIUser() *model.User {
|
||||||
|
@@ -1,31 +1,13 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import "github.com/authorizerdev/authorizer/server/graph/model"
|
|
||||||
|
|
||||||
// VerificationRequest model for db
|
// VerificationRequest model for db
|
||||||
type VerificationRequest struct {
|
type VerificationRequest struct {
|
||||||
Key string `json:"_key,omitempty" bson:"_key"` // for arangodb
|
Key string `json:"_key,omitempty" bson:"_key"` // for arangodb
|
||||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id"`
|
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id"`
|
||||||
Token string `gorm:"type:text" json:"token" bson:"token"`
|
Token string `gorm:"type:text" json:"token" bson:"token"`
|
||||||
Identifier string `gorm:"uniqueIndex:idx_email_identifier" json:"identifier" bson:"identifier"`
|
Identifier string `gorm:"uniqueIndex:idx_email_identifier" json:"identifier" bson:"identifier"`
|
||||||
ExpiresAt int64 `json:"expires_at" bson:"expires_at"`
|
ExpiresAt int64 `json:"expires_at" bson:"expires_at"`
|
||||||
CreatedAt int64 `json:"created_at" bson:"created_at"`
|
CreatedAt int64 `gorm:"autoCreateTime" json:"created_at" bson:"created_at"`
|
||||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at"`
|
UpdatedAt int64 `gorm:"autoUpdateTime" json:"updated_at" bson:"updated_at"`
|
||||||
Email string `gorm:"uniqueIndex:idx_email_identifier" json:"email" bson:"email"`
|
Email string `gorm:"uniqueIndex:idx_email_identifier" json:"email" bson:"email"`
|
||||||
Nonce string `gorm:"type:text" json:"nonce" bson:"nonce"`
|
|
||||||
RedirectURI string `gorm:"type:text" json:"redirect_uri" bson:"redirect_uri"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *VerificationRequest) AsAPIVerificationRequest() *model.VerificationRequest {
|
|
||||||
return &model.VerificationRequest{
|
|
||||||
ID: v.ID,
|
|
||||||
Token: &v.Token,
|
|
||||||
Identifier: &v.Identifier,
|
|
||||||
Expires: &v.ExpiresAt,
|
|
||||||
CreatedAt: &v.CreatedAt,
|
|
||||||
UpdatedAt: &v.UpdatedAt,
|
|
||||||
Email: &v.Email,
|
|
||||||
Nonce: &v.Nonce,
|
|
||||||
RedirectURI: &v.RedirectURI,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -24,7 +24,7 @@ type provider struct {
|
|||||||
func NewProvider() (*provider, error) {
|
func NewProvider() (*provider, error) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
conn, err := http.NewConnection(http.ConnectionConfig{
|
conn, err := http.NewConnection(http.ConnectionConfig{
|
||||||
Endpoints: []string{envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseURL)},
|
Endpoints: []string{envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseURL)},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -39,16 +39,16 @@ func NewProvider() (*provider, error) {
|
|||||||
|
|
||||||
var arangodb driver.Database
|
var arangodb driver.Database
|
||||||
|
|
||||||
arangodb_exists, err := arangoClient.DatabaseExists(nil, envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseName))
|
arangodb_exists, err := arangoClient.DatabaseExists(nil, envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseName))
|
||||||
|
|
||||||
if arangodb_exists {
|
if arangodb_exists {
|
||||||
log.Println(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseName) + " db exists already")
|
log.Println(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseName) + " db exists already")
|
||||||
arangodb, err = arangoClient.Database(nil, envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseName))
|
arangodb, err = arangoClient.Database(nil, envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
arangodb, err = arangoClient.CreateDatabase(nil, envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseName), nil)
|
arangodb, err = arangoClient.CreateDatabase(nil, envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseName), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -34,6 +34,7 @@ func (p *provider) DeleteSession(userId string) error {
|
|||||||
}
|
}
|
||||||
cursor, err := p.db.Query(nil, query, bindVars)
|
cursor, err := p.db.Query(nil, query, bindVars)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Println("=> error deleting arangodb session:", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer cursor.Close()
|
defer cursor.Close()
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
package arangodb
|
package arangodb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -12,7 +11,6 @@ import (
|
|||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
"github.com/authorizerdev/authorizer/server/envstore"
|
"github.com/authorizerdev/authorizer/server/envstore"
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -23,7 +21,7 @@ func (p *provider) AddUser(user models.User) (models.User, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if user.Roles == "" {
|
if user.Roles == "" {
|
||||||
user.Roles = strings.Join(envstore.EnvStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyDefaultRoles), ",")
|
user.Roles = strings.Join(envstore.EnvInMemoryStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyDefaultRoles), ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
user.CreatedAt = time.Now().Unix()
|
user.CreatedAt = time.Now().Unix()
|
||||||
@@ -68,40 +66,32 @@ func (p *provider) DeleteUser(user models.User) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ListUsers to get list of users from database
|
// ListUsers to get list of users from database
|
||||||
func (p *provider) ListUsers(pagination model.Pagination) (*model.Users, error) {
|
func (p *provider) ListUsers() ([]models.User, error) {
|
||||||
var users []*model.User
|
var users []models.User
|
||||||
ctx := driver.WithQueryFullCount(context.Background())
|
query := fmt.Sprintf("FOR d in %s RETURN d", models.Collections.User)
|
||||||
|
|
||||||
query := fmt.Sprintf("FOR d in %s SORT d.created_at DESC LIMIT %d, %d RETURN d", models.Collections.User, pagination.Offset, pagination.Limit)
|
cursor, err := p.db.Query(nil, query, nil)
|
||||||
|
|
||||||
cursor, err := p.db.Query(ctx, query, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return users, err
|
||||||
}
|
}
|
||||||
defer cursor.Close()
|
defer cursor.Close()
|
||||||
|
|
||||||
paginationClone := pagination
|
|
||||||
paginationClone.Total = cursor.Statistics().FullCount()
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
var user models.User
|
var user models.User
|
||||||
meta, err := cursor.ReadDocument(nil, &user)
|
meta, err := cursor.ReadDocument(nil, &user)
|
||||||
|
|
||||||
if arangoDriver.IsNoMoreDocuments(err) {
|
if driver.IsNoMoreDocuments(err) {
|
||||||
break
|
break
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return nil, err
|
return users, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if meta.Key != "" {
|
if meta.Key != "" {
|
||||||
users = append(users, user.AsAPIUser())
|
users = append(users, user)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &model.Users{
|
return users, nil
|
||||||
Pagination: &paginationClone,
|
|
||||||
Users: users,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserByEmail to get user information from database using email address
|
// GetUserByEmail to get user information from database using email address
|
||||||
|
@@ -1,14 +1,12 @@
|
|||||||
package arangodb
|
package arangodb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/arangodb/go-driver"
|
"github.com/arangodb/go-driver"
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -95,20 +93,17 @@ func (p *provider) GetVerificationRequestByEmail(email string, identifier string
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ListVerificationRequests to get list of verification requests from database
|
// ListVerificationRequests to get list of verification requests from database
|
||||||
func (p *provider) ListVerificationRequests(pagination model.Pagination) (*model.VerificationRequests, error) {
|
func (p *provider) ListVerificationRequests() ([]models.VerificationRequest, error) {
|
||||||
var verificationRequests []*model.VerificationRequest
|
var verificationRequests []models.VerificationRequest
|
||||||
ctx := driver.WithQueryFullCount(context.Background())
|
|
||||||
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(ctx, query, nil)
|
query := fmt.Sprintf("FOR d in %s RETURN d", models.Collections.VerificationRequest)
|
||||||
|
|
||||||
|
cursor, err := p.db.Query(nil, query, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return verificationRequests, err
|
||||||
}
|
}
|
||||||
defer cursor.Close()
|
defer cursor.Close()
|
||||||
|
|
||||||
paginationClone := pagination
|
|
||||||
paginationClone.Total = cursor.Statistics().FullCount()
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
var verificationRequest models.VerificationRequest
|
var verificationRequest models.VerificationRequest
|
||||||
meta, err := cursor.ReadDocument(nil, &verificationRequest)
|
meta, err := cursor.ReadDocument(nil, &verificationRequest)
|
||||||
@@ -116,19 +111,16 @@ func (p *provider) ListVerificationRequests(pagination model.Pagination) (*model
|
|||||||
if driver.IsNoMoreDocuments(err) {
|
if driver.IsNoMoreDocuments(err) {
|
||||||
break
|
break
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return nil, err
|
return verificationRequests, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if meta.Key != "" {
|
if meta.Key != "" {
|
||||||
verificationRequests = append(verificationRequests, verificationRequest.AsAPIVerificationRequest())
|
verificationRequests = append(verificationRequests, verificationRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &model.VerificationRequests{
|
return verificationRequests, nil
|
||||||
VerificationRequests: verificationRequests,
|
|
||||||
Pagination: &paginationClone,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteVerificationRequest to delete verification request from database
|
// DeleteVerificationRequest to delete verification request from database
|
||||||
|
51
server/db/providers/faunadb/env.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package faunadb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
f "github.com/fauna/faunadb-go/v5/faunadb"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
|
||||||
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddEnv to save environment information in database
|
||||||
|
func (p *provider) AddEnv(env models.Env) (models.Env, error) {
|
||||||
|
if env.ID == "" {
|
||||||
|
env.ID = uuid.New().String()
|
||||||
|
env.Key = env.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
env.CreatedAt = time.Now().Unix()
|
||||||
|
env.UpdatedAt = time.Now().Unix()
|
||||||
|
|
||||||
|
_, err := p.db.Query(
|
||||||
|
f.Create(
|
||||||
|
f.Collection(models.Collections.Env),
|
||||||
|
f.Obj{
|
||||||
|
"data": env,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("error adding env:", err)
|
||||||
|
return env, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return env, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateEnv to update environment information in database
|
||||||
|
func (p *provider) UpdateEnv(env models.Env) (models.Env, error) {
|
||||||
|
env.UpdatedAt = time.Now().Unix()
|
||||||
|
|
||||||
|
return env, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEnv to get environment information from database
|
||||||
|
func (p *provider) GetEnv() (models.Env, error) {
|
||||||
|
var env models.Env
|
||||||
|
|
||||||
|
return env, nil
|
||||||
|
}
|
164
server/db/providers/faunadb/faunadb.go
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
package faunadb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
|
"github.com/authorizerdev/authorizer/server/envstore"
|
||||||
|
f "github.com/fauna/faunadb-go/v5/faunadb"
|
||||||
|
)
|
||||||
|
|
||||||
|
type provider struct {
|
||||||
|
db *f.FaunaClient
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewProvider returns a new faunadb provider
|
||||||
|
func NewProvider() (*provider, error) {
|
||||||
|
secret := ""
|
||||||
|
dbURL := "https://db.fauna.com"
|
||||||
|
|
||||||
|
// secret,url is stored in DATABASE_URL
|
||||||
|
dbURLSplit := strings.Split(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseURL), ":")
|
||||||
|
secret = dbURLSplit[0]
|
||||||
|
|
||||||
|
if len(dbURLSplit) > 1 {
|
||||||
|
dbURL = dbURLSplit[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
client := f.NewFaunaClient(secret, f.Endpoint(dbURL))
|
||||||
|
if client == nil {
|
||||||
|
return nil, errors.New("failed to create faunadb client")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := client.Query(
|
||||||
|
f.CreateCollection(f.Obj{"name": models.Collections.Env}))
|
||||||
|
if err != nil {
|
||||||
|
log.Println("error:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.Query(
|
||||||
|
f.CreateIndex(
|
||||||
|
f.Obj{
|
||||||
|
"name": "env_id",
|
||||||
|
"source": f.Collection(models.Collections.Env),
|
||||||
|
"values": "_id",
|
||||||
|
"unique": true,
|
||||||
|
}))
|
||||||
|
if err != nil {
|
||||||
|
log.Println("error:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.Query(
|
||||||
|
f.CreateIndex(
|
||||||
|
f.Obj{
|
||||||
|
"name": "env_key",
|
||||||
|
"source": f.Collection(models.Collections.Env),
|
||||||
|
"values": "_key",
|
||||||
|
"unique": true,
|
||||||
|
}))
|
||||||
|
if err != nil {
|
||||||
|
log.Println("error:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.Query(
|
||||||
|
f.CreateCollection(f.Obj{"name": models.Collections.User}))
|
||||||
|
if err != nil {
|
||||||
|
log.Println("error:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.Query(
|
||||||
|
f.CreateIndex(
|
||||||
|
f.Obj{
|
||||||
|
"name": "_id",
|
||||||
|
"source": f.Collection(models.Collections.User),
|
||||||
|
"unique": true,
|
||||||
|
}))
|
||||||
|
if err != nil {
|
||||||
|
log.Println("error:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.Query(
|
||||||
|
f.CreateIndex(
|
||||||
|
f.Obj{
|
||||||
|
"name": "_key",
|
||||||
|
"source": f.Collection(models.Collections.User),
|
||||||
|
"unique": true,
|
||||||
|
}))
|
||||||
|
if err != nil {
|
||||||
|
log.Println("error:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.Query(
|
||||||
|
f.CreateIndex(
|
||||||
|
f.Obj{
|
||||||
|
"name": "email",
|
||||||
|
"source": f.Collection(models.Collections.User),
|
||||||
|
"unique": true,
|
||||||
|
}))
|
||||||
|
if err != nil {
|
||||||
|
log.Println("error:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.Query(
|
||||||
|
f.CreateCollection(f.Obj{"name": models.Collections.Session}))
|
||||||
|
if err != nil {
|
||||||
|
log.Println("error:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.Query(
|
||||||
|
f.CreateIndex(
|
||||||
|
f.Obj{
|
||||||
|
"name": "_id",
|
||||||
|
"source": f.Collection(models.Collections.Session),
|
||||||
|
"unique": true,
|
||||||
|
}))
|
||||||
|
if err != nil {
|
||||||
|
log.Println("error:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.Query(
|
||||||
|
f.CreateIndex(
|
||||||
|
f.Obj{
|
||||||
|
"name": "_key",
|
||||||
|
"source": f.Collection(models.Collections.Session),
|
||||||
|
"unique": true,
|
||||||
|
}))
|
||||||
|
if err != nil {
|
||||||
|
log.Println("error:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.Query(
|
||||||
|
f.CreateCollection(f.Obj{"name": models.Collections.VerificationRequest}))
|
||||||
|
if err != nil {
|
||||||
|
log.Println("error:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.Query(
|
||||||
|
f.CreateIndex(
|
||||||
|
f.Obj{
|
||||||
|
"name": "_id",
|
||||||
|
"source": f.Collection(models.Collections.VerificationRequest),
|
||||||
|
"unique": true,
|
||||||
|
}))
|
||||||
|
if err != nil {
|
||||||
|
log.Println("error:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.Query(
|
||||||
|
f.CreateIndex(
|
||||||
|
f.Obj{
|
||||||
|
"name": "_key",
|
||||||
|
"source": f.Collection(models.Collections.VerificationRequest),
|
||||||
|
"unique": true,
|
||||||
|
}))
|
||||||
|
if err != nil {
|
||||||
|
log.Println("error:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &provider{
|
||||||
|
db: client,
|
||||||
|
}, nil
|
||||||
|
}
|
25
server/db/providers/faunadb/session.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package faunadb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddSession to save session information in database
|
||||||
|
func (p *provider) AddSession(session models.Session) error {
|
||||||
|
if session.ID == "" {
|
||||||
|
session.ID = uuid.New().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
session.CreatedAt = time.Now().Unix()
|
||||||
|
session.UpdatedAt = time.Now().Unix()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteSession to delete session information from database
|
||||||
|
func (p *provider) DeleteSession(userId string) error {
|
||||||
|
return nil
|
||||||
|
}
|
60
server/db/providers/faunadb/user.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package faunadb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
|
"github.com/authorizerdev/authorizer/server/envstore"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddUser to save user information in database
|
||||||
|
func (p *provider) AddUser(user models.User) (models.User, error) {
|
||||||
|
if user.ID == "" {
|
||||||
|
user.ID = uuid.New().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.Roles == "" {
|
||||||
|
user.Roles = strings.Join(envstore.EnvInMemoryStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyDefaultRoles), ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
user.CreatedAt = time.Now().Unix()
|
||||||
|
user.UpdatedAt = time.Now().Unix()
|
||||||
|
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateUser to update user information in database
|
||||||
|
func (p *provider) UpdateUser(user models.User) (models.User, error) {
|
||||||
|
user.UpdatedAt = time.Now().Unix()
|
||||||
|
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteUser to delete user information from database
|
||||||
|
func (p *provider) DeleteUser(user models.User) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListUsers to get list of users from database
|
||||||
|
func (p *provider) ListUsers() ([]models.User, error) {
|
||||||
|
var users []models.User
|
||||||
|
|
||||||
|
return users, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserByEmail to get user information from database using email address
|
||||||
|
func (p *provider) GetUserByEmail(email string) (models.User, error) {
|
||||||
|
var user models.User
|
||||||
|
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserByID to get user information from database using user ID
|
||||||
|
func (p *provider) GetUserByID(id string) (models.User, error) {
|
||||||
|
var user models.User
|
||||||
|
|
||||||
|
return user, nil
|
||||||
|
}
|
46
server/db/providers/faunadb/verification_requests.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package faunadb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddVerification to save verification request in database
|
||||||
|
func (p *provider) AddVerificationRequest(verificationRequest models.VerificationRequest) (models.VerificationRequest, error) {
|
||||||
|
if verificationRequest.ID == "" {
|
||||||
|
verificationRequest.ID = uuid.New().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
verificationRequest.CreatedAt = time.Now().Unix()
|
||||||
|
verificationRequest.UpdatedAt = time.Now().Unix()
|
||||||
|
|
||||||
|
return verificationRequest, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVerificationRequestByToken to get verification request from database using token
|
||||||
|
func (p *provider) GetVerificationRequestByToken(token string) (models.VerificationRequest, error) {
|
||||||
|
var verificationRequest models.VerificationRequest
|
||||||
|
|
||||||
|
return verificationRequest, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVerificationRequestByEmail to get verification request by email from database
|
||||||
|
func (p *provider) GetVerificationRequestByEmail(email string, identifier string) (models.VerificationRequest, error) {
|
||||||
|
var verificationRequest models.VerificationRequest
|
||||||
|
|
||||||
|
return verificationRequest, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListVerificationRequests to get list of verification requests from database
|
||||||
|
func (p *provider) ListVerificationRequests() ([]models.VerificationRequest, error) {
|
||||||
|
var verificationRequests []models.VerificationRequest
|
||||||
|
|
||||||
|
return verificationRequests, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteVerificationRequest to delete verification request from database
|
||||||
|
func (p *provider) DeleteVerificationRequest(verificationRequest models.VerificationRequest) error {
|
||||||
|
return nil
|
||||||
|
}
|
@@ -19,7 +19,7 @@ type provider struct {
|
|||||||
|
|
||||||
// NewProvider to initialize mongodb connection
|
// NewProvider to initialize mongodb connection
|
||||||
func NewProvider() (*provider, error) {
|
func NewProvider() (*provider, error) {
|
||||||
mongodbOptions := options.Client().ApplyURI(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseURL))
|
mongodbOptions := options.Client().ApplyURI(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseURL))
|
||||||
maxWait := time.Duration(5 * time.Second)
|
maxWait := time.Duration(5 * time.Second)
|
||||||
mongodbOptions.ConnectTimeout = &maxWait
|
mongodbOptions.ConnectTimeout = &maxWait
|
||||||
mongoClient, err := mongo.NewClient(mongodbOptions)
|
mongoClient, err := mongo.NewClient(mongodbOptions)
|
||||||
@@ -37,7 +37,7 @@ func NewProvider() (*provider, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
mongodb := mongoClient.Database(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseName), options.Database())
|
mongodb := mongoClient.Database(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseName), options.Database())
|
||||||
|
|
||||||
mongodb.CreateCollection(ctx, models.Collections.User, options.CreateCollection())
|
mongodb.CreateCollection(ctx, models.Collections.User, options.CreateCollection())
|
||||||
userCollection := mongodb.Collection(models.Collections.User, options.Collection())
|
userCollection := mongodb.Collection(models.Collections.User, options.Collection())
|
||||||
|
@@ -8,7 +8,6 @@ import (
|
|||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
"github.com/authorizerdev/authorizer/server/envstore"
|
"github.com/authorizerdev/authorizer/server/envstore"
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"go.mongodb.org/mongo-driver/bson"
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
"go.mongodb.org/mongo-driver/mongo/options"
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
@@ -21,7 +20,7 @@ func (p *provider) AddUser(user models.User) (models.User, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if user.Roles == "" {
|
if user.Roles == "" {
|
||||||
user.Roles = strings.Join(envstore.EnvStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyDefaultRoles), ",")
|
user.Roles = strings.Join(envstore.EnvInMemoryStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyDefaultRoles), ",")
|
||||||
}
|
}
|
||||||
user.CreatedAt = time.Now().Unix()
|
user.CreatedAt = time.Now().Unix()
|
||||||
user.UpdatedAt = time.Now().Unix()
|
user.UpdatedAt = time.Now().Unix()
|
||||||
@@ -61,29 +60,13 @@ func (p *provider) DeleteUser(user models.User) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ListUsers to get list of users from database
|
// ListUsers to get list of users from database
|
||||||
func (p *provider) ListUsers(pagination model.Pagination) (*model.Users, error) {
|
func (p *provider) ListUsers() ([]models.User, error) {
|
||||||
var users []*model.User
|
var users []models.User
|
||||||
opts := options.Find()
|
|
||||||
opts.SetLimit(pagination.Limit)
|
|
||||||
opts.SetSkip(pagination.Offset)
|
|
||||||
opts.SetSort(bson.M{"created_at": -1})
|
|
||||||
|
|
||||||
paginationClone := pagination
|
|
||||||
// TODO add pagination total
|
|
||||||
|
|
||||||
userCollection := p.db.Collection(models.Collections.User, options.Collection())
|
userCollection := p.db.Collection(models.Collections.User, options.Collection())
|
||||||
count, err := userCollection.CountDocuments(nil, bson.M{}, options.Count())
|
cursor, err := userCollection.Find(nil, bson.M{}, options.Find())
|
||||||
if err != nil {
|
|
||||||
log.Println("error getting total users:", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
paginationClone.Total = count
|
|
||||||
|
|
||||||
cursor, err := userCollection.Find(nil, bson.M{}, opts)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("error getting users:", err)
|
log.Println("error getting users:", err)
|
||||||
return nil, err
|
return users, err
|
||||||
}
|
}
|
||||||
defer cursor.Close(nil)
|
defer cursor.Close(nil)
|
||||||
|
|
||||||
@@ -91,15 +74,12 @@ func (p *provider) ListUsers(pagination model.Pagination) (*model.Users, error)
|
|||||||
var user models.User
|
var user models.User
|
||||||
err := cursor.Decode(&user)
|
err := cursor.Decode(&user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return users, err
|
||||||
}
|
}
|
||||||
users = append(users, user.AsAPIUser())
|
users = append(users, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &model.Users{
|
return users, nil
|
||||||
Pagination: &paginationClone,
|
|
||||||
Users: users,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserByEmail to get user information from database using email address
|
// GetUserByEmail to get user information from database using email address
|
||||||
|
@@ -5,7 +5,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"go.mongodb.org/mongo-driver/bson"
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
"go.mongodb.org/mongo-driver/mongo/options"
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
@@ -57,24 +56,13 @@ func (p *provider) GetVerificationRequestByEmail(email string, identifier string
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ListVerificationRequests to get list of verification requests from database
|
// ListVerificationRequests to get list of verification requests from database
|
||||||
func (p *provider) ListVerificationRequests(pagination model.Pagination) (*model.VerificationRequests, error) {
|
func (p *provider) ListVerificationRequests() ([]models.VerificationRequest, error) {
|
||||||
var verificationRequests []*model.VerificationRequest
|
var verificationRequests []models.VerificationRequest
|
||||||
|
|
||||||
opts := options.Find()
|
|
||||||
opts.SetLimit(pagination.Limit)
|
|
||||||
opts.SetSkip(pagination.Offset)
|
|
||||||
opts.SetSort(bson.M{"created_at": -1})
|
|
||||||
|
|
||||||
verificationRequestCollection := p.db.Collection(models.Collections.VerificationRequest, options.Collection())
|
verificationRequestCollection := p.db.Collection(models.Collections.VerificationRequest, options.Collection())
|
||||||
|
cursor, err := verificationRequestCollection.Find(nil, bson.M{}, options.Find())
|
||||||
verificationRequestCollectionCount, err := verificationRequestCollection.CountDocuments(nil, bson.M{})
|
|
||||||
paginationClone := pagination
|
|
||||||
paginationClone.Total = verificationRequestCollectionCount
|
|
||||||
|
|
||||||
cursor, err := verificationRequestCollection.Find(nil, bson.M{}, opts)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("error getting verification requests:", err)
|
log.Println("error getting verification requests:", err)
|
||||||
return nil, err
|
return verificationRequests, err
|
||||||
}
|
}
|
||||||
defer cursor.Close(nil)
|
defer cursor.Close(nil)
|
||||||
|
|
||||||
@@ -82,15 +70,12 @@ func (p *provider) ListVerificationRequests(pagination model.Pagination) (*model
|
|||||||
var verificationRequest models.VerificationRequest
|
var verificationRequest models.VerificationRequest
|
||||||
err := cursor.Decode(&verificationRequest)
|
err := cursor.Decode(&verificationRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return verificationRequests, err
|
||||||
}
|
}
|
||||||
verificationRequests = append(verificationRequests, verificationRequest.AsAPIVerificationRequest())
|
verificationRequests = append(verificationRequests, verificationRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &model.VerificationRequests{
|
return verificationRequests, nil
|
||||||
VerificationRequests: verificationRequests,
|
|
||||||
Pagination: &paginationClone,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteVerificationRequest to delete verification request from database
|
// DeleteVerificationRequest to delete verification request from database
|
||||||
|
@@ -1,9 +1,6 @@
|
|||||||
package providers
|
package providers
|
||||||
|
|
||||||
import (
|
import "github.com/authorizerdev/authorizer/server/db/models"
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Provider interface {
|
type Provider interface {
|
||||||
// AddUser to save user information in database
|
// AddUser to save user information in database
|
||||||
@@ -13,7 +10,7 @@ type Provider interface {
|
|||||||
// DeleteUser to delete user information from database
|
// DeleteUser to delete user information from database
|
||||||
DeleteUser(user models.User) error
|
DeleteUser(user models.User) error
|
||||||
// ListUsers to get list of users from database
|
// ListUsers to get list of users from database
|
||||||
ListUsers(pagination model.Pagination) (*model.Users, error)
|
ListUsers() ([]models.User, error)
|
||||||
// GetUserByEmail to get user information from database using email address
|
// GetUserByEmail to get user information from database using email address
|
||||||
GetUserByEmail(email string) (models.User, error)
|
GetUserByEmail(email string) (models.User, error)
|
||||||
// GetUserByID to get user information from database using user ID
|
// GetUserByID to get user information from database using user ID
|
||||||
@@ -26,7 +23,7 @@ type Provider interface {
|
|||||||
// GetVerificationRequestByEmail to get verification request by email from database
|
// GetVerificationRequestByEmail to get verification request by email from database
|
||||||
GetVerificationRequestByEmail(email string, identifier string) (models.VerificationRequest, error)
|
GetVerificationRequestByEmail(email string, identifier string) (models.VerificationRequest, error)
|
||||||
// ListVerificationRequests to get list of verification requests from database
|
// ListVerificationRequests to get list of verification requests from database
|
||||||
ListVerificationRequests(pagination model.Pagination) (*model.VerificationRequests, error)
|
ListVerificationRequests() ([]models.VerificationRequest, error)
|
||||||
// DeleteVerificationRequest to delete verification request from database
|
// DeleteVerificationRequest to delete verification request from database
|
||||||
DeleteVerificationRequest(verificationRequest models.VerificationRequest) error
|
DeleteVerificationRequest(verificationRequest models.VerificationRequest) error
|
||||||
|
|
||||||
|
@@ -15,10 +15,8 @@ func (p *provider) AddEnv(env models.Env) (models.Env, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
env.Key = env.ID
|
env.Key = env.ID
|
||||||
env.CreatedAt = time.Now().Unix()
|
|
||||||
env.UpdatedAt = time.Now().Unix()
|
|
||||||
|
|
||||||
result := p.db.Create(&env)
|
result := p.db.Create(&env)
|
||||||
|
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
log.Println("error adding config:", result.Error)
|
log.Println("error adding config:", result.Error)
|
||||||
return env, result.Error
|
return env, result.Error
|
||||||
|
@@ -2,7 +2,6 @@ package sql
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
@@ -16,8 +15,6 @@ func (p *provider) AddSession(session models.Session) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
session.Key = session.ID
|
session.Key = session.ID
|
||||||
session.CreatedAt = time.Now().Unix()
|
|
||||||
session.UpdatedAt = time.Now().Unix()
|
|
||||||
res := p.db.Clauses(
|
res := p.db.Clauses(
|
||||||
clause.OnConflict{
|
clause.OnConflict{
|
||||||
DoNothing: true,
|
DoNothing: true,
|
||||||
|
@@ -1,10 +1,6 @@
|
|||||||
package sql
|
package sql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
"github.com/authorizerdev/authorizer/server/envstore"
|
"github.com/authorizerdev/authorizer/server/envstore"
|
||||||
@@ -13,7 +9,6 @@ import (
|
|||||||
"gorm.io/driver/sqlite"
|
"gorm.io/driver/sqlite"
|
||||||
"gorm.io/driver/sqlserver"
|
"gorm.io/driver/sqlserver"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"gorm.io/gorm/logger"
|
|
||||||
"gorm.io/gorm/schema"
|
"gorm.io/gorm/schema"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -25,41 +20,33 @@ type provider struct {
|
|||||||
func NewProvider() (*provider, error) {
|
func NewProvider() (*provider, error) {
|
||||||
var sqlDB *gorm.DB
|
var sqlDB *gorm.DB
|
||||||
var err error
|
var err error
|
||||||
customLogger := logger.New(
|
|
||||||
log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
|
|
||||||
logger.Config{
|
|
||||||
SlowThreshold: time.Second, // Slow SQL threshold
|
|
||||||
LogLevel: logger.Silent, // Log level
|
|
||||||
IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger
|
|
||||||
Colorful: false, // Disable color
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
ormConfig := &gorm.Config{
|
ormConfig := &gorm.Config{
|
||||||
Logger: customLogger,
|
|
||||||
NamingStrategy: schema.NamingStrategy{
|
NamingStrategy: schema.NamingStrategy{
|
||||||
TablePrefix: models.Prefix,
|
TablePrefix: models.Prefix,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
switch envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) {
|
|
||||||
case constants.DbTypePostgres, constants.DbTypeYugabyte:
|
switch envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) {
|
||||||
sqlDB, err = gorm.Open(postgres.Open(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseURL)), ormConfig)
|
case constants.DbTypePostgres:
|
||||||
|
sqlDB, err = gorm.Open(postgres.Open(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseURL)), ormConfig)
|
||||||
|
break
|
||||||
case constants.DbTypeSqlite:
|
case constants.DbTypeSqlite:
|
||||||
sqlDB, err = gorm.Open(sqlite.Open(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseURL)), ormConfig)
|
sqlDB, err = gorm.Open(sqlite.Open(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseURL)), ormConfig)
|
||||||
case constants.DbTypeMysql, constants.DbTypeMariaDB:
|
break
|
||||||
sqlDB, err = gorm.Open(mysql.Open(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseURL)), ormConfig)
|
case constants.DbTypeMysql:
|
||||||
|
sqlDB, err = gorm.Open(mysql.Open(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseURL)), ormConfig)
|
||||||
|
break
|
||||||
case constants.DbTypeSqlserver:
|
case constants.DbTypeSqlserver:
|
||||||
sqlDB, err = gorm.Open(sqlserver.Open(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseURL)), ormConfig)
|
sqlDB, err = gorm.Open(sqlserver.Open(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseURL)), ormConfig)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = sqlDB.AutoMigrate(&models.User{}, &models.VerificationRequest{}, &models.Session{}, &models.Env{})
|
sqlDB.AutoMigrate(&models.User{}, &models.VerificationRequest{}, &models.Session{}, &models.Env{})
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &provider{
|
return &provider{
|
||||||
db: sqlDB,
|
db: sqlDB,
|
||||||
}, nil
|
}, nil
|
||||||
|
@@ -8,7 +8,6 @@ import (
|
|||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
"github.com/authorizerdev/authorizer/server/envstore"
|
"github.com/authorizerdev/authorizer/server/envstore"
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"gorm.io/gorm/clause"
|
"gorm.io/gorm/clause"
|
||||||
)
|
)
|
||||||
@@ -20,11 +19,9 @@ func (p *provider) AddUser(user models.User) (models.User, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if user.Roles == "" {
|
if user.Roles == "" {
|
||||||
user.Roles = strings.Join(envstore.EnvStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyDefaultRoles), ",")
|
user.Roles = strings.Join(envstore.EnvInMemoryStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyDefaultRoles), ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
user.CreatedAt = time.Now().Unix()
|
|
||||||
user.UpdatedAt = time.Now().Unix()
|
|
||||||
user.Key = user.ID
|
user.Key = user.ID
|
||||||
result := p.db.Clauses(
|
result := p.db.Clauses(
|
||||||
clause.OnConflict{
|
clause.OnConflict{
|
||||||
@@ -67,32 +64,15 @@ func (p *provider) DeleteUser(user models.User) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ListUsers to get list of users from database
|
// ListUsers to get list of users from database
|
||||||
func (p *provider) ListUsers(pagination model.Pagination) (*model.Users, error) {
|
func (p *provider) ListUsers() ([]models.User, error) {
|
||||||
var users []models.User
|
var users []models.User
|
||||||
result := p.db.Limit(int(pagination.Limit)).Offset(int(pagination.Offset)).Order("created_at DESC").Find(&users)
|
result := p.db.Find(&users)
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
log.Println("error getting users:", result.Error)
|
log.Println("error getting users:", result.Error)
|
||||||
return nil, result.Error
|
return users, result.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
responseUsers := []*model.User{}
|
return users, nil
|
||||||
for _, user := range users {
|
|
||||||
responseUsers = append(responseUsers, user.AsAPIUser())
|
|
||||||
}
|
|
||||||
|
|
||||||
var total int64
|
|
||||||
totalRes := p.db.Model(&models.User{}).Count(&total)
|
|
||||||
if totalRes.Error != nil {
|
|
||||||
return nil, totalRes.Error
|
|
||||||
}
|
|
||||||
|
|
||||||
paginationClone := pagination
|
|
||||||
paginationClone.Total = total
|
|
||||||
|
|
||||||
return &model.Users{
|
|
||||||
Pagination: &paginationClone,
|
|
||||||
Users: responseUsers,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserByEmail to get user information from database using email address
|
// GetUserByEmail to get user information from database using email address
|
||||||
|
@@ -2,10 +2,8 @@ package sql
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"gorm.io/gorm/clause"
|
"gorm.io/gorm/clause"
|
||||||
)
|
)
|
||||||
@@ -17,11 +15,9 @@ func (p *provider) AddVerificationRequest(verificationRequest models.Verificatio
|
|||||||
}
|
}
|
||||||
|
|
||||||
verificationRequest.Key = verificationRequest.ID
|
verificationRequest.Key = verificationRequest.ID
|
||||||
verificationRequest.CreatedAt = time.Now().Unix()
|
|
||||||
verificationRequest.UpdatedAt = time.Now().Unix()
|
|
||||||
result := p.db.Clauses(clause.OnConflict{
|
result := p.db.Clauses(clause.OnConflict{
|
||||||
Columns: []clause.Column{{Name: "email"}, {Name: "identifier"}},
|
Columns: []clause.Column{{Name: "email"}, {Name: "identifier"}},
|
||||||
DoUpdates: clause.AssignmentColumns([]string{"token", "expires_at", "nonce", "redirect_uri"}),
|
DoUpdates: clause.AssignmentColumns([]string{"token", "expires_at"}),
|
||||||
}).Create(&verificationRequest)
|
}).Create(&verificationRequest)
|
||||||
|
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
@@ -60,33 +56,15 @@ func (p *provider) GetVerificationRequestByEmail(email string, identifier string
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ListVerificationRequests to get list of verification requests from database
|
// ListVerificationRequests to get list of verification requests from database
|
||||||
func (p *provider) ListVerificationRequests(pagination model.Pagination) (*model.VerificationRequests, error) {
|
func (p *provider) ListVerificationRequests() ([]models.VerificationRequest, error) {
|
||||||
var verificationRequests []models.VerificationRequest
|
var verificationRequests []models.VerificationRequest
|
||||||
|
|
||||||
result := p.db.Limit(int(pagination.Limit)).Offset(int(pagination.Offset)).Order("created_at DESC").Find(&verificationRequests)
|
result := p.db.Find(&verificationRequests)
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
log.Println("error getting verification requests:", result.Error)
|
log.Println("error getting verification requests:", result.Error)
|
||||||
return nil, result.Error
|
return verificationRequests, result.Error
|
||||||
}
|
}
|
||||||
|
return verificationRequests, nil
|
||||||
responseVerificationRequests := []*model.VerificationRequest{}
|
|
||||||
for _, v := range verificationRequests {
|
|
||||||
responseVerificationRequests = append(responseVerificationRequests, v.AsAPIVerificationRequest())
|
|
||||||
}
|
|
||||||
|
|
||||||
var total int64
|
|
||||||
totalRes := p.db.Model(&models.VerificationRequest{}).Count(&total)
|
|
||||||
if totalRes.Error != nil {
|
|
||||||
return nil, totalRes.Error
|
|
||||||
}
|
|
||||||
|
|
||||||
paginationClone := pagination
|
|
||||||
paginationClone.Total = total
|
|
||||||
|
|
||||||
return &model.VerificationRequests{
|
|
||||||
VerificationRequests: responseVerificationRequests,
|
|
||||||
Pagination: &paginationClone,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteVerificationRequest to delete verification request from database
|
// DeleteVerificationRequest to delete verification request from database
|
||||||
|
@@ -31,18 +31,14 @@ func addEmailTemplate(a string, b map[string]interface{}, templateName string) s
|
|||||||
|
|
||||||
// SendMail function to send mail
|
// SendMail function to send mail
|
||||||
func SendMail(to []string, Subject, bodyMessage string) error {
|
func SendMail(to []string, Subject, bodyMessage string) error {
|
||||||
// dont trigger email sending in case of test
|
|
||||||
if envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyEnv) == "test" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
m := gomail.NewMessage()
|
m := gomail.NewMessage()
|
||||||
m.SetHeader("From", envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeySenderEmail))
|
m.SetHeader("From", envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeySenderEmail))
|
||||||
m.SetHeader("To", to...)
|
m.SetHeader("To", to...)
|
||||||
m.SetHeader("Subject", Subject)
|
m.SetHeader("Subject", Subject)
|
||||||
m.SetBody("text/html", bodyMessage)
|
m.SetBody("text/html", bodyMessage)
|
||||||
port, _ := strconv.Atoi(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeySmtpPort))
|
port, _ := strconv.Atoi(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeySmtpPort))
|
||||||
d := gomail.NewDialer(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeySmtpHost), port, envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeySmtpUsername), envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeySmtpPassword))
|
d := gomail.NewDialer(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeySmtpHost), port, envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeySmtpUsername), envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeySmtpPassword))
|
||||||
if envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyEnv) == "development" {
|
if envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyEnv) == "development" {
|
||||||
d.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
d.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
||||||
}
|
}
|
||||||
if err := d.DialAndSend(m); err != nil {
|
if err := d.DialAndSend(m); err != nil {
|
||||||
|
@@ -6,10 +6,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// SendForgotPasswordMail to send forgot password email
|
// SendForgotPasswordMail to send forgot password email
|
||||||
func SendForgotPasswordMail(toEmail, token, hostname string) error {
|
func SendForgotPasswordMail(toEmail, token, host string) error {
|
||||||
resetPasswordUrl := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyResetPasswordURL)
|
resetPasswordUrl := envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyResetPasswordURL)
|
||||||
if resetPasswordUrl == "" {
|
if resetPasswordUrl == "" {
|
||||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyResetPasswordURL, hostname+"/app/reset-password")
|
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyResetPasswordURL, envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL)+"/app/reset-password")
|
||||||
}
|
}
|
||||||
|
|
||||||
// The receiver needs to be in slice as the receive supports multiple receiver
|
// The receiver needs to be in slice as the receive supports multiple receiver
|
||||||
@@ -103,8 +103,8 @@ func SendForgotPasswordMail(toEmail, token, hostname string) error {
|
|||||||
`
|
`
|
||||||
|
|
||||||
data := make(map[string]interface{}, 3)
|
data := make(map[string]interface{}, 3)
|
||||||
data["org_logo"] = envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyOrganizationLogo)
|
data["org_logo"] = envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyOrganizationLogo)
|
||||||
data["org_name"] = envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyOrganizationName)
|
data["org_name"] = envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyOrganizationName)
|
||||||
data["verification_url"] = resetPasswordUrl + "?token=" + token
|
data["verification_url"] = resetPasswordUrl + "?token=" + token
|
||||||
message = addEmailTemplate(message, data, "reset_password_email.tmpl")
|
message = addEmailTemplate(message, data, "reset_password_email.tmpl")
|
||||||
|
|
||||||
|
@@ -1,113 +0,0 @@
|
|||||||
package email
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
|
||||||
"github.com/authorizerdev/authorizer/server/envstore"
|
|
||||||
)
|
|
||||||
|
|
||||||
// InviteEmail to send invite email
|
|
||||||
func InviteEmail(toEmail, token, url string) error {
|
|
||||||
// The receiver needs to be in slice as the receive supports multiple receiver
|
|
||||||
Receiver := []string{toEmail}
|
|
||||||
|
|
||||||
Subject := "Please accept the invitation"
|
|
||||||
message := `
|
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta content="width=device-width, initial-scale=1" name="viewport">
|
|
||||||
<meta name="x-apple-disable-message-reformatting">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
<meta content="telephone=no" name="format-detection">
|
|
||||||
<title></title>
|
|
||||||
<!--[if (mso 16)]>
|
|
||||||
<style type="text/css">
|
|
||||||
a {}
|
|
||||||
</style>
|
|
||||||
<![endif]-->
|
|
||||||
<!--[if gte mso 9]><style>sup { font-size: 100%% !important; }</style><![endif]-->
|
|
||||||
<!--[if gte mso 9]>
|
|
||||||
<xml>
|
|
||||||
<o:OfficeDocumentSettings>
|
|
||||||
<o:AllowPNG></o:AllowPNG>
|
|
||||||
<o:PixelsPerInch>96</o:PixelsPerInch>
|
|
||||||
</o:OfficeDocumentSettings>
|
|
||||||
</xml>
|
|
||||||
<![endif]-->
|
|
||||||
</head>
|
|
||||||
<body style="font-family: sans-serif;">
|
|
||||||
<div class="es-wrapper-color">
|
|
||||||
<!--[if gte mso 9]>
|
|
||||||
<v:background xmlns:v="urn:schemas-microsoft-com:vml" fill="t">
|
|
||||||
<v:fill type="tile" color="#ffffff"></v:fill>
|
|
||||||
</v:background>
|
|
||||||
<![endif]-->
|
|
||||||
<table class="es-wrapper" width="100%%" cellspacing="0" cellpadding="0">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td class="esd-email-paddings" valign="top">
|
|
||||||
<table class="es-content esd-footer-popover" cellspacing="0" cellpadding="0" align="center">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td class="esd-stripe" align="center">
|
|
||||||
<table class="es-content-body" style="border-left:1px solid transparent;border-right:1px solid transparent;border-top:1px solid transparent;border-bottom:1px solid transparent;padding:20px 0px;" width="600" cellspacing="0" cellpadding="0" bgcolor="#ffffff" align="center">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td class="esd-structure es-p20t es-p40b es-p40r es-p40l" esd-custom-block-id="8537" align="left">
|
|
||||||
<table width="100%%" cellspacing="0" cellpadding="0">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td class="esd-container-frame" width="518" align="left">
|
|
||||||
<table width="100%%" cellspacing="0" cellpadding="0">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td class="esd-block-image es-m-txt-c es-p5b" style="font-size:0;padding:10px" align="center"><a target="_blank" clicktracking="off"><img src="{{.org_logo}}" alt="icon" style="display: block;" title="icon" width="30"></a></td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr style="background: rgb(249,250,251);padding: 10px;margin-bottom:10px;border-radius:5px;">
|
|
||||||
<td class="esd-block-text es-m-txt-c es-p15t" align="center" style="padding:10px;padding-bottom:30px;">
|
|
||||||
<p>Hi there 👋</p>
|
|
||||||
<p>Join us! You are invited to sign-up for <b>{{.org_name}}</b>. Please accept the invitation by clicking the clicking the button below.</p> <br/>
|
|
||||||
<a
|
|
||||||
clicktracking="off" href="{{.verification_url}}" class="es-button" target="_blank" style="text-decoration: none;padding:10px 15px;background-color: rgba(59,130,246,1);color: #fff;font-size: 1em;border-radius:5px;">Get Started</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<div style="position: absolute; left: -9999px; top: -9999px; margin: 0px;"></div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
`
|
|
||||||
data := make(map[string]interface{}, 3)
|
|
||||||
data["org_logo"] = envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyOrganizationLogo)
|
|
||||||
data["org_name"] = envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyOrganizationName)
|
|
||||||
data["verification_url"] = url + "?token=" + token
|
|
||||||
message = addEmailTemplate(message, data, "invite_email.tmpl")
|
|
||||||
// bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message)
|
|
||||||
|
|
||||||
err := SendMail(Receiver, Subject, message)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("=> error sending email:", err)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
@@ -1,14 +1,12 @@
|
|||||||
package email
|
package email
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/envstore"
|
"github.com/authorizerdev/authorizer/server/envstore"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SendVerificationMail to send verification email
|
// SendVerificationMail to send verification email
|
||||||
func SendVerificationMail(toEmail, token, hostname string) error {
|
func SendVerificationMail(toEmail, token string) error {
|
||||||
// The receiver needs to be in slice as the receive supports multiple receiver
|
// The receiver needs to be in slice as the receive supports multiple receiver
|
||||||
Receiver := []string{toEmail}
|
Receiver := []string{toEmail}
|
||||||
|
|
||||||
@@ -99,15 +97,11 @@ func SendVerificationMail(toEmail, token, hostname string) error {
|
|||||||
</html>
|
</html>
|
||||||
`
|
`
|
||||||
data := make(map[string]interface{}, 3)
|
data := make(map[string]interface{}, 3)
|
||||||
data["org_logo"] = envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyOrganizationLogo)
|
data["org_logo"] = envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyOrganizationLogo)
|
||||||
data["org_name"] = envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyOrganizationName)
|
data["org_name"] = envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyOrganizationName)
|
||||||
data["verification_url"] = hostname + "/verify_email?token=" + token
|
data["verification_url"] = envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL) + "/verify_email?token=" + token
|
||||||
message = addEmailTemplate(message, data, "verify_email.tmpl")
|
message = addEmailTemplate(message, data, "verify_email.tmpl")
|
||||||
// bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message)
|
// bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message)
|
||||||
|
|
||||||
err := SendMail(Receiver, Subject, message)
|
return SendMail(Receiver, Subject, message)
|
||||||
if err != nil {
|
|
||||||
log.Println("=> error sending email:", err)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
290
server/env/env.go
vendored
@@ -1,286 +1,182 @@
|
|||||||
package env
|
package env
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/crypto"
|
|
||||||
"github.com/authorizerdev/authorizer/server/envstore"
|
"github.com/authorizerdev/authorizer/server/envstore"
|
||||||
"github.com/authorizerdev/authorizer/server/utils"
|
"github.com/authorizerdev/authorizer/server/utils"
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// InitRequiredEnv to initialize EnvData and through error if required env are not present
|
|
||||||
func InitRequiredEnv() error {
|
|
||||||
envPath := os.Getenv(constants.EnvKeyEnvPath)
|
|
||||||
|
|
||||||
if envPath == "" {
|
|
||||||
envPath = envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyEnvPath)
|
|
||||||
if envPath == "" {
|
|
||||||
envPath = `.env`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if envstore.ARG_ENV_FILE != nil && *envstore.ARG_ENV_FILE != "" {
|
|
||||||
envPath = *envstore.ARG_ENV_FILE
|
|
||||||
}
|
|
||||||
|
|
||||||
err := godotenv.Load(envPath)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("using OS env instead of %s file", envPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
dbURL := os.Getenv(constants.EnvKeyDatabaseURL)
|
|
||||||
dbType := os.Getenv(constants.EnvKeyDatabaseType)
|
|
||||||
dbName := os.Getenv(constants.EnvKeyDatabaseName)
|
|
||||||
|
|
||||||
if strings.TrimSpace(dbType) == "" {
|
|
||||||
if envstore.ARG_DB_TYPE != nil && *envstore.ARG_DB_TYPE != "" {
|
|
||||||
dbType = strings.TrimSpace(*envstore.ARG_DB_TYPE)
|
|
||||||
}
|
|
||||||
|
|
||||||
if dbType == "" {
|
|
||||||
return errors.New("invalid database type. DATABASE_TYPE is empty")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.TrimSpace(dbURL) == "" && envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseURL) == "" {
|
|
||||||
if envstore.ARG_DB_URL != nil && *envstore.ARG_DB_URL != "" {
|
|
||||||
dbURL = strings.TrimSpace(*envstore.ARG_DB_URL)
|
|
||||||
}
|
|
||||||
|
|
||||||
if dbURL == "" {
|
|
||||||
return errors.New("invalid database url. DATABASE_URL is required")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if dbName == "" {
|
|
||||||
if dbName == "" {
|
|
||||||
dbName = "authorizer"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyEnvPath, envPath)
|
|
||||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyDatabaseURL, dbURL)
|
|
||||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyDatabaseType, dbType)
|
|
||||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyDatabaseName, dbName)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InitEnv to initialize EnvData and through error if required env are not present
|
// InitEnv to initialize EnvData and through error if required env are not present
|
||||||
func InitAllEnv() error {
|
func InitEnv() {
|
||||||
envData, err := GetEnvData()
|
// get clone of current store
|
||||||
if err != nil {
|
envData := envstore.EnvInMemoryStoreObj.GetEnvStoreClone()
|
||||||
log.Println("No env data found in db, using local clone of env data")
|
|
||||||
// get clone of current store
|
|
||||||
envData = envstore.EnvStoreObj.GetEnvStoreClone()
|
|
||||||
}
|
|
||||||
|
|
||||||
clientID := envData.StringEnv[constants.EnvKeyClientID]
|
|
||||||
// unique client id for each instance
|
|
||||||
if clientID == "" {
|
|
||||||
clientID = uuid.New().String()
|
|
||||||
envData.StringEnv[constants.EnvKeyClientID] = clientID
|
|
||||||
}
|
|
||||||
|
|
||||||
clientSecret := envData.StringEnv[constants.EnvKeyClientSecret]
|
|
||||||
// unique client id for each instance
|
|
||||||
if clientSecret == "" {
|
|
||||||
clientSecret = uuid.New().String()
|
|
||||||
envData.StringEnv[constants.EnvKeyClientSecret] = clientSecret
|
|
||||||
}
|
|
||||||
|
|
||||||
if envData.StringEnv[constants.EnvKeyEnv] == "" {
|
if envData.StringEnv[constants.EnvKeyEnv] == "" {
|
||||||
envData.StringEnv[constants.EnvKeyEnv] = os.Getenv(constants.EnvKeyEnv)
|
envData.StringEnv[constants.EnvKeyEnv] = os.Getenv("ENV")
|
||||||
if envData.StringEnv[constants.EnvKeyEnv] == "" {
|
if envData.StringEnv[constants.EnvKeyEnv] == "" {
|
||||||
envData.StringEnv[constants.EnvKeyEnv] = "production"
|
envData.StringEnv[constants.EnvKeyEnv] = "production"
|
||||||
}
|
}
|
||||||
|
|
||||||
if envData.StringEnv[constants.EnvKeyEnv] == "production" {
|
if envData.StringEnv[constants.EnvKeyEnv] == "production" {
|
||||||
envData.BoolEnv[constants.EnvKeyIsProd] = true
|
envData.BoolEnv[constants.EnvKeyIsProd] = true
|
||||||
gin.SetMode(gin.ReleaseMode)
|
os.Setenv("GIN_MODE", "release")
|
||||||
} else {
|
} else {
|
||||||
envData.BoolEnv[constants.EnvKeyIsProd] = false
|
envData.BoolEnv[constants.EnvKeyIsProd] = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set authorizer url to empty string so that fresh url is obtained with every server start
|
||||||
|
envData.StringEnv[constants.EnvKeyAuthorizerURL] = ""
|
||||||
if envData.StringEnv[constants.EnvKeyAppURL] == "" {
|
if envData.StringEnv[constants.EnvKeyAppURL] == "" {
|
||||||
envData.StringEnv[constants.EnvKeyAppURL] = os.Getenv(constants.EnvKeyAppURL)
|
envData.StringEnv[constants.EnvKeyAppURL] = os.Getenv(constants.EnvKeyAppURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if envData.StringEnv[constants.EnvKeyEnvPath] == "" {
|
||||||
|
envData.StringEnv[constants.EnvKeyEnvPath] = `.env`
|
||||||
|
}
|
||||||
|
|
||||||
|
if envstore.ARG_ENV_FILE != nil && *envstore.ARG_ENV_FILE != "" {
|
||||||
|
envData.StringEnv[constants.EnvKeyEnvPath] = *envstore.ARG_ENV_FILE
|
||||||
|
}
|
||||||
|
|
||||||
|
err := godotenv.Load(envData.StringEnv[constants.EnvKeyEnvPath])
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error loading %s file", envData.StringEnv[constants.EnvKeyEnvPath])
|
||||||
|
}
|
||||||
|
|
||||||
if envData.StringEnv[constants.EnvKeyPort] == "" {
|
if envData.StringEnv[constants.EnvKeyPort] == "" {
|
||||||
envData.StringEnv[constants.EnvKeyPort] = os.Getenv(constants.EnvKeyPort)
|
envData.StringEnv[constants.EnvKeyPort] = os.Getenv("PORT")
|
||||||
if envData.StringEnv[constants.EnvKeyPort] == "" {
|
if envData.StringEnv[constants.EnvKeyPort] == "" {
|
||||||
envData.StringEnv[constants.EnvKeyPort] = "8080"
|
envData.StringEnv[constants.EnvKeyPort] = "8080"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if envData.StringEnv[constants.EnvKeyAdminSecret] == "" {
|
if envData.StringEnv[constants.EnvKeyAdminSecret] == "" {
|
||||||
envData.StringEnv[constants.EnvKeyAdminSecret] = os.Getenv(constants.EnvKeyAdminSecret)
|
envData.StringEnv[constants.EnvKeyAdminSecret] = os.Getenv("ADMIN_SECRET")
|
||||||
|
}
|
||||||
|
|
||||||
|
if envData.StringEnv[constants.EnvKeyDatabaseType] == "" {
|
||||||
|
envData.StringEnv[constants.EnvKeyDatabaseType] = os.Getenv("DATABASE_TYPE")
|
||||||
|
|
||||||
|
if envstore.ARG_DB_TYPE != nil && *envstore.ARG_DB_TYPE != "" {
|
||||||
|
envData.StringEnv[constants.EnvKeyDatabaseType] = *envstore.ARG_DB_TYPE
|
||||||
|
}
|
||||||
|
|
||||||
|
if envData.StringEnv[constants.EnvKeyDatabaseType] == "" {
|
||||||
|
panic("DATABASE_TYPE is required")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if envData.StringEnv[constants.EnvKeyDatabaseURL] == "" {
|
||||||
|
envData.StringEnv[constants.EnvKeyDatabaseURL] = os.Getenv("DATABASE_URL")
|
||||||
|
|
||||||
|
if envstore.ARG_DB_URL != nil && *envstore.ARG_DB_URL != "" {
|
||||||
|
envData.StringEnv[constants.EnvKeyDatabaseURL] = *envstore.ARG_DB_URL
|
||||||
|
}
|
||||||
|
|
||||||
|
if envData.StringEnv[constants.EnvKeyDatabaseURL] == "" {
|
||||||
|
panic("DATABASE_URL is required")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if envData.StringEnv[constants.EnvKeyDatabaseName] == "" {
|
||||||
|
envData.StringEnv[constants.EnvKeyDatabaseName] = os.Getenv("DATABASE_NAME")
|
||||||
|
if envData.StringEnv[constants.EnvKeyDatabaseName] == "" {
|
||||||
|
envData.StringEnv[constants.EnvKeyDatabaseName] = "authorizer"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if envData.StringEnv[constants.EnvKeySmtpHost] == "" {
|
if envData.StringEnv[constants.EnvKeySmtpHost] == "" {
|
||||||
envData.StringEnv[constants.EnvKeySmtpHost] = os.Getenv(constants.EnvKeySmtpHost)
|
envData.StringEnv[constants.EnvKeySmtpHost] = os.Getenv("SMTP_HOST")
|
||||||
}
|
}
|
||||||
|
|
||||||
if envData.StringEnv[constants.EnvKeySmtpPort] == "" {
|
if envData.StringEnv[constants.EnvKeySmtpPort] == "" {
|
||||||
envData.StringEnv[constants.EnvKeySmtpPort] = os.Getenv(constants.EnvKeySmtpPort)
|
envData.StringEnv[constants.EnvKeySmtpPort] = os.Getenv("SMTP_PORT")
|
||||||
}
|
}
|
||||||
|
|
||||||
if envData.StringEnv[constants.EnvKeySmtpUsername] == "" {
|
if envData.StringEnv[constants.EnvKeySmtpUsername] == "" {
|
||||||
envData.StringEnv[constants.EnvKeySmtpUsername] = os.Getenv(constants.EnvKeySmtpUsername)
|
envData.StringEnv[constants.EnvKeySmtpUsername] = os.Getenv("SMTP_USERNAME")
|
||||||
}
|
}
|
||||||
|
|
||||||
if envData.StringEnv[constants.EnvKeySmtpPassword] == "" {
|
if envData.StringEnv[constants.EnvKeySmtpPassword] == "" {
|
||||||
envData.StringEnv[constants.EnvKeySmtpPassword] = os.Getenv(constants.EnvKeySmtpPassword)
|
envData.StringEnv[constants.EnvKeySmtpPassword] = os.Getenv("SMTP_PASSWORD")
|
||||||
}
|
}
|
||||||
|
|
||||||
if envData.StringEnv[constants.EnvKeySenderEmail] == "" {
|
if envData.StringEnv[constants.EnvKeySenderEmail] == "" {
|
||||||
envData.StringEnv[constants.EnvKeySenderEmail] = os.Getenv(constants.EnvKeySenderEmail)
|
envData.StringEnv[constants.EnvKeySenderEmail] = os.Getenv("SENDER_EMAIL")
|
||||||
}
|
}
|
||||||
|
|
||||||
algo := envData.StringEnv[constants.EnvKeyJwtType]
|
if envData.StringEnv[constants.EnvKeyJwtSecret] == "" {
|
||||||
if algo == "" {
|
envData.StringEnv[constants.EnvKeyJwtSecret] = os.Getenv("JWT_SECRET")
|
||||||
envData.StringEnv[constants.EnvKeyJwtType] = os.Getenv(constants.EnvKeyJwtType)
|
|
||||||
if envData.StringEnv[constants.EnvKeyJwtType] == "" {
|
|
||||||
envData.StringEnv[constants.EnvKeyJwtType] = "RS256"
|
|
||||||
algo = envData.StringEnv[constants.EnvKeyJwtType]
|
|
||||||
} else {
|
|
||||||
algo = envData.StringEnv[constants.EnvKeyJwtType]
|
|
||||||
if !crypto.IsHMACA(algo) && !crypto.IsRSA(algo) && !crypto.IsECDSA(algo) {
|
|
||||||
return errors.New("invalid JWT_TYPE")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if crypto.IsHMACA(algo) {
|
|
||||||
if envData.StringEnv[constants.EnvKeyJwtSecret] == "" {
|
if envData.StringEnv[constants.EnvKeyJwtSecret] == "" {
|
||||||
envData.StringEnv[constants.EnvKeyJwtSecret] = os.Getenv(constants.EnvKeyJwtSecret)
|
envData.StringEnv[constants.EnvKeyJwtSecret] = uuid.New().String()
|
||||||
if envData.StringEnv[constants.EnvKeyJwtSecret] == "" {
|
|
||||||
envData.StringEnv[constants.EnvKeyJwtSecret], _, err = crypto.NewHMACKey(algo, clientID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if crypto.IsRSA(algo) || crypto.IsECDSA(algo) {
|
if envData.StringEnv[constants.EnvKeyJwtType] == "" {
|
||||||
privateKey, publicKey := "", ""
|
envData.StringEnv[constants.EnvKeyJwtType] = os.Getenv("JWT_TYPE")
|
||||||
|
if envData.StringEnv[constants.EnvKeyJwtType] == "" {
|
||||||
if envData.StringEnv[constants.EnvKeyJwtPrivateKey] == "" {
|
envData.StringEnv[constants.EnvKeyJwtType] = "HS256"
|
||||||
privateKey = os.Getenv(constants.EnvKeyJwtPrivateKey)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if envData.StringEnv[constants.EnvKeyJwtPublicKey] == "" {
|
|
||||||
publicKey = os.Getenv(constants.EnvKeyJwtPublicKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
// if algo is RSA / ECDSA, then we need to have both private and public key
|
|
||||||
// if either of them is not present generate new keys
|
|
||||||
if privateKey == "" || publicKey == "" {
|
|
||||||
if crypto.IsRSA(algo) {
|
|
||||||
_, privateKey, publicKey, _, err = crypto.NewRSAKey(algo, clientID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else if crypto.IsECDSA(algo) {
|
|
||||||
_, privateKey, publicKey, _, err = crypto.NewECDSAKey(algo, clientID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// parse keys to make sure they are valid
|
|
||||||
if crypto.IsRSA(algo) {
|
|
||||||
_, err = crypto.ParseRsaPrivateKeyFromPemStr(privateKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := crypto.ParseRsaPublicKeyFromPemStr(publicKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if crypto.IsECDSA(algo) {
|
|
||||||
_, err = crypto.ParseEcdsaPrivateKeyFromPemStr(privateKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := crypto.ParseEcdsaPublicKeyFromPemStr(publicKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
envData.StringEnv[constants.EnvKeyJwtPrivateKey] = privateKey
|
|
||||||
envData.StringEnv[constants.EnvKeyJwtPublicKey] = publicKey
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if envData.StringEnv[constants.EnvKeyJwtRoleClaim] == "" {
|
if envData.StringEnv[constants.EnvKeyJwtRoleClaim] == "" {
|
||||||
envData.StringEnv[constants.EnvKeyJwtRoleClaim] = os.Getenv(constants.EnvKeyJwtRoleClaim)
|
envData.StringEnv[constants.EnvKeyJwtRoleClaim] = os.Getenv("JWT_ROLE_CLAIM")
|
||||||
|
|
||||||
if envData.StringEnv[constants.EnvKeyJwtRoleClaim] == "" {
|
if envData.StringEnv[constants.EnvKeyJwtRoleClaim] == "" {
|
||||||
envData.StringEnv[constants.EnvKeyJwtRoleClaim] = "role"
|
envData.StringEnv[constants.EnvKeyJwtRoleClaim] = "role"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if envData.StringEnv[constants.EnvKeyCustomAccessTokenScript] == "" {
|
|
||||||
envData.StringEnv[constants.EnvKeyCustomAccessTokenScript] = os.Getenv(constants.EnvKeyCustomAccessTokenScript)
|
|
||||||
}
|
|
||||||
|
|
||||||
if envData.StringEnv[constants.EnvKeyRedisURL] == "" {
|
if envData.StringEnv[constants.EnvKeyRedisURL] == "" {
|
||||||
envData.StringEnv[constants.EnvKeyRedisURL] = os.Getenv(constants.EnvKeyRedisURL)
|
envData.StringEnv[constants.EnvKeyRedisURL] = os.Getenv("REDIS_URL")
|
||||||
}
|
}
|
||||||
|
|
||||||
if envData.StringEnv[constants.EnvKeyCookieName] == "" {
|
if envData.StringEnv[constants.EnvKeyCookieName] == "" {
|
||||||
envData.StringEnv[constants.EnvKeyCookieName] = os.Getenv(constants.EnvKeyCookieName)
|
envData.StringEnv[constants.EnvKeyCookieName] = os.Getenv("COOKIE_NAME")
|
||||||
if envData.StringEnv[constants.EnvKeyCookieName] == "" {
|
if envData.StringEnv[constants.EnvKeyCookieName] == "" {
|
||||||
envData.StringEnv[constants.EnvKeyCookieName] = "authorizer"
|
envData.StringEnv[constants.EnvKeyCookieName] = "authorizer"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if envData.StringEnv[constants.EnvKeyGoogleClientID] == "" {
|
if envData.StringEnv[constants.EnvKeyGoogleClientID] == "" {
|
||||||
envData.StringEnv[constants.EnvKeyGoogleClientID] = os.Getenv(constants.EnvKeyGoogleClientID)
|
envData.StringEnv[constants.EnvKeyGoogleClientID] = os.Getenv("GOOGLE_CLIENT_ID")
|
||||||
}
|
}
|
||||||
|
|
||||||
if envData.StringEnv[constants.EnvKeyGoogleClientSecret] == "" {
|
if envData.StringEnv[constants.EnvKeyGoogleClientSecret] == "" {
|
||||||
envData.StringEnv[constants.EnvKeyGoogleClientSecret] = os.Getenv(constants.EnvKeyGoogleClientSecret)
|
envData.StringEnv[constants.EnvKeyGoogleClientSecret] = os.Getenv("GOOGLE_CLIENT_SECRET")
|
||||||
}
|
}
|
||||||
|
|
||||||
if envData.StringEnv[constants.EnvKeyGithubClientID] == "" {
|
if envData.StringEnv[constants.EnvKeyGithubClientID] == "" {
|
||||||
envData.StringEnv[constants.EnvKeyGithubClientID] = os.Getenv(constants.EnvKeyGithubClientID)
|
envData.StringEnv[constants.EnvKeyGithubClientID] = os.Getenv("GITHUB_CLIENT_ID")
|
||||||
}
|
}
|
||||||
|
|
||||||
if envData.StringEnv[constants.EnvKeyGithubClientSecret] == "" {
|
if envData.StringEnv[constants.EnvKeyGithubClientSecret] == "" {
|
||||||
envData.StringEnv[constants.EnvKeyGithubClientSecret] = os.Getenv(constants.EnvKeyGithubClientSecret)
|
envData.StringEnv[constants.EnvKeyGithubClientSecret] = os.Getenv("GITHUB_CLIENT_SECRET")
|
||||||
}
|
}
|
||||||
|
|
||||||
if envData.StringEnv[constants.EnvKeyFacebookClientID] == "" {
|
if envData.StringEnv[constants.EnvKeyFacebookClientID] == "" {
|
||||||
envData.StringEnv[constants.EnvKeyFacebookClientID] = os.Getenv(constants.EnvKeyFacebookClientID)
|
envData.StringEnv[constants.EnvKeyFacebookClientID] = os.Getenv("FACEBOOK_CLIENT_ID")
|
||||||
}
|
}
|
||||||
|
|
||||||
if envData.StringEnv[constants.EnvKeyFacebookClientSecret] == "" {
|
if envData.StringEnv[constants.EnvKeyFacebookClientSecret] == "" {
|
||||||
envData.StringEnv[constants.EnvKeyFacebookClientSecret] = os.Getenv(constants.EnvKeyFacebookClientSecret)
|
envData.StringEnv[constants.EnvKeyFacebookClientSecret] = os.Getenv("FACEBOOK_CLIENT_SECRET")
|
||||||
}
|
}
|
||||||
|
|
||||||
if envData.StringEnv[constants.EnvKeyResetPasswordURL] == "" {
|
if envData.StringEnv[constants.EnvKeyResetPasswordURL] == "" {
|
||||||
envData.StringEnv[constants.EnvKeyResetPasswordURL] = strings.TrimPrefix(os.Getenv(constants.EnvKeyResetPasswordURL), "/")
|
envData.StringEnv[constants.EnvKeyResetPasswordURL] = strings.TrimPrefix(os.Getenv("RESET_PASSWORD_URL"), "/")
|
||||||
}
|
}
|
||||||
|
|
||||||
envData.BoolEnv[constants.EnvKeyDisableBasicAuthentication] = os.Getenv(constants.EnvKeyDisableBasicAuthentication) == "true"
|
envData.BoolEnv[constants.EnvKeyDisableBasicAuthentication] = os.Getenv("DISABLE_BASIC_AUTHENTICATION") == "true"
|
||||||
envData.BoolEnv[constants.EnvKeyDisableEmailVerification] = os.Getenv(constants.EnvKeyDisableEmailVerification) == "true"
|
envData.BoolEnv[constants.EnvKeyDisableEmailVerification] = os.Getenv("DISABLE_EMAIL_VERIFICATION") == "true"
|
||||||
envData.BoolEnv[constants.EnvKeyDisableMagicLinkLogin] = os.Getenv(constants.EnvKeyDisableMagicLinkLogin) == "true"
|
envData.BoolEnv[constants.EnvKeyDisableMagicLinkLogin] = os.Getenv("DISABLE_MAGIC_LINK_LOGIN") == "true"
|
||||||
envData.BoolEnv[constants.EnvKeyDisableLoginPage] = os.Getenv(constants.EnvKeyDisableLoginPage) == "true"
|
envData.BoolEnv[constants.EnvKeyDisableLoginPage] = os.Getenv("DISABLE_LOGIN_PAGE") == "true"
|
||||||
|
|
||||||
// no need to add nil check as its already done above
|
// no need to add nil check as its already done above
|
||||||
if envData.StringEnv[constants.EnvKeySmtpHost] == "" || envData.StringEnv[constants.EnvKeySmtpUsername] == "" || envData.StringEnv[constants.EnvKeySmtpPassword] == "" || envData.StringEnv[constants.EnvKeySenderEmail] == "" && envData.StringEnv[constants.EnvKeySmtpPort] == "" {
|
if envData.StringEnv[constants.EnvKeySmtpHost] == "" || envData.StringEnv[constants.EnvKeySmtpUsername] == "" || envData.StringEnv[constants.EnvKeySmtpPassword] == "" || envData.StringEnv[constants.EnvKeySenderEmail] == "" && envData.StringEnv[constants.EnvKeySmtpPort] == "" {
|
||||||
@@ -292,7 +188,7 @@ func InitAllEnv() error {
|
|||||||
envData.BoolEnv[constants.EnvKeyDisableMagicLinkLogin] = true
|
envData.BoolEnv[constants.EnvKeyDisableMagicLinkLogin] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
allowedOriginsSplit := strings.Split(os.Getenv(constants.EnvKeyAllowedOrigins), ",")
|
allowedOriginsSplit := strings.Split(os.Getenv("ALLOWED_ORIGINS"), ",")
|
||||||
allowedOrigins := []string{}
|
allowedOrigins := []string{}
|
||||||
hasWildCard := false
|
hasWildCard := false
|
||||||
|
|
||||||
@@ -320,14 +216,14 @@ func InitAllEnv() error {
|
|||||||
|
|
||||||
envData.SliceEnv[constants.EnvKeyAllowedOrigins] = allowedOrigins
|
envData.SliceEnv[constants.EnvKeyAllowedOrigins] = allowedOrigins
|
||||||
|
|
||||||
rolesEnv := strings.TrimSpace(os.Getenv(constants.EnvKeyRoles))
|
rolesEnv := strings.TrimSpace(os.Getenv("ROLES"))
|
||||||
rolesSplit := strings.Split(rolesEnv, ",")
|
rolesSplit := strings.Split(rolesEnv, ",")
|
||||||
roles := []string{}
|
roles := []string{}
|
||||||
if len(rolesEnv) == 0 {
|
if len(rolesEnv) == 0 {
|
||||||
roles = []string{"user"}
|
roles = []string{"user"}
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultRolesEnv := strings.TrimSpace(os.Getenv(constants.EnvKeyDefaultRoles))
|
defaultRolesEnv := strings.TrimSpace(os.Getenv("DEFAULT_ROLES"))
|
||||||
defaultRoleSplit := strings.Split(defaultRolesEnv, ",")
|
defaultRoleSplit := strings.Split(defaultRolesEnv, ",")
|
||||||
defaultRoles := []string{}
|
defaultRoles := []string{}
|
||||||
|
|
||||||
@@ -335,7 +231,7 @@ func InitAllEnv() error {
|
|||||||
defaultRoles = []string{"user"}
|
defaultRoles = []string{"user"}
|
||||||
}
|
}
|
||||||
|
|
||||||
protectedRolesEnv := strings.TrimSpace(os.Getenv(constants.EnvKeyProtectedRoles))
|
protectedRolesEnv := strings.TrimSpace(os.Getenv("PROTECTED_ROLES"))
|
||||||
protectedRolesSplit := strings.Split(protectedRolesEnv, ",")
|
protectedRolesSplit := strings.Split(protectedRolesEnv, ",")
|
||||||
protectedRoles := []string{}
|
protectedRoles := []string{}
|
||||||
|
|
||||||
@@ -350,28 +246,28 @@ func InitAllEnv() error {
|
|||||||
trimVal := strings.TrimSpace(val)
|
trimVal := strings.TrimSpace(val)
|
||||||
if trimVal != "" {
|
if trimVal != "" {
|
||||||
roles = append(roles, trimVal)
|
roles = append(roles, trimVal)
|
||||||
if utils.StringSliceContains(defaultRoleSplit, trimVal) {
|
}
|
||||||
defaultRoles = append(defaultRoles, trimVal)
|
|
||||||
}
|
if utils.StringSliceContains(defaultRoleSplit, trimVal) {
|
||||||
|
defaultRoles = append(defaultRoles, trimVal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(roles) > 0 && len(defaultRoles) == 0 && len(defaultRolesEnv) > 0 {
|
if len(roles) > 0 && len(defaultRoles) == 0 && len(defaultRolesEnv) > 0 {
|
||||||
return errors.New(`invalid DEFAULT_ROLE environment variable. It can be one from give ROLES environment variable value`)
|
panic(`Invalid DEFAULT_ROLE environment variable. It can be one from give ROLES environment variable value`)
|
||||||
}
|
}
|
||||||
|
|
||||||
envData.SliceEnv[constants.EnvKeyRoles] = roles
|
envData.SliceEnv[constants.EnvKeyRoles] = roles
|
||||||
envData.SliceEnv[constants.EnvKeyDefaultRoles] = defaultRoles
|
envData.SliceEnv[constants.EnvKeyDefaultRoles] = defaultRoles
|
||||||
envData.SliceEnv[constants.EnvKeyProtectedRoles] = protectedRoles
|
envData.SliceEnv[constants.EnvKeyProtectedRoles] = protectedRoles
|
||||||
|
|
||||||
if os.Getenv(constants.EnvKeyOrganizationName) != "" {
|
if os.Getenv("ORGANIZATION_NAME") != "" {
|
||||||
envData.StringEnv[constants.EnvKeyOrganizationName] = os.Getenv(constants.EnvKeyOrganizationName)
|
envData.StringEnv[constants.EnvKeyOrganizationName] = os.Getenv("ORGANIZATION_NAME")
|
||||||
}
|
}
|
||||||
|
|
||||||
if os.Getenv(constants.EnvKeyOrganizationLogo) != "" {
|
if os.Getenv("ORGANIZATION_LOGO") != "" {
|
||||||
envData.StringEnv[constants.EnvKeyOrganizationLogo] = os.Getenv(constants.EnvKeyOrganizationLogo)
|
envData.StringEnv[constants.EnvKeyOrganizationLogo] = os.Getenv("ORGANIZATION_LOGO")
|
||||||
}
|
}
|
||||||
|
|
||||||
envstore.EnvStoreObj.UpdateEnvStore(envData)
|
envstore.EnvInMemoryStoreObj.UpdateEnvStore(envData)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
79
server/env/persist_env.go
vendored
@@ -7,51 +7,14 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/crypto"
|
|
||||||
"github.com/authorizerdev/authorizer/server/db"
|
"github.com/authorizerdev/authorizer/server/db"
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
"github.com/authorizerdev/authorizer/server/envstore"
|
"github.com/authorizerdev/authorizer/server/envstore"
|
||||||
"github.com/authorizerdev/authorizer/server/utils"
|
"github.com/authorizerdev/authorizer/server/utils"
|
||||||
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetEnvData returns the env data from database
|
|
||||||
func GetEnvData() (envstore.Store, error) {
|
|
||||||
var result envstore.Store
|
|
||||||
env, err := db.Provider.GetEnv()
|
|
||||||
// config not found in db
|
|
||||||
if err != nil {
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
|
|
||||||
encryptionKey := env.Hash
|
|
||||||
decryptedEncryptionKey, err := crypto.DecryptB64(encryptionKey)
|
|
||||||
if err != nil {
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
|
|
||||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyEncryptionKey, decryptedEncryptionKey)
|
|
||||||
|
|
||||||
b64DecryptedConfig, err := crypto.DecryptB64(env.EnvData)
|
|
||||||
if err != nil {
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
|
|
||||||
decryptedConfigs, err := crypto.DecryptAESEnv([]byte(b64DecryptedConfig))
|
|
||||||
if err != nil {
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal(decryptedConfigs, &result)
|
|
||||||
if err != nil {
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// PersistEnv persists the environment variables to the database
|
// PersistEnv persists the environment variables to the database
|
||||||
func PersistEnv() error {
|
func PersistEnv() error {
|
||||||
env, err := db.Provider.GetEnv()
|
env, err := db.Provider.GetEnv()
|
||||||
@@ -59,10 +22,15 @@ func PersistEnv() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
// AES encryption needs 32 bit key only, so we chop off last 4 characters from 36 bit uuid
|
// AES encryption needs 32 bit key only, so we chop off last 4 characters from 36 bit uuid
|
||||||
hash := uuid.New().String()[:36-4]
|
hash := uuid.New().String()[:36-4]
|
||||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyEncryptionKey, hash)
|
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyEncryptionKey, hash)
|
||||||
encodedHash := crypto.EncryptB64(hash)
|
encodedHash := utils.EncryptB64(hash)
|
||||||
|
|
||||||
encryptedConfig, err := crypto.EncryptEnvData(envstore.EnvStoreObj.GetEnvStoreClone())
|
configData, err := json.Marshal(envstore.EnvInMemoryStoreObj.GetEnvStoreClone())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptedConfig, err := utils.EncryptAES(configData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -72,27 +40,18 @@ func PersistEnv() error {
|
|||||||
EnvData: encryptedConfig,
|
EnvData: encryptedConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
env, err = db.Provider.AddEnv(env)
|
db.Provider.AddEnv(env)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// decrypt the config data from db
|
// decrypt the config data from db
|
||||||
// decryption can be done using the hash stored in db
|
// decryption can be done using the hash stored in db
|
||||||
encryptionKey := env.Hash
|
encryptionKey := env.Hash
|
||||||
decryptedEncryptionKey, err := crypto.DecryptB64(encryptionKey)
|
decryptedEncryptionKey, err := utils.DecryptB64(encryptionKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyEncryptionKey, decryptedEncryptionKey)
|
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyEncryptionKey, decryptedEncryptionKey)
|
||||||
|
decryptedConfigs, err := utils.DecryptAES(env.EnvData)
|
||||||
b64DecryptedConfig, err := crypto.DecryptB64(env.EnvData)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
decryptedConfigs, err := crypto.DecryptAESEnv([]byte(b64DecryptedConfig))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -111,7 +70,6 @@ func PersistEnv() error {
|
|||||||
hasChanged := false
|
hasChanged := false
|
||||||
|
|
||||||
for key, value := range storeData.StringEnv {
|
for key, value := range storeData.StringEnv {
|
||||||
// don't override unexposed envs
|
|
||||||
if key != constants.EnvKeyEncryptionKey {
|
if key != constants.EnvKeyEncryptionKey {
|
||||||
// check only for derivative keys
|
// check only for derivative keys
|
||||||
// No need to check for ENCRYPTION_KEY which special key we use for encrypting config data
|
// No need to check for ENCRYPTION_KEY which special key we use for encrypting config data
|
||||||
@@ -165,16 +123,10 @@ func PersistEnv() error {
|
|||||||
hasChanged = true
|
hasChanged = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
envstore.EnvStoreObj.UpdateEnvStore(storeData)
|
|
||||||
jwk, err := crypto.GenerateJWKBasedOnEnv()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// updating jwk
|
|
||||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyJWK, jwk)
|
|
||||||
|
|
||||||
|
envstore.EnvInMemoryStoreObj.UpdateEnvStore(storeData)
|
||||||
if hasChanged {
|
if hasChanged {
|
||||||
encryptedConfig, err := crypto.EncryptEnvData(storeData)
|
encryptedConfig, err := utils.EncryptEnvData(storeData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -186,6 +138,7 @@ func PersistEnv() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@@ -22,19 +22,20 @@ type Store struct {
|
|||||||
SliceEnv map[string][]string `json:"slice_env"`
|
SliceEnv map[string][]string `json:"slice_env"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnvStore struct
|
// EnvInMemoryStore struct
|
||||||
type EnvStore struct {
|
type EnvInMemoryStore struct {
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
store *Store
|
store *Store
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultStore = &EnvStore{
|
// EnvInMemoryStoreObj global variable for EnvInMemoryStore
|
||||||
|
var EnvInMemoryStoreObj = &EnvInMemoryStore{
|
||||||
store: &Store{
|
store: &Store{
|
||||||
StringEnv: map[string]string{
|
StringEnv: map[string]string{
|
||||||
constants.EnvKeyAdminCookieName: "authorizer-admin",
|
constants.EnvKeyAdminCookieName: "authorizer-admin",
|
||||||
constants.EnvKeyJwtRoleClaim: "role",
|
constants.EnvKeyJwtRoleClaim: "role",
|
||||||
constants.EnvKeyOrganizationName: "Authorizer",
|
constants.EnvKeyOrganizationName: "Authorizer",
|
||||||
constants.EnvKeyOrganizationLogo: "https://www.authorizer.dev/images/logo.png",
|
constants.EnvKeyOrganizationLogo: "https://www.authorizer.io/images/logo.png",
|
||||||
},
|
},
|
||||||
BoolEnv: map[string]bool{
|
BoolEnv: map[string]bool{
|
||||||
constants.EnvKeyDisableBasicAuthentication: false,
|
constants.EnvKeyDisableBasicAuthentication: false,
|
||||||
@@ -46,11 +47,8 @@ var defaultStore = &EnvStore{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnvStoreObj.GetBoolStoreEnvVariable global variable for EnvStore
|
|
||||||
var EnvStoreObj = defaultStore
|
|
||||||
|
|
||||||
// UpdateEnvStore to update the whole env store object
|
// UpdateEnvStore to update the whole env store object
|
||||||
func (e *EnvStore) UpdateEnvStore(store Store) {
|
func (e *EnvInMemoryStore) UpdateEnvStore(store Store) {
|
||||||
e.mutex.Lock()
|
e.mutex.Lock()
|
||||||
defer e.mutex.Unlock()
|
defer e.mutex.Unlock()
|
||||||
// just override the keys + new keys
|
// just override the keys + new keys
|
||||||
@@ -69,7 +67,7 @@ func (e *EnvStore) UpdateEnvStore(store Store) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdateEnvVariable to update the particular env variable
|
// UpdateEnvVariable to update the particular env variable
|
||||||
func (e *EnvStore) UpdateEnvVariable(storeIdentifier, key string, value interface{}) {
|
func (e *EnvInMemoryStore) UpdateEnvVariable(storeIdentifier, key string, value interface{}) {
|
||||||
e.mutex.Lock()
|
e.mutex.Lock()
|
||||||
defer e.mutex.Unlock()
|
defer e.mutex.Unlock()
|
||||||
switch storeIdentifier {
|
switch storeIdentifier {
|
||||||
@@ -83,37 +81,31 @@ func (e *EnvStore) UpdateEnvVariable(storeIdentifier, key string, value interfac
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetStringStoreEnvVariable to get the env variable from string store object
|
// GetStringStoreEnvVariable to get the env variable from string store object
|
||||||
func (e *EnvStore) GetStringStoreEnvVariable(key string) string {
|
func (e *EnvInMemoryStore) GetStringStoreEnvVariable(key string) string {
|
||||||
// e.mutex.Lock()
|
// e.mutex.Lock()
|
||||||
// defer e.mutex.Unlock()
|
// defer e.mutex.Unlock()
|
||||||
return e.store.StringEnv[key]
|
return e.store.StringEnv[key]
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBoolStoreEnvVariable to get the env variable from bool store object
|
// GetBoolStoreEnvVariable to get the env variable from bool store object
|
||||||
func (e *EnvStore) GetBoolStoreEnvVariable(key string) bool {
|
func (e *EnvInMemoryStore) GetBoolStoreEnvVariable(key string) bool {
|
||||||
// e.mutex.Lock()
|
// e.mutex.Lock()
|
||||||
// defer e.mutex.Unlock()
|
// defer e.mutex.Unlock()
|
||||||
return e.store.BoolEnv[key]
|
return e.store.BoolEnv[key]
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSliceStoreEnvVariable to get the env variable from slice store object
|
// GetSliceStoreEnvVariable to get the env variable from slice store object
|
||||||
func (e *EnvStore) GetSliceStoreEnvVariable(key string) []string {
|
func (e *EnvInMemoryStore) GetSliceStoreEnvVariable(key string) []string {
|
||||||
// e.mutex.Lock()
|
// e.mutex.Lock()
|
||||||
// defer e.mutex.Unlock()
|
// defer e.mutex.Unlock()
|
||||||
return e.store.SliceEnv[key]
|
return e.store.SliceEnv[key]
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetEnvStoreClone to get clone of current env store object
|
// GetEnvStoreClone to get clone of current env store object
|
||||||
func (e *EnvStore) GetEnvStoreClone() Store {
|
func (e *EnvInMemoryStore) GetEnvStoreClone() Store {
|
||||||
e.mutex.Lock()
|
e.mutex.Lock()
|
||||||
defer e.mutex.Unlock()
|
defer e.mutex.Unlock()
|
||||||
|
|
||||||
result := *e.store
|
result := *e.store
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *EnvStore) ResetStore() {
|
|
||||||
e.mutex.Lock()
|
|
||||||
defer e.mutex.Unlock()
|
|
||||||
e.store = defaultStore.store
|
|
||||||
}
|
|
||||||
|
@@ -6,6 +6,8 @@ require (
|
|||||||
github.com/99designs/gqlgen v0.14.0
|
github.com/99designs/gqlgen v0.14.0
|
||||||
github.com/arangodb/go-driver v1.2.1
|
github.com/arangodb/go-driver v1.2.1
|
||||||
github.com/coreos/go-oidc/v3 v3.1.0
|
github.com/coreos/go-oidc/v3 v3.1.0
|
||||||
|
github.com/fauna/faunadb-go/v5 v5.0.0-beta // indirect
|
||||||
|
github.com/gin-contrib/location v0.0.2
|
||||||
github.com/gin-gonic/gin v1.7.2
|
github.com/gin-gonic/gin v1.7.2
|
||||||
github.com/go-playground/validator/v10 v10.8.0 // indirect
|
github.com/go-playground/validator/v10 v10.8.0 // indirect
|
||||||
github.com/go-redis/redis/v8 v8.11.0
|
github.com/go-redis/redis/v8 v8.11.0
|
||||||
@@ -30,7 +32,6 @@ require (
|
|||||||
google.golang.org/protobuf v1.27.1 // indirect
|
google.golang.org/protobuf v1.27.1 // indirect
|
||||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||||
gopkg.in/mail.v2 v2.3.1
|
gopkg.in/mail.v2 v2.3.1
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0
|
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
gorm.io/driver/mysql v1.2.1
|
gorm.io/driver/mysql v1.2.1
|
||||||
gorm.io/driver/postgres v1.2.3
|
gorm.io/driver/postgres v1.2.3
|
||||||
|
@@ -79,11 +79,16 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
|
|||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
|
github.com/fauna/faunadb-go/v5 v5.0.0-beta h1:qjig7OPEsDPH/DJuHWIrOboreYd5aLQnSCzLgHDpnck=
|
||||||
|
github.com/fauna/faunadb-go/v5 v5.0.0-beta/go.mod h1:eoEA8JUERBnzK5/8Rxnetzx326ImTZ8c++wi2GQwrEU=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
|
github.com/gin-contrib/location v0.0.2 h1:QZKh1+K/LLR4KG/61eIO3b7MLuKi8tytQhV6texLgP4=
|
||||||
|
github.com/gin-contrib/location v0.0.2/go.mod h1:NGoidiRlf0BlA/VKSVp+g3cuSMeTmip/63PhEjRhUAc=
|
||||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
|
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||||
github.com/gin-gonic/gin v1.7.2 h1:Tg03T9yM2xa8j6I3Z3oqLaQRSmKvxPd6g/2HJ6zICFA=
|
github.com/gin-gonic/gin v1.7.2 h1:Tg03T9yM2xa8j6I3Z3oqLaQRSmKvxPd6g/2HJ6zICFA=
|
||||||
github.com/gin-gonic/gin v1.7.2/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
|
github.com/gin-gonic/gin v1.7.2/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
@@ -97,6 +102,7 @@ github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8c
|
|||||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||||
|
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||||
github.com/go-playground/validator/v10 v10.8.0 h1:1kAa0fCrnpv+QYdkdcRzrRM7AyYs5o8+jZdJCz9xj6k=
|
github.com/go-playground/validator/v10 v10.8.0 h1:1kAa0fCrnpv+QYdkdcRzrRM7AyYs5o8+jZdJCz9xj6k=
|
||||||
github.com/go-playground/validator/v10 v10.8.0/go.mod h1:9JhgTzTaE31GZDpH/HSvHiRJrJ3iKAgqqH0Bl/Ocjdk=
|
github.com/go-playground/validator/v10 v10.8.0/go.mod h1:9JhgTzTaE31GZDpH/HSvHiRJrJ3iKAgqqH0Bl/Ocjdk=
|
||||||
@@ -452,6 +458,7 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
|
|||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
|
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
|
||||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
@@ -682,9 +689,8 @@ gopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw=
|
|||||||
gopkg.in/readline.v1 v1.0.0-20160726135117-62c6fe619375/go.mod h1:lNEQeAhU009zbRxng+XOj5ITVgY24WcbNnQopyfKoYQ=
|
gopkg.in/readline.v1 v1.0.0-20160726135117-62c6fe619375/go.mod h1:lNEQeAhU009zbRxng+XOj5ITVgY24WcbNnQopyfKoYQ=
|
||||||
gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI=
|
gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI=
|
||||||
gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78=
|
gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78=
|
||||||
|
gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w=
|
||||||
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
|
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
@@ -11,12 +11,10 @@ type AdminSignupInput struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type AuthResponse struct {
|
type AuthResponse struct {
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
AccessToken *string `json:"access_token"`
|
AccessToken *string `json:"access_token"`
|
||||||
IDToken *string `json:"id_token"`
|
ExpiresAt *int64 `json:"expires_at"`
|
||||||
RefreshToken *string `json:"refresh_token"`
|
User *User `json:"user"`
|
||||||
ExpiresIn *int64 `json:"expires_in"`
|
|
||||||
User *User `json:"user"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type DeleteUserInput struct {
|
type DeleteUserInput struct {
|
||||||
@@ -25,11 +23,9 @@ type DeleteUserInput struct {
|
|||||||
|
|
||||||
type Env struct {
|
type Env struct {
|
||||||
AdminSecret *string `json:"ADMIN_SECRET"`
|
AdminSecret *string `json:"ADMIN_SECRET"`
|
||||||
DatabaseName string `json:"DATABASE_NAME"`
|
DatabaseName *string `json:"DATABASE_NAME"`
|
||||||
DatabaseURL string `json:"DATABASE_URL"`
|
DatabaseURL *string `json:"DATABASE_URL"`
|
||||||
DatabaseType string `json:"DATABASE_TYPE"`
|
DatabaseType *string `json:"DATABASE_TYPE"`
|
||||||
ClientID string `json:"CLIENT_ID"`
|
|
||||||
ClientSecret string `json:"CLIENT_SECRET"`
|
|
||||||
CustomAccessTokenScript *string `json:"CUSTOM_ACCESS_TOKEN_SCRIPT"`
|
CustomAccessTokenScript *string `json:"CUSTOM_ACCESS_TOKEN_SCRIPT"`
|
||||||
SMTPHost *string `json:"SMTP_HOST"`
|
SMTPHost *string `json:"SMTP_HOST"`
|
||||||
SMTPPort *string `json:"SMTP_PORT"`
|
SMTPPort *string `json:"SMTP_PORT"`
|
||||||
@@ -38,9 +34,8 @@ type Env struct {
|
|||||||
SenderEmail *string `json:"SENDER_EMAIL"`
|
SenderEmail *string `json:"SENDER_EMAIL"`
|
||||||
JwtType *string `json:"JWT_TYPE"`
|
JwtType *string `json:"JWT_TYPE"`
|
||||||
JwtSecret *string `json:"JWT_SECRET"`
|
JwtSecret *string `json:"JWT_SECRET"`
|
||||||
JwtPrivateKey *string `json:"JWT_PRIVATE_KEY"`
|
|
||||||
JwtPublicKey *string `json:"JWT_PUBLIC_KEY"`
|
|
||||||
AllowedOrigins []string `json:"ALLOWED_ORIGINS"`
|
AllowedOrigins []string `json:"ALLOWED_ORIGINS"`
|
||||||
|
AuthorizerURL *string `json:"AUTHORIZER_URL"`
|
||||||
AppURL *string `json:"APP_URL"`
|
AppURL *string `json:"APP_URL"`
|
||||||
RedisURL *string `json:"REDIS_URL"`
|
RedisURL *string `json:"REDIS_URL"`
|
||||||
CookieName *string `json:"COOKIE_NAME"`
|
CookieName *string `json:"COOKIE_NAME"`
|
||||||
@@ -69,34 +64,27 @@ type Error struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ForgotPasswordInput struct {
|
type ForgotPasswordInput struct {
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
State *string `json:"state"`
|
|
||||||
RedirectURI *string `json:"redirect_uri"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type InviteMemberInput struct {
|
type IsValidJWTQueryInput struct {
|
||||||
Emails []string `json:"emails"`
|
Jwt *string `json:"jwt"`
|
||||||
RedirectURI *string `json:"redirect_uri"`
|
Roles []string `json:"roles"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LoginInput struct {
|
type LoginInput struct {
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
Roles []string `json:"roles"`
|
Roles []string `json:"roles"`
|
||||||
Scope []string `json:"scope"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type MagicLinkLoginInput struct {
|
type MagicLinkLoginInput struct {
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Roles []string `json:"roles"`
|
Roles []string `json:"roles"`
|
||||||
Scope []string `json:"scope"`
|
|
||||||
State *string `json:"state"`
|
|
||||||
RedirectURI *string `json:"redirect_uri"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Meta struct {
|
type Meta struct {
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
ClientID string `json:"client_id"`
|
|
||||||
IsGoogleLoginEnabled bool `json:"is_google_login_enabled"`
|
IsGoogleLoginEnabled bool `json:"is_google_login_enabled"`
|
||||||
IsFacebookLoginEnabled bool `json:"is_facebook_login_enabled"`
|
IsFacebookLoginEnabled bool `json:"is_facebook_login_enabled"`
|
||||||
IsGithubLoginEnabled bool `json:"is_github_login_enabled"`
|
IsGithubLoginEnabled bool `json:"is_github_login_enabled"`
|
||||||
@@ -105,26 +93,6 @@ type Meta struct {
|
|||||||
IsMagicLinkLoginEnabled bool `json:"is_magic_link_login_enabled"`
|
IsMagicLinkLoginEnabled bool `json:"is_magic_link_login_enabled"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type OAuthRevokeInput struct {
|
|
||||||
RefreshToken string `json:"refresh_token"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type PaginatedInput struct {
|
|
||||||
Pagination *PaginationInput `json:"pagination"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Pagination struct {
|
|
||||||
Limit int64 `json:"limit"`
|
|
||||||
Page int64 `json:"page"`
|
|
||||||
Offset int64 `json:"offset"`
|
|
||||||
Total int64 `json:"total"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type PaginationInput struct {
|
|
||||||
Limit *int64 `json:"limit"`
|
|
||||||
Page *int64 `json:"page"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ResendVerifyEmailInput struct {
|
type ResendVerifyEmailInput struct {
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Identifier string `json:"identifier"`
|
Identifier string `json:"identifier"`
|
||||||
@@ -142,7 +110,6 @@ type Response struct {
|
|||||||
|
|
||||||
type SessionQueryInput struct {
|
type SessionQueryInput struct {
|
||||||
Roles []string `json:"roles"`
|
Roles []string `json:"roles"`
|
||||||
Scope []string `json:"scope"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type SignUpInput struct {
|
type SignUpInput struct {
|
||||||
@@ -158,7 +125,6 @@ type SignUpInput struct {
|
|||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
ConfirmPassword string `json:"confirm_password"`
|
ConfirmPassword string `json:"confirm_password"`
|
||||||
Roles []string `json:"roles"`
|
Roles []string `json:"roles"`
|
||||||
Scope []string `json:"scope"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateEnvInput struct {
|
type UpdateEnvInput struct {
|
||||||
@@ -167,14 +133,12 @@ type UpdateEnvInput struct {
|
|||||||
OldAdminSecret *string `json:"OLD_ADMIN_SECRET"`
|
OldAdminSecret *string `json:"OLD_ADMIN_SECRET"`
|
||||||
SMTPHost *string `json:"SMTP_HOST"`
|
SMTPHost *string `json:"SMTP_HOST"`
|
||||||
SMTPPort *string `json:"SMTP_PORT"`
|
SMTPPort *string `json:"SMTP_PORT"`
|
||||||
SMTPUsername *string `json:"SMTP_USERNAME"`
|
|
||||||
SMTPPassword *string `json:"SMTP_PASSWORD"`
|
|
||||||
SenderEmail *string `json:"SENDER_EMAIL"`
|
SenderEmail *string `json:"SENDER_EMAIL"`
|
||||||
|
SenderPassword *string `json:"SENDER_PASSWORD"`
|
||||||
JwtType *string `json:"JWT_TYPE"`
|
JwtType *string `json:"JWT_TYPE"`
|
||||||
JwtSecret *string `json:"JWT_SECRET"`
|
JwtSecret *string `json:"JWT_SECRET"`
|
||||||
JwtPrivateKey *string `json:"JWT_PRIVATE_KEY"`
|
|
||||||
JwtPublicKey *string `json:"JWT_PUBLIC_KEY"`
|
|
||||||
AllowedOrigins []string `json:"ALLOWED_ORIGINS"`
|
AllowedOrigins []string `json:"ALLOWED_ORIGINS"`
|
||||||
|
AuthorizerURL *string `json:"AUTHORIZER_URL"`
|
||||||
AppURL *string `json:"APP_URL"`
|
AppURL *string `json:"APP_URL"`
|
||||||
RedisURL *string `json:"REDIS_URL"`
|
RedisURL *string `json:"REDIS_URL"`
|
||||||
CookieName *string `json:"COOKIE_NAME"`
|
CookieName *string `json:"COOKIE_NAME"`
|
||||||
@@ -247,26 +211,19 @@ type User struct {
|
|||||||
UpdatedAt *int64 `json:"updated_at"`
|
UpdatedAt *int64 `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Users struct {
|
type ValidJWTResponse struct {
|
||||||
Pagination *Pagination `json:"pagination"`
|
Valid bool `json:"valid"`
|
||||||
Users []*User `json:"users"`
|
Message string `json:"message"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type VerificationRequest struct {
|
type VerificationRequest struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Identifier *string `json:"identifier"`
|
Identifier *string `json:"identifier"`
|
||||||
Token *string `json:"token"`
|
Token *string `json:"token"`
|
||||||
Email *string `json:"email"`
|
Email *string `json:"email"`
|
||||||
Expires *int64 `json:"expires"`
|
Expires *int64 `json:"expires"`
|
||||||
CreatedAt *int64 `json:"created_at"`
|
CreatedAt *int64 `json:"created_at"`
|
||||||
UpdatedAt *int64 `json:"updated_at"`
|
UpdatedAt *int64 `json:"updated_at"`
|
||||||
Nonce *string `json:"nonce"`
|
|
||||||
RedirectURI *string `json:"redirect_uri"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type VerificationRequests struct {
|
|
||||||
Pagination *Pagination `json:"pagination"`
|
|
||||||
VerificationRequests []*VerificationRequest `json:"verification_requests"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type VerifyEmailInput struct {
|
type VerifyEmailInput struct {
|
||||||
|
@@ -5,16 +5,8 @@ scalar Int64
|
|||||||
scalar Map
|
scalar Map
|
||||||
scalar Any
|
scalar Any
|
||||||
|
|
||||||
type Pagination {
|
|
||||||
limit: Int64!
|
|
||||||
page: Int64!
|
|
||||||
offset: Int64!
|
|
||||||
total: Int64!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Meta {
|
type Meta {
|
||||||
version: String!
|
version: String!
|
||||||
client_id: String!
|
|
||||||
is_google_login_enabled: Boolean!
|
is_google_login_enabled: Boolean!
|
||||||
is_facebook_login_enabled: Boolean!
|
is_facebook_login_enabled: Boolean!
|
||||||
is_github_login_enabled: Boolean!
|
is_github_login_enabled: Boolean!
|
||||||
@@ -44,11 +36,6 @@ type User {
|
|||||||
updated_at: Int64
|
updated_at: Int64
|
||||||
}
|
}
|
||||||
|
|
||||||
type Users {
|
|
||||||
pagination: Pagination!
|
|
||||||
users: [User!]!
|
|
||||||
}
|
|
||||||
|
|
||||||
type VerificationRequest {
|
type VerificationRequest {
|
||||||
id: ID!
|
id: ID!
|
||||||
identifier: String
|
identifier: String
|
||||||
@@ -57,13 +44,6 @@ type VerificationRequest {
|
|||||||
expires: Int64
|
expires: Int64
|
||||||
created_at: Int64
|
created_at: Int64
|
||||||
updated_at: Int64
|
updated_at: Int64
|
||||||
nonce: String
|
|
||||||
redirect_uri: String
|
|
||||||
}
|
|
||||||
|
|
||||||
type VerificationRequests {
|
|
||||||
pagination: Pagination!
|
|
||||||
verification_requests: [VerificationRequest!]!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Error {
|
type Error {
|
||||||
@@ -74,9 +54,7 @@ type Error {
|
|||||||
type AuthResponse {
|
type AuthResponse {
|
||||||
message: String!
|
message: String!
|
||||||
access_token: String
|
access_token: String
|
||||||
id_token: String
|
expires_at: Int64
|
||||||
refresh_token: String
|
|
||||||
expires_in: Int64
|
|
||||||
user: User
|
user: User
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,13 +62,16 @@ type Response {
|
|||||||
message: String!
|
message: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ValidJWTResponse {
|
||||||
|
valid: Boolean!
|
||||||
|
message: String!
|
||||||
|
}
|
||||||
|
|
||||||
type Env {
|
type Env {
|
||||||
ADMIN_SECRET: String
|
ADMIN_SECRET: String
|
||||||
DATABASE_NAME: String!
|
DATABASE_NAME: String
|
||||||
DATABASE_URL: String!
|
DATABASE_URL: String
|
||||||
DATABASE_TYPE: String!
|
DATABASE_TYPE: String
|
||||||
CLIENT_ID: String!
|
|
||||||
CLIENT_SECRET: String!
|
|
||||||
CUSTOM_ACCESS_TOKEN_SCRIPT: String
|
CUSTOM_ACCESS_TOKEN_SCRIPT: String
|
||||||
SMTP_HOST: String
|
SMTP_HOST: String
|
||||||
SMTP_PORT: String
|
SMTP_PORT: String
|
||||||
@@ -99,9 +80,8 @@ type Env {
|
|||||||
SENDER_EMAIL: String
|
SENDER_EMAIL: String
|
||||||
JWT_TYPE: String
|
JWT_TYPE: String
|
||||||
JWT_SECRET: String
|
JWT_SECRET: String
|
||||||
JWT_PRIVATE_KEY: String
|
|
||||||
JWT_PUBLIC_KEY: String
|
|
||||||
ALLOWED_ORIGINS: [String!]
|
ALLOWED_ORIGINS: [String!]
|
||||||
|
AUTHORIZER_URL: String
|
||||||
APP_URL: String
|
APP_URL: String
|
||||||
REDIS_URL: String
|
REDIS_URL: String
|
||||||
COOKIE_NAME: String
|
COOKIE_NAME: String
|
||||||
@@ -130,14 +110,12 @@ input UpdateEnvInput {
|
|||||||
OLD_ADMIN_SECRET: String
|
OLD_ADMIN_SECRET: String
|
||||||
SMTP_HOST: String
|
SMTP_HOST: String
|
||||||
SMTP_PORT: String
|
SMTP_PORT: String
|
||||||
SMTP_USERNAME: String
|
|
||||||
SMTP_PASSWORD: String
|
|
||||||
SENDER_EMAIL: String
|
SENDER_EMAIL: String
|
||||||
|
SENDER_PASSWORD: String
|
||||||
JWT_TYPE: String
|
JWT_TYPE: String
|
||||||
JWT_SECRET: String
|
JWT_SECRET: String
|
||||||
JWT_PRIVATE_KEY: String
|
|
||||||
JWT_PUBLIC_KEY: String
|
|
||||||
ALLOWED_ORIGINS: [String!]
|
ALLOWED_ORIGINS: [String!]
|
||||||
|
AUTHORIZER_URL: String
|
||||||
APP_URL: String
|
APP_URL: String
|
||||||
REDIS_URL: String
|
REDIS_URL: String
|
||||||
COOKIE_NAME: String
|
COOKIE_NAME: String
|
||||||
@@ -181,14 +159,12 @@ input SignUpInput {
|
|||||||
password: String!
|
password: String!
|
||||||
confirm_password: String!
|
confirm_password: String!
|
||||||
roles: [String!]
|
roles: [String!]
|
||||||
scope: [String!]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input LoginInput {
|
input LoginInput {
|
||||||
email: String!
|
email: String!
|
||||||
password: String!
|
password: String!
|
||||||
roles: [String!]
|
roles: [String!]
|
||||||
scope: [String!]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input VerifyEmailInput {
|
input VerifyEmailInput {
|
||||||
@@ -232,8 +208,6 @@ input UpdateUserInput {
|
|||||||
|
|
||||||
input ForgotPasswordInput {
|
input ForgotPasswordInput {
|
||||||
email: String!
|
email: String!
|
||||||
state: String
|
|
||||||
redirect_uri: String
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input ResetPasswordInput {
|
input ResetPasswordInput {
|
||||||
@@ -249,32 +223,15 @@ input DeleteUserInput {
|
|||||||
input MagicLinkLoginInput {
|
input MagicLinkLoginInput {
|
||||||
email: String!
|
email: String!
|
||||||
roles: [String!]
|
roles: [String!]
|
||||||
scope: [String!]
|
|
||||||
state: String
|
|
||||||
redirect_uri: String
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input SessionQueryInput {
|
input SessionQueryInput {
|
||||||
roles: [String!]
|
roles: [String!]
|
||||||
scope: [String!]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input PaginationInput {
|
input IsValidJWTQueryInput {
|
||||||
limit: Int64
|
jwt: String
|
||||||
page: Int64
|
roles: [String!]
|
||||||
}
|
|
||||||
|
|
||||||
input PaginatedInput {
|
|
||||||
pagination: PaginationInput
|
|
||||||
}
|
|
||||||
|
|
||||||
input OAuthRevokeInput {
|
|
||||||
refresh_token: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
input InviteMemberInput {
|
|
||||||
emails: [String!]!
|
|
||||||
redirect_uri: String
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
@@ -287,7 +244,6 @@ type Mutation {
|
|||||||
resend_verify_email(params: ResendVerifyEmailInput!): Response!
|
resend_verify_email(params: ResendVerifyEmailInput!): Response!
|
||||||
forgot_password(params: ForgotPasswordInput!): Response!
|
forgot_password(params: ForgotPasswordInput!): Response!
|
||||||
reset_password(params: ResetPasswordInput!): Response!
|
reset_password(params: ResetPasswordInput!): Response!
|
||||||
revoke(params: OAuthRevokeInput!): Response!
|
|
||||||
# admin only apis
|
# admin only apis
|
||||||
_delete_user(params: DeleteUserInput!): Response!
|
_delete_user(params: DeleteUserInput!): Response!
|
||||||
_update_user(params: UpdateUserInput!): User!
|
_update_user(params: UpdateUserInput!): User!
|
||||||
@@ -295,16 +251,16 @@ type Mutation {
|
|||||||
_admin_login(params: AdminLoginInput!): Response!
|
_admin_login(params: AdminLoginInput!): Response!
|
||||||
_admin_logout: Response!
|
_admin_logout: Response!
|
||||||
_update_env(params: UpdateEnvInput!): Response!
|
_update_env(params: UpdateEnvInput!): Response!
|
||||||
_invite_members(params: InviteMemberInput!): Response!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Query {
|
type Query {
|
||||||
meta: Meta!
|
meta: Meta!
|
||||||
session(params: SessionQueryInput): AuthResponse!
|
session(params: SessionQueryInput): AuthResponse!
|
||||||
|
is_valid_jwt(params: IsValidJWTQueryInput): ValidJWTResponse!
|
||||||
profile: User!
|
profile: User!
|
||||||
# admin only apis
|
# admin only apis
|
||||||
_users(params: PaginatedInput): Users!
|
_users: [User!]!
|
||||||
_verification_requests(params: PaginatedInput): VerificationRequests!
|
_verification_requests: [VerificationRequest!]!
|
||||||
_admin_session: Response!
|
_admin_session: Response!
|
||||||
_env: Env!
|
_env: Env!
|
||||||
}
|
}
|
||||||
|
@@ -47,10 +47,6 @@ func (r *mutationResolver) ResetPassword(ctx context.Context, params model.Reset
|
|||||||
return resolvers.ResetPasswordResolver(ctx, params)
|
return resolvers.ResetPasswordResolver(ctx, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *mutationResolver) Revoke(ctx context.Context, params model.OAuthRevokeInput) (*model.Response, error) {
|
|
||||||
return resolvers.RevokeResolver(ctx, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *mutationResolver) DeleteUser(ctx context.Context, params model.DeleteUserInput) (*model.Response, error) {
|
func (r *mutationResolver) DeleteUser(ctx context.Context, params model.DeleteUserInput) (*model.Response, error) {
|
||||||
return resolvers.DeleteUserResolver(ctx, params)
|
return resolvers.DeleteUserResolver(ctx, params)
|
||||||
}
|
}
|
||||||
@@ -75,10 +71,6 @@ func (r *mutationResolver) UpdateEnv(ctx context.Context, params model.UpdateEnv
|
|||||||
return resolvers.UpdateEnvResolver(ctx, params)
|
return resolvers.UpdateEnvResolver(ctx, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *mutationResolver) InviteMembers(ctx context.Context, params model.InviteMemberInput) (*model.Response, error) {
|
|
||||||
return resolvers.InviteMembersResolver(ctx, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *queryResolver) Meta(ctx context.Context) (*model.Meta, error) {
|
func (r *queryResolver) Meta(ctx context.Context) (*model.Meta, error) {
|
||||||
return resolvers.MetaResolver(ctx)
|
return resolvers.MetaResolver(ctx)
|
||||||
}
|
}
|
||||||
@@ -87,16 +79,20 @@ func (r *queryResolver) Session(ctx context.Context, params *model.SessionQueryI
|
|||||||
return resolvers.SessionResolver(ctx, params)
|
return resolvers.SessionResolver(ctx, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *queryResolver) IsValidJwt(ctx context.Context, params *model.IsValidJWTQueryInput) (*model.ValidJWTResponse, error) {
|
||||||
|
return resolvers.IsValidJwtResolver(ctx, params)
|
||||||
|
}
|
||||||
|
|
||||||
func (r *queryResolver) Profile(ctx context.Context) (*model.User, error) {
|
func (r *queryResolver) Profile(ctx context.Context) (*model.User, error) {
|
||||||
return resolvers.ProfileResolver(ctx)
|
return resolvers.ProfileResolver(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *queryResolver) Users(ctx context.Context, params *model.PaginatedInput) (*model.Users, error) {
|
func (r *queryResolver) Users(ctx context.Context) ([]*model.User, error) {
|
||||||
return resolvers.UsersResolver(ctx, params)
|
return resolvers.UsersResolver(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *queryResolver) VerificationRequests(ctx context.Context, params *model.PaginatedInput) (*model.VerificationRequests, error) {
|
func (r *queryResolver) VerificationRequests(ctx context.Context) ([]*model.VerificationRequest, error) {
|
||||||
return resolvers.VerificationRequestsResolver(ctx, params)
|
return resolvers.VerificationRequestsResolver(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *queryResolver) AdminSession(ctx context.Context) (*model.Response, error) {
|
func (r *queryResolver) AdminSession(ctx context.Context) (*model.Response, error) {
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -21,31 +22,45 @@ type State struct {
|
|||||||
// AppHandler is the handler for the /app route
|
// AppHandler is the handler for the /app route
|
||||||
func AppHandler() gin.HandlerFunc {
|
func AppHandler() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
hostname := utils.GetHost(c)
|
state := c.Query("state")
|
||||||
if envstore.EnvStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableLoginPage) {
|
|
||||||
c.JSON(400, gin.H{"error": "login page is not enabled"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
redirect_uri := strings.TrimSpace(c.Query("redirect_uri"))
|
var stateObj State
|
||||||
state := strings.TrimSpace(c.Query("state"))
|
|
||||||
scopeString := strings.TrimSpace(c.Query("scope"))
|
if state == "" {
|
||||||
|
stateObj.AuthorizerURL = envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL)
|
||||||
|
stateObj.RedirectURL = stateObj.AuthorizerURL + "/app"
|
||||||
|
|
||||||
var scope []string
|
|
||||||
if scopeString == "" {
|
|
||||||
scope = []string{"openid", "profile", "email"}
|
|
||||||
} else {
|
} else {
|
||||||
scope = strings.Split(scopeString, " ")
|
decodedState, err := utils.DecryptB64(state)
|
||||||
}
|
if err != nil {
|
||||||
|
c.JSON(400, gin.H{"error": "[unable to decode state] invalid state"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal([]byte(decodedState), &stateObj)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(400, gin.H{"error": "[unable to parse state] invalid state"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
stateObj.AuthorizerURL = strings.TrimSuffix(stateObj.AuthorizerURL, "/")
|
||||||
|
stateObj.RedirectURL = strings.TrimSuffix(stateObj.RedirectURL, "/")
|
||||||
|
|
||||||
if redirect_uri == "" {
|
|
||||||
redirect_uri = hostname + "/app"
|
|
||||||
} else {
|
|
||||||
// validate redirect url with allowed origins
|
// validate redirect url with allowed origins
|
||||||
if !utils.IsValidOrigin(redirect_uri) {
|
if !utils.IsValidOrigin(stateObj.RedirectURL) {
|
||||||
c.JSON(400, gin.H{"error": "invalid redirect url"})
|
c.JSON(400, gin.H{"error": "invalid redirect url"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if stateObj.AuthorizerURL == "" {
|
||||||
|
c.JSON(400, gin.H{"error": "invalid authorizer url"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate host and domain of authorizer url
|
||||||
|
if strings.TrimSuffix(stateObj.AuthorizerURL, "/") != envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL) {
|
||||||
|
c.JSON(400, gin.H{"error": "invalid host url"})
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// debug the request state
|
// debug the request state
|
||||||
@@ -56,13 +71,11 @@ func AppHandler() gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.HTML(http.StatusOK, "app.tmpl", gin.H{
|
c.HTML(http.StatusOK, "app.tmpl", gin.H{
|
||||||
"data": map[string]interface{}{
|
"data": map[string]string{
|
||||||
"authorizerURL": hostname,
|
"authorizerURL": stateObj.AuthorizerURL,
|
||||||
"redirectURL": redirect_uri,
|
"redirectURL": stateObj.RedirectURL,
|
||||||
"scope": scope,
|
"organizationName": envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyOrganizationName),
|
||||||
"state": state,
|
"organizationLogo": envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyOrganizationLogo),
|
||||||
"organizationName": envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyOrganizationName),
|
|
||||||
"organizationLogo": envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyOrganizationLogo),
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@@ -1,333 +0,0 @@
|
|||||||
package handlers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
|
||||||
"github.com/authorizerdev/authorizer/server/cookie"
|
|
||||||
"github.com/authorizerdev/authorizer/server/db"
|
|
||||||
"github.com/authorizerdev/authorizer/server/envstore"
|
|
||||||
"github.com/authorizerdev/authorizer/server/sessionstore"
|
|
||||||
"github.com/authorizerdev/authorizer/server/token"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AuthorizeHandler is the handler for the /authorize route
|
|
||||||
// required params
|
|
||||||
// ?redirect_uri = redirect url
|
|
||||||
// ?response_mode = to decide if result should be html or re-direct
|
|
||||||
// state[recommended] = to prevent CSRF attack (for authorizer its compulsory)
|
|
||||||
// code_challenge = to prevent CSRF attack
|
|
||||||
// code_challenge_method = to prevent CSRF attack [only sh256 is supported]
|
|
||||||
|
|
||||||
// check the flow for generating and verifying codes: https://developer.okta.com/blog/2019/08/22/okta-authjs-pkce#:~:text=PKCE%20works%20by%20having%20the,is%20called%20the%20Code%20Challenge.
|
|
||||||
func AuthorizeHandler() gin.HandlerFunc {
|
|
||||||
return func(gc *gin.Context) {
|
|
||||||
redirectURI := strings.TrimSpace(gc.Query("redirect_uri"))
|
|
||||||
responseType := strings.TrimSpace(gc.Query("response_type"))
|
|
||||||
state := strings.TrimSpace(gc.Query("state"))
|
|
||||||
codeChallenge := strings.TrimSpace(gc.Query("code_challenge"))
|
|
||||||
scopeString := strings.TrimSpace(gc.Query("scope"))
|
|
||||||
clientID := strings.TrimSpace(gc.Query("client_id"))
|
|
||||||
template := "authorize.tmpl"
|
|
||||||
responseMode := strings.TrimSpace(gc.Query("response_mode"))
|
|
||||||
|
|
||||||
var scope []string
|
|
||||||
if scopeString == "" {
|
|
||||||
scope = []string{"openid", "profile", "email"}
|
|
||||||
} else {
|
|
||||||
scope = strings.Split(scopeString, " ")
|
|
||||||
}
|
|
||||||
|
|
||||||
if responseMode == "" {
|
|
||||||
responseMode = "query"
|
|
||||||
}
|
|
||||||
|
|
||||||
if responseMode != "query" && responseMode != "web_message" {
|
|
||||||
gc.JSON(400, gin.H{"error": "invalid response mode"})
|
|
||||||
}
|
|
||||||
|
|
||||||
if redirectURI == "" {
|
|
||||||
redirectURI = "/app"
|
|
||||||
}
|
|
||||||
|
|
||||||
isQuery := responseMode == "query"
|
|
||||||
|
|
||||||
loginURL := "/app?state=" + state + "&scope=" + strings.Join(scope, " ") + "&redirect_uri=" + redirectURI
|
|
||||||
|
|
||||||
if clientID == "" {
|
|
||||||
if isQuery {
|
|
||||||
gc.Redirect(http.StatusFound, loginURL)
|
|
||||||
} else {
|
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
|
||||||
"target_origin": redirectURI,
|
|
||||||
"authorization_response": map[string]interface{}{
|
|
||||||
"type": "authorization_response",
|
|
||||||
"response": map[string]string{
|
|
||||||
"error": "client_id is required",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if clientID != envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyClientID) {
|
|
||||||
if isQuery {
|
|
||||||
gc.Redirect(http.StatusFound, loginURL)
|
|
||||||
} else {
|
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
|
||||||
"target_origin": redirectURI,
|
|
||||||
"authorization_response": map[string]interface{}{
|
|
||||||
"type": "authorization_response",
|
|
||||||
"response": map[string]string{
|
|
||||||
"error": "invalid_client_id",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if state == "" {
|
|
||||||
if isQuery {
|
|
||||||
gc.Redirect(http.StatusFound, loginURL)
|
|
||||||
} else {
|
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
|
||||||
"target_origin": redirectURI,
|
|
||||||
"authorization_response": map[string]interface{}{
|
|
||||||
"type": "authorization_response",
|
|
||||||
"response": map[string]string{
|
|
||||||
"error": "state is required",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if responseType == "" {
|
|
||||||
responseType = "token"
|
|
||||||
}
|
|
||||||
|
|
||||||
isResponseTypeCode := responseType == "code"
|
|
||||||
isResponseTypeToken := responseType == "token"
|
|
||||||
|
|
||||||
if !isResponseTypeCode && !isResponseTypeToken {
|
|
||||||
if isQuery {
|
|
||||||
gc.Redirect(http.StatusFound, loginURL)
|
|
||||||
} else {
|
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
|
||||||
"target_origin": redirectURI,
|
|
||||||
"authorization_response": map[string]interface{}{
|
|
||||||
"type": "authorization_response",
|
|
||||||
"response": map[string]string{
|
|
||||||
"error": "response_type is invalid",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if isResponseTypeCode {
|
|
||||||
if codeChallenge == "" {
|
|
||||||
if isQuery {
|
|
||||||
gc.Redirect(http.StatusFound, loginURL)
|
|
||||||
} else {
|
|
||||||
gc.HTML(http.StatusBadRequest, template, gin.H{
|
|
||||||
"target_origin": redirectURI,
|
|
||||||
"authorization_response": map[string]interface{}{
|
|
||||||
"type": "authorization_response",
|
|
||||||
"response": map[string]string{
|
|
||||||
"error": "code_challenge is required",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sessionToken, err := cookie.GetSession(gc)
|
|
||||||
if err != nil {
|
|
||||||
if isQuery {
|
|
||||||
gc.Redirect(http.StatusFound, loginURL)
|
|
||||||
} else {
|
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
|
||||||
"target_origin": redirectURI,
|
|
||||||
"authorization_response": map[string]interface{}{
|
|
||||||
"type": "authorization_response",
|
|
||||||
"response": map[string]string{
|
|
||||||
"error": "login_required",
|
|
||||||
"error_description": "Login is required",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// get session from cookie
|
|
||||||
claims, err := token.ValidateBrowserSession(gc, sessionToken)
|
|
||||||
if err != nil {
|
|
||||||
if isQuery {
|
|
||||||
gc.Redirect(http.StatusFound, loginURL)
|
|
||||||
} else {
|
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
|
||||||
"target_origin": redirectURI,
|
|
||||||
"authorization_response": map[string]interface{}{
|
|
||||||
"type": "authorization_response",
|
|
||||||
"response": map[string]string{
|
|
||||||
"error": "login_required",
|
|
||||||
"error_description": "Login is required",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
userID := claims.Subject
|
|
||||||
user, err := db.Provider.GetUserByID(userID)
|
|
||||||
if err != nil {
|
|
||||||
if isQuery {
|
|
||||||
gc.Redirect(http.StatusFound, loginURL)
|
|
||||||
} else {
|
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
|
||||||
"target_origin": redirectURI,
|
|
||||||
"authorization_response": map[string]interface{}{
|
|
||||||
"type": "authorization_response",
|
|
||||||
"response": map[string]string{
|
|
||||||
"error": "signup_required",
|
|
||||||
"error_description": "Sign up required",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// if user is logged in
|
|
||||||
// based on the response type, generate the response
|
|
||||||
if isResponseTypeCode {
|
|
||||||
// rollover the session for security
|
|
||||||
sessionstore.RemoveState(sessionToken)
|
|
||||||
nonce := uuid.New().String()
|
|
||||||
newSessionTokenData, newSessionToken, err := token.CreateSessionToken(user, nonce, claims.Roles, scope)
|
|
||||||
if err != nil {
|
|
||||||
if isQuery {
|
|
||||||
gc.Redirect(http.StatusFound, loginURL)
|
|
||||||
} else {
|
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
|
||||||
"target_origin": redirectURI,
|
|
||||||
"authorization_response": map[string]interface{}{
|
|
||||||
"type": "authorization_response",
|
|
||||||
"response": map[string]string{
|
|
||||||
"error": "login_required",
|
|
||||||
"error_description": "Login is required",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
sessionstore.SetState(newSessionToken, newSessionTokenData.Nonce+"@"+user.ID)
|
|
||||||
cookie.SetSession(gc, newSessionToken)
|
|
||||||
code := uuid.New().String()
|
|
||||||
sessionstore.SetState(codeChallenge, code+"@"+newSessionToken)
|
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
|
||||||
"target_origin": redirectURI,
|
|
||||||
"authorization_response": map[string]interface{}{
|
|
||||||
"type": "authorization_response",
|
|
||||||
"response": map[string]string{
|
|
||||||
"code": code,
|
|
||||||
"state": state,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if isResponseTypeToken {
|
|
||||||
// rollover the session for security
|
|
||||||
authToken, err := token.CreateAuthToken(gc, user, claims.Roles, scope)
|
|
||||||
if err != nil {
|
|
||||||
if isQuery {
|
|
||||||
gc.Redirect(http.StatusFound, loginURL)
|
|
||||||
} else {
|
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
|
||||||
"target_origin": redirectURI,
|
|
||||||
"authorization_response": map[string]interface{}{
|
|
||||||
"type": "authorization_response",
|
|
||||||
"response": map[string]string{
|
|
||||||
"error": "login_required",
|
|
||||||
"error_description": "Login is required",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
sessionstore.RemoveState(sessionToken)
|
|
||||||
sessionstore.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
|
|
||||||
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
|
||||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
|
||||||
expiresIn := int64(1800)
|
|
||||||
|
|
||||||
// used of query mode
|
|
||||||
params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(expiresIn, 10) + "&state=" + state + "&id_token=" + authToken.IDToken.Token
|
|
||||||
|
|
||||||
res := map[string]interface{}{
|
|
||||||
"access_token": authToken.AccessToken.Token,
|
|
||||||
"id_token": authToken.IDToken.Token,
|
|
||||||
"state": state,
|
|
||||||
"scope": scope,
|
|
||||||
"token_type": "Bearer",
|
|
||||||
"expires_in": expiresIn,
|
|
||||||
}
|
|
||||||
|
|
||||||
if authToken.RefreshToken != nil {
|
|
||||||
res["refresh_token"] = authToken.RefreshToken.Token
|
|
||||||
params += "&refresh_token=" + authToken.RefreshToken.Token
|
|
||||||
sessionstore.SetState(authToken.RefreshToken.Token, authToken.FingerPrint+"@"+user.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
if isQuery {
|
|
||||||
if strings.Contains(redirectURI, "?") {
|
|
||||||
gc.Redirect(http.StatusFound, redirectURI+"&"+params)
|
|
||||||
} else {
|
|
||||||
gc.Redirect(http.StatusFound, redirectURI+"?"+params)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
|
||||||
"target_origin": redirectURI,
|
|
||||||
"authorization_response": map[string]interface{}{
|
|
||||||
"type": "authorization_response",
|
|
||||||
"response": res,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if isQuery {
|
|
||||||
gc.Redirect(http.StatusFound, loginURL)
|
|
||||||
} else {
|
|
||||||
// by default return with error
|
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
|
||||||
"target_origin": redirectURI,
|
|
||||||
"authorization_response": map[string]interface{}{
|
|
||||||
"type": "authorization_response",
|
|
||||||
"response": map[string]string{
|
|
||||||
"error": "login_required",
|
|
||||||
"error_description": "Login is required",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -13,7 +13,7 @@ func DashboardHandler() gin.HandlerFunc {
|
|||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
isOnboardingCompleted := false
|
isOnboardingCompleted := false
|
||||||
|
|
||||||
if envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminSecret) != "" {
|
if envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminSecret) != "" {
|
||||||
isOnboardingCompleted = true
|
isOnboardingCompleted = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,15 +0,0 @@
|
|||||||
package handlers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
// HealthHandler is the handler for /health route.
|
|
||||||
// It states if server is in healthy state or not
|
|
||||||
func HealthHandler() gin.HandlerFunc {
|
|
||||||
return func(c *gin.Context) {
|
|
||||||
c.String(http.StatusOK, "OK")
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,30 +0,0 @@
|
|||||||
package handlers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
|
||||||
"github.com/authorizerdev/authorizer/server/envstore"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
func JWKsHandler() gin.HandlerFunc {
|
|
||||||
return func(c *gin.Context) {
|
|
||||||
var data map[string]string
|
|
||||||
jwk := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJWK)
|
|
||||||
err := json.Unmarshal([]byte(jwk), &data)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(500, gin.H{
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(200, gin.H{
|
|
||||||
"keys": []map[string]string{
|
|
||||||
data,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,47 +0,0 @@
|
|||||||
package handlers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/cookie"
|
|
||||||
"github.com/authorizerdev/authorizer/server/crypto"
|
|
||||||
"github.com/authorizerdev/authorizer/server/sessionstore"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Handler to logout user
|
|
||||||
func LogoutHandler() gin.HandlerFunc {
|
|
||||||
return func(gc *gin.Context) {
|
|
||||||
redirectURL := strings.TrimSpace(gc.Query("redirect_uri"))
|
|
||||||
// get fingerprint hash
|
|
||||||
fingerprintHash, err := cookie.GetSession(gc)
|
|
||||||
if err != nil {
|
|
||||||
gc.JSON(http.StatusUnauthorized, gin.H{
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
decryptedFingerPrint, err := crypto.DecryptAES(fingerprintHash)
|
|
||||||
if err != nil {
|
|
||||||
gc.JSON(http.StatusUnauthorized, gin.H{
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fingerPrint := string(decryptedFingerPrint)
|
|
||||||
|
|
||||||
sessionstore.RemoveState(fingerPrint)
|
|
||||||
cookie.DeleteSession(gc)
|
|
||||||
|
|
||||||
if redirectURL != "" {
|
|
||||||
gc.Redirect(http.StatusFound, redirectURL)
|
|
||||||
} else {
|
|
||||||
gc.JSON(http.StatusOK, gin.H{
|
|
||||||
"message": "Logged out successfully",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -7,7 +7,6 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -31,23 +30,22 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
|||||||
provider := c.Param("oauth_provider")
|
provider := c.Param("oauth_provider")
|
||||||
state := c.Request.FormValue("state")
|
state := c.Request.FormValue("state")
|
||||||
|
|
||||||
sessionState := sessionstore.GetState(state)
|
sessionState := sessionstore.GetSocailLoginState(state)
|
||||||
if sessionState == "" {
|
if sessionState == "" {
|
||||||
c.JSON(400, gin.H{"error": "invalid oauth state"})
|
c.JSON(400, gin.H{"error": "invalid oauth state"})
|
||||||
}
|
}
|
||||||
sessionstore.GetState(state)
|
sessionstore.RemoveSocialLoginState(state)
|
||||||
// contains random token, redirect url, role
|
// contains random token, redirect url, role
|
||||||
sessionSplit := strings.Split(state, "___")
|
sessionSplit := strings.Split(state, "___")
|
||||||
|
|
||||||
if len(sessionSplit) < 3 {
|
// TODO validate redirect url
|
||||||
|
if len(sessionSplit) < 2 {
|
||||||
c.JSON(400, gin.H{"error": "invalid redirect url"})
|
c.JSON(400, gin.H{"error": "invalid redirect url"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
stateValue := sessionSplit[0]
|
|
||||||
redirectURL := sessionSplit[1]
|
|
||||||
inputRoles := strings.Split(sessionSplit[2], ",")
|
inputRoles := strings.Split(sessionSplit[2], ",")
|
||||||
scopes := strings.Split(sessionSplit[3], ",")
|
redirectURL := sessionSplit[1]
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
user := models.User{}
|
user := models.User{}
|
||||||
@@ -76,7 +74,7 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
|||||||
// make sure inputRoles don't include protected roles
|
// make sure inputRoles don't include protected roles
|
||||||
hasProtectedRole := false
|
hasProtectedRole := false
|
||||||
for _, ir := range inputRoles {
|
for _, ir := range inputRoles {
|
||||||
if utils.StringSliceContains(envstore.EnvStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyProtectedRoles), ir) {
|
if utils.StringSliceContains(envstore.EnvInMemoryStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyProtectedRoles), ir) {
|
||||||
hasProtectedRole = true
|
hasProtectedRole = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -98,13 +96,8 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
|||||||
if !strings.Contains(signupMethod, provider) {
|
if !strings.Contains(signupMethod, provider) {
|
||||||
signupMethod = signupMethod + "," + provider
|
signupMethod = signupMethod + "," + provider
|
||||||
}
|
}
|
||||||
user = existingUser
|
|
||||||
user.SignupMethods = signupMethod
|
user.SignupMethods = signupMethod
|
||||||
|
user.Password = existingUser.Password
|
||||||
if user.EmailVerifiedAt == nil {
|
|
||||||
now := time.Now().Unix()
|
|
||||||
user.EmailVerifiedAt = &now
|
|
||||||
}
|
|
||||||
|
|
||||||
// There multiple scenarios with roles here in social login
|
// There multiple scenarios with roles here in social login
|
||||||
// 1. user has access to protected roles + roles and trying to login
|
// 1. user has access to protected roles + roles and trying to login
|
||||||
@@ -124,7 +117,7 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
|||||||
// check if it contains protected unassigned role
|
// check if it contains protected unassigned role
|
||||||
hasProtectedRole := false
|
hasProtectedRole := false
|
||||||
for _, ur := range unasignedRoles {
|
for _, ur := range unasignedRoles {
|
||||||
if utils.StringSliceContains(envstore.EnvStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyProtectedRoles), ur) {
|
if utils.StringSliceContains(envstore.EnvInMemoryStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyProtectedRoles), ur) {
|
||||||
hasProtectedRole = true
|
hasProtectedRole = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,36 +131,17 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
|||||||
} else {
|
} else {
|
||||||
user.Roles = existingUser.Roles
|
user.Roles = existingUser.Roles
|
||||||
}
|
}
|
||||||
|
user.Key = existingUser.Key
|
||||||
|
user.ID = existingUser.ID
|
||||||
user, err = db.Provider.UpdateUser(user)
|
user, err = db.Provider.UpdateUser(user)
|
||||||
if err != nil {
|
|
||||||
c.JSON(500, gin.H{"error": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
authToken, err := token.CreateAuthToken(c, user, inputRoles, scopes)
|
user, _ = db.Provider.GetUserByEmail(user.Email)
|
||||||
if err != nil {
|
|
||||||
c.JSON(500, gin.H{"error": err.Error()})
|
|
||||||
}
|
|
||||||
expiresIn := int64(1800)
|
|
||||||
params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(expiresIn, 10) + "&state=" + stateValue + "&id_token=" + authToken.IDToken.Token
|
|
||||||
|
|
||||||
cookie.SetSession(c, authToken.FingerPrintHash)
|
authToken, _ := token.CreateAuthToken(user, inputRoles)
|
||||||
sessionstore.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
|
sessionstore.SetUserSession(user.ID, authToken.FingerPrint, authToken.RefreshToken.Token)
|
||||||
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
cookie.SetCookie(c, authToken.AccessToken.Token, authToken.RefreshToken.Token, authToken.FingerPrintHash)
|
||||||
|
utils.SaveSessionInDB(user.ID, c)
|
||||||
if authToken.RefreshToken != nil {
|
|
||||||
params = params + `&refresh_token=` + authToken.RefreshToken.Token
|
|
||||||
sessionstore.SetState(authToken.RefreshToken.Token, authToken.FingerPrint+"@"+user.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
go utils.SaveSessionInDB(c, user.ID)
|
|
||||||
if strings.Contains(redirectURL, "?") {
|
|
||||||
redirectURL = redirectURL + "&" + params
|
|
||||||
} else {
|
|
||||||
redirectURL = redirectURL + "?" + params
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Redirect(http.StatusTemporaryRedirect, redirectURL)
|
c.Redirect(http.StatusTemporaryRedirect, redirectURL)
|
||||||
}
|
}
|
||||||
@@ -247,7 +221,7 @@ func processGithubUserInfo(code string) (models.User, error) {
|
|||||||
GivenName: &firstName,
|
GivenName: &firstName,
|
||||||
FamilyName: &lastName,
|
FamilyName: &lastName,
|
||||||
Picture: &picture,
|
Picture: &picture,
|
||||||
Email: userRawData["sub"],
|
Email: userRawData["email"],
|
||||||
}
|
}
|
||||||
|
|
||||||
return user, nil
|
return user, nil
|
||||||
@@ -280,7 +254,7 @@ func processFacebookUserInfo(code string) (models.User, error) {
|
|||||||
userRawData := make(map[string]interface{})
|
userRawData := make(map[string]interface{})
|
||||||
json.Unmarshal(body, &userRawData)
|
json.Unmarshal(body, &userRawData)
|
||||||
|
|
||||||
email := fmt.Sprintf("%v", userRawData["sub"])
|
email := fmt.Sprintf("%v", userRawData["email"])
|
||||||
|
|
||||||
picObject := userRawData["picture"].(map[string]interface{})["data"]
|
picObject := userRawData["picture"].(map[string]interface{})["data"]
|
||||||
picDataObject := picObject.(map[string]interface{})
|
picDataObject := picObject.(map[string]interface{})
|
||||||
|
@@ -10,55 +10,41 @@ import (
|
|||||||
"github.com/authorizerdev/authorizer/server/sessionstore"
|
"github.com/authorizerdev/authorizer/server/sessionstore"
|
||||||
"github.com/authorizerdev/authorizer/server/utils"
|
"github.com/authorizerdev/authorizer/server/utils"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
// OAuthLoginHandler set host in the oauth state that is useful for redirecting to oauth_callback
|
// OAuthLoginHandler set host in the oauth state that is useful for redirecting to oauth_callback
|
||||||
func OAuthLoginHandler() gin.HandlerFunc {
|
func OAuthLoginHandler() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
hostname := utils.GetHost(c)
|
// TODO validate redirect URL
|
||||||
redirectURI := strings.TrimSpace(c.Query("redirectURL"))
|
redirectURL := c.Query("redirectURL")
|
||||||
roles := strings.TrimSpace(c.Query("roles"))
|
roles := c.Query("roles")
|
||||||
state := strings.TrimSpace(c.Query("state"))
|
|
||||||
scopeString := strings.TrimSpace(c.Query("scope"))
|
|
||||||
|
|
||||||
if redirectURI == "" {
|
if redirectURL == "" {
|
||||||
c.JSON(400, gin.H{
|
c.JSON(400, gin.H{
|
||||||
"error": "invalid redirect uri",
|
"error": "invalid redirect url",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if state == "" {
|
|
||||||
c.JSON(400, gin.H{
|
|
||||||
"error": "invalid state",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var scope []string
|
|
||||||
if scopeString == "" {
|
|
||||||
scope = []string{"openid", "profile", "email"}
|
|
||||||
} else {
|
|
||||||
scope = strings.Split(scopeString, " ")
|
|
||||||
}
|
|
||||||
|
|
||||||
if roles != "" {
|
if roles != "" {
|
||||||
// validate role
|
// validate role
|
||||||
rolesSplit := strings.Split(roles, ",")
|
rolesSplit := strings.Split(roles, ",")
|
||||||
|
|
||||||
// use protected roles verification for admin login only.
|
// use protected roles verification for admin login only.
|
||||||
// though if not associated with user, it will be rejected from oauth_callback
|
// though if not associated with user, it will be rejected from oauth_callback
|
||||||
if !utils.IsValidRoles(append([]string{}, append(envstore.EnvStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyRoles), envstore.EnvStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyProtectedRoles)...)...), rolesSplit) {
|
if !utils.IsValidRoles(append([]string{}, append(envstore.EnvInMemoryStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyRoles), envstore.EnvInMemoryStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyProtectedRoles)...)...), rolesSplit) {
|
||||||
c.JSON(400, gin.H{
|
c.JSON(400, gin.H{
|
||||||
"error": "invalid role",
|
"error": "invalid role",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
roles = strings.Join(envstore.EnvStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyDefaultRoles), ",")
|
roles = strings.Join(envstore.EnvInMemoryStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyDefaultRoles), ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
oauthStateString := state + "___" + redirectURI + "___" + roles + "___" + strings.Join(scope, ",")
|
uuid := uuid.New()
|
||||||
|
oauthStateString := uuid.String() + "___" + redirectURL + "___" + roles
|
||||||
|
|
||||||
provider := c.Param("oauth_provider")
|
provider := c.Param("oauth_provider")
|
||||||
isProviderConfigured := true
|
isProviderConfigured := true
|
||||||
@@ -68,9 +54,9 @@ func OAuthLoginHandler() gin.HandlerFunc {
|
|||||||
isProviderConfigured = false
|
isProviderConfigured = false
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
sessionstore.SetState(oauthStateString, constants.SignupMethodGoogle)
|
sessionstore.SetSocailLoginState(oauthStateString, constants.SignupMethodGoogle)
|
||||||
// during the init of OAuthProvider authorizer url might be empty
|
// during the init of OAuthProvider authorizer url might be empty
|
||||||
oauth.OAuthProviders.GoogleConfig.RedirectURL = hostname + "/oauth_callback/google"
|
oauth.OAuthProviders.GoogleConfig.RedirectURL = envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL) + "/oauth_callback/google"
|
||||||
url := oauth.OAuthProviders.GoogleConfig.AuthCodeURL(oauthStateString)
|
url := oauth.OAuthProviders.GoogleConfig.AuthCodeURL(oauthStateString)
|
||||||
c.Redirect(http.StatusTemporaryRedirect, url)
|
c.Redirect(http.StatusTemporaryRedirect, url)
|
||||||
case constants.SignupMethodGithub:
|
case constants.SignupMethodGithub:
|
||||||
@@ -78,8 +64,8 @@ func OAuthLoginHandler() gin.HandlerFunc {
|
|||||||
isProviderConfigured = false
|
isProviderConfigured = false
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
sessionstore.SetState(oauthStateString, constants.SignupMethodGithub)
|
sessionstore.SetSocailLoginState(oauthStateString, constants.SignupMethodGithub)
|
||||||
oauth.OAuthProviders.GithubConfig.RedirectURL = hostname + "/oauth_callback/github"
|
oauth.OAuthProviders.GithubConfig.RedirectURL = envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL) + "/oauth_callback/github"
|
||||||
url := oauth.OAuthProviders.GithubConfig.AuthCodeURL(oauthStateString)
|
url := oauth.OAuthProviders.GithubConfig.AuthCodeURL(oauthStateString)
|
||||||
c.Redirect(http.StatusTemporaryRedirect, url)
|
c.Redirect(http.StatusTemporaryRedirect, url)
|
||||||
case constants.SignupMethodFacebook:
|
case constants.SignupMethodFacebook:
|
||||||
@@ -87,8 +73,8 @@ func OAuthLoginHandler() gin.HandlerFunc {
|
|||||||
isProviderConfigured = false
|
isProviderConfigured = false
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
sessionstore.SetState(oauthStateString, constants.SignupMethodFacebook)
|
sessionstore.SetSocailLoginState(oauthStateString, constants.SignupMethodFacebook)
|
||||||
oauth.OAuthProviders.FacebookConfig.RedirectURL = hostname + "/oauth_callback/facebook"
|
oauth.OAuthProviders.FacebookConfig.RedirectURL = envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL) + "/oauth_callback/facebook"
|
||||||
url := oauth.OAuthProviders.FacebookConfig.AuthCodeURL(oauthStateString)
|
url := oauth.OAuthProviders.FacebookConfig.AuthCodeURL(oauthStateString)
|
||||||
c.Redirect(http.StatusTemporaryRedirect, url)
|
c.Redirect(http.StatusTemporaryRedirect, url)
|
||||||
default:
|
default:
|
||||||
|
@@ -1,30 +0,0 @@
|
|||||||
package handlers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
|
||||||
"github.com/authorizerdev/authorizer/server/envstore"
|
|
||||||
"github.com/authorizerdev/authorizer/server/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
// OpenIDConfigurationHandler handler for open-id configurations
|
|
||||||
func OpenIDConfigurationHandler() gin.HandlerFunc {
|
|
||||||
return func(c *gin.Context) {
|
|
||||||
issuer := utils.GetHost(c)
|
|
||||||
jwtType := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtType)
|
|
||||||
|
|
||||||
c.JSON(200, gin.H{
|
|
||||||
"issuer": issuer,
|
|
||||||
"authorization_endpoint": issuer + "/authorize",
|
|
||||||
"token_endpoint": issuer + "/token",
|
|
||||||
"userinfo_endpoint": issuer + "/userinfo",
|
|
||||||
"jwks_uri": issuer + "/.well-known/jwks.json",
|
|
||||||
"response_types_supported": []string{"code", "token", "id_token", "code token", "code id_token", "token id_token", "code token id_token"},
|
|
||||||
"scopes_supported": []string{"openid", "email", "profile", "email_verified", "given_name", "family_name", "nick_name", "picture"},
|
|
||||||
"response_modes_supported": []string{"query", "fragment", "form_post"},
|
|
||||||
"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", "gender", "birthdate", "phone_number", "phone_number_verified"},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|