Compare commits
178 Commits
0.14.0-bet
...
0.32.0-bet
Author | SHA1 | Date | |
---|---|---|---|
![]() |
484d0c0882 | ||
![]() |
be59c3615f | ||
![]() |
db351f7771 | ||
![]() |
91c29c4092 | ||
![]() |
415b97535e | ||
![]() |
7d1272d815 | ||
![]() |
c9ba0b13f8 | ||
![]() |
fadd9f6168 | ||
![]() |
395e2e2a85 | ||
![]() |
6335084835 | ||
![]() |
eab336cd3d | ||
![]() |
f4691fca1f | ||
![]() |
341d4fbae5 | ||
![]() |
e467b45ab1 | ||
![]() |
7edfad3486 | ||
![]() |
80578b88ac | ||
![]() |
5646e7a0e7 | ||
![]() |
53a592ef63 | ||
![]() |
3337dbd0a4 | ||
![]() |
82a2a42f84 | ||
![]() |
ac49b5bb70 | ||
![]() |
926ab07c07 | ||
![]() |
7a2dbea019 | ||
![]() |
dff50097e8 | ||
![]() |
aff9d3af20 | ||
![]() |
02eb1d6677 | ||
![]() |
78a673e4ad | ||
![]() |
e0d8644264 | ||
![]() |
d8c662eaad | ||
![]() |
6d1d259f71 | ||
![]() |
2841853d37 | ||
![]() |
360dd3c3bd | ||
![]() |
c6add0cca6 | ||
![]() |
7ac6252aac | ||
![]() |
5d2d1c342b | ||
![]() |
6da0a85936 | ||
![]() |
116972d725 | ||
![]() |
d1e1e287db | ||
![]() |
a7f04f8754 | ||
![]() |
69b56c9912 | ||
![]() |
98015708a2 | ||
![]() |
1b5a7b8fb0 | ||
![]() |
8b9bcdfdbe | ||
![]() |
ba429da05f | ||
![]() |
7c7bb42003 | ||
![]() |
eeff88c853 | ||
![]() |
cf8762b7a0 | ||
![]() |
c61c3024ec | ||
![]() |
7e3bd6a721 | ||
![]() |
1146468a03 | ||
![]() |
268b22ffb2 | ||
![]() |
43359f1dba | ||
![]() |
1941cf4299 | ||
![]() |
7b13034081 | ||
![]() |
7c16900618 | ||
![]() |
d722fe258d | ||
![]() |
99dc5ee572 | ||
![]() |
8bee421d0a | ||
![]() |
714b79e4ab | ||
![]() |
d886d780b4 | ||
![]() |
d7bb10fd21 | ||
![]() |
f5515bec28 | ||
![]() |
b35d86fd40 | ||
![]() |
a638f02014 | ||
![]() |
2c4bc9adb6 | ||
![]() |
5884802e60 | ||
![]() |
241f977b2a | ||
![]() |
ed855a274a | ||
![]() |
049ea64475 | ||
![]() |
5e4f34c889 | ||
![]() |
ab717d956a | ||
![]() |
6209c4d506 | ||
![]() |
2bc4c74930 | ||
![]() |
1efa419cdf | ||
![]() |
4ceb6db4ba | ||
![]() |
9edc8d0fb5 | ||
![]() |
da0fcb109b | ||
![]() |
3e51a7bd01 | ||
![]() |
28bed69b2e | ||
![]() |
0433d64737 | ||
![]() |
773213e5a4 | ||
![]() |
de44c40de5 | ||
![]() |
a7fa988bf0 | ||
![]() |
538a2d0b59 | ||
![]() |
f519f0eb0e | ||
![]() |
d5ad4a6e55 | ||
![]() |
d9b49ca932 | ||
![]() |
7c5aab7bf3 | ||
![]() |
c783e101d5 | ||
![]() |
ebccfb18cd | ||
![]() |
b7aeff57af | ||
![]() |
075c287f34 | ||
![]() |
4778827545 | ||
![]() |
39c2c364d9 | ||
![]() |
961f2271c1 | ||
![]() |
aaf0831793 | ||
![]() |
27cb41c54c | ||
![]() |
718b2d535f | ||
![]() |
ed6a1ceccc | ||
![]() |
fd52d6e5d3 | ||
![]() |
325aa88368 | ||
![]() |
75e44ff698 | ||
![]() |
d5f1c5a5eb | ||
![]() |
39947f1753 | ||
![]() |
4fa9f79c3f | ||
![]() |
fe73c2f6f8 | ||
![]() |
4a3e3633ea | ||
![]() |
dbbe36f6b5 | ||
![]() |
819dd57377 | ||
![]() |
044b025ba2 | ||
![]() |
41b5f00b83 | ||
![]() |
3c31b7fdc7 | ||
![]() |
7e91c6ca28 | ||
![]() |
b1b43a41ca | ||
![]() |
f969495178 | ||
![]() |
3c4c128931 | ||
![]() |
003cec4f48 | ||
![]() |
3e488155dc | ||
![]() |
4f4a3a91e1 | ||
![]() |
a3d9783aef | ||
![]() |
7d77396657 | ||
![]() |
7a18fc6312 | ||
![]() |
90e2709eeb | ||
![]() |
4c4743ac24 | ||
![]() |
b2541c8e9a | ||
![]() |
1f3dec6ea6 | ||
![]() |
a6b743465f | ||
![]() |
f356b4728d | ||
![]() |
ec4ef97766 | ||
![]() |
47d67bf3cd | ||
![]() |
0c54da1168 | ||
![]() |
d6f60ce464 | ||
![]() |
3aa888b14e | ||
![]() |
30be32a10b | ||
![]() |
69d781d6cf | ||
![]() |
e4d9c60971 | ||
![]() |
96edb43b67 | ||
![]() |
21fef67c7d | ||
![]() |
9f09823c8b | ||
![]() |
1a64149da7 | ||
![]() |
99b846811a | ||
![]() |
df7837f44d | ||
![]() |
d709f53c47 | ||
![]() |
a257b77501 | ||
![]() |
2213619ed5 | ||
![]() |
f65ea72944 | ||
![]() |
32f8c99a71 | ||
![]() |
8ec52a90f1 | ||
![]() |
2498958295 | ||
![]() |
2913fa0603 | ||
![]() |
e126bfddad | ||
![]() |
83001b859c | ||
![]() |
74a8024131 | ||
![]() |
5e6ee8d9b0 | ||
![]() |
3e7150f872 | ||
![]() |
9a19552f72 | ||
![]() |
ab01ff249d | ||
![]() |
1b387f7564 | ||
![]() |
8e79ab77b2 | ||
![]() |
2bf6b8f91d | ||
![]() |
776c0fba8b | ||
![]() |
dd64aa2e79 | ||
![]() |
157b13baa7 | ||
![]() |
d1e284116d | ||
![]() |
2f9725d8e1 | ||
![]() |
ee7aea7bee | ||
![]() |
5d73df0040 | ||
![]() |
60cd317e67 | ||
![]() |
f5bdc8db39 | ||
![]() |
9eca697a91 | ||
![]() |
7136ee924d | ||
![]() |
fd9eb7c733 | ||
![]() |
917eaeb2ed | ||
![]() |
3bb90acc9e | ||
![]() |
a69b8e290c | ||
![]() |
674eeeea4e | ||
![]() |
8c2bf6ee0d | ||
![]() |
57bc091499 |
@@ -9,3 +9,4 @@ build
|
||||
data.db
|
||||
app/node_modules
|
||||
app/build
|
||||
certs/
|
||||
|
@@ -1,3 +1,4 @@
|
||||
ENV=production
|
||||
DATABASE_URL=data.db
|
||||
DATABASE_TYPE=sqlite
|
||||
CUSTOM_ACCESS_TOKEN_SCRIPT="function(user,tokenPayload){var data = tokenPayload;data.extra = {'x-extra-id': user.id};return data;}"
|
9
.env.test
Normal file
9
.env.test
Normal file
@@ -0,0 +1,9 @@
|
||||
ENV=test
|
||||
DATABASE_URL=test.db
|
||||
DATABASE_TYPE=sqlite
|
||||
CUSTOM_ACCESS_TOKEN_SCRIPT="function(user,tokenPayload){var data = tokenPayload;data.extra = {'x-extra-id': user.id};return data;}"
|
||||
SMTP_HOST=smtp.mailtrap.io
|
||||
SMTP_PORT=2525
|
||||
SMTP_USERNAME=test
|
||||
SMTP_PASSWORD=test
|
||||
SENDER_EMAIL="info@authorizer.dev"
|
15
.github/workflows/release.yaml
vendored
15
.github/workflows/release.yaml
vendored
@@ -1,4 +1,19 @@
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
logLevel:
|
||||
description: 'Log level'
|
||||
required: true
|
||||
default: 'warning'
|
||||
type: choice
|
||||
options:
|
||||
- info
|
||||
- warning
|
||||
- debug
|
||||
tags:
|
||||
description: 'Tags'
|
||||
required: false
|
||||
type: boolean
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
|
6
.gitignore
vendored
6
.gitignore
vendored
@@ -8,7 +8,11 @@ dashboard/build
|
||||
build
|
||||
.env
|
||||
data.db
|
||||
test.db
|
||||
.DS_Store
|
||||
.env.local
|
||||
*.tar.gz
|
||||
.vscode/
|
||||
.vscode/
|
||||
.yalc
|
||||
yalc.lock
|
||||
certs/
|
2
Makefile
2
Makefile
@@ -10,7 +10,7 @@ build-dashboard:
|
||||
clean:
|
||||
rm -rf build
|
||||
test:
|
||||
cd server && go clean --testcache && go test -v ./test
|
||||
rm -rf server/test/test.db && rm -rf test.db && cd server && go clean --testcache && go test -p 1 -v ./test
|
||||
generate:
|
||||
cd server && go get github.com/99designs/gqlgen/cmd@v0.14.0 && go run github.com/99designs/gqlgen generate
|
||||
|
88
README.md
88
README.md
@@ -26,18 +26,16 @@
|
||||
- ✅ Sign-in / Sign-up with email ID and password
|
||||
- ✅ Secure session management
|
||||
- ✅ Email verification
|
||||
- ✅ OAuth2 and OpenID compatible APIs
|
||||
- ✅ APIs to update profile securely
|
||||
- ✅ Forgot password flow using email
|
||||
- ✅ Social logins (Google, Github, Facebook, more coming soon)
|
||||
- ✅ Role-based access management
|
||||
- ✅ Password-less login with email and magic link
|
||||
- ✅ Password-less login with magic link login
|
||||
|
||||
## Roadmap
|
||||
|
||||
- Support more JWT encryption algorithms (Currently supporting HS256)
|
||||
- 2 Factor authentication
|
||||
- Back office (Admin dashboard to manage user)
|
||||
- Support more database
|
||||
- VueJS SDK
|
||||
- Svelte SDK
|
||||
- React Native SDK
|
||||
@@ -59,35 +57,42 @@
|
||||
|
||||
# Getting Started
|
||||
|
||||
## Trying out Authorizer
|
||||
## Step 1: Get Authorizer Instance
|
||||
|
||||
### Deploy Production Ready Instance
|
||||
|
||||
Deploy production ready Authorizer instance using one click deployment options available below
|
||||
|
||||
| **Infra provider** | **One-click link** | **Additional information** |
|
||||
| :----------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------: |
|
||||
| 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) |
|
||||
| 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) |
|
||||
|
||||
### Deploy Authorizer Using Source Code
|
||||
|
||||
This guide helps you practice using Authorizer to evaluate it before you use it in a production environment. It includes instructions for installing the Authorizer server in local or standalone mode.
|
||||
|
||||
- [Install using source code](#install-using-source-code)
|
||||
- [Install using binaries](#install-using-binaries)
|
||||
- [Install instance on heroku](#install-instance-on-Heroku)
|
||||
- [Install instance on railway.app](#install-instance-on-railway)
|
||||
#### Install using source code
|
||||
|
||||
## Install using source code
|
||||
|
||||
### Prerequisites
|
||||
#### Prerequisites
|
||||
|
||||
- OS: Linux or macOS or windows
|
||||
- Go: (Golang)(https://golang.org/dl/) >= v1.15
|
||||
|
||||
### Project Setup
|
||||
#### Project Setup
|
||||
|
||||
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
|
||||
3. Change directory to authorizer: `cd authorizer`
|
||||
5. Create Env file `cp .env.sample .env`. Check all the supported env [here](https://docs.authorizer.dev/core/env/)
|
||||
6. Build Dashboard `make build-dashboard`
|
||||
7. Build App `make build-app`
|
||||
8. Build Server `make clean && make`
|
||||
4. Create Env file `cp .env.sample .env`. Check all the supported env [here](https://docs.authorizer.dev/core/env/)
|
||||
5. Build Dashboard `make build-dashboard`
|
||||
6. Build App `make build-app`
|
||||
7. 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
|
||||
9. Run binary `./build/server`
|
||||
8. Run binary `./build/server`
|
||||
|
||||
## Install using binaries
|
||||
### Deploy Authorizer using binaries
|
||||
|
||||
Deploy / Try Authorizer using binaries. With each [Authorizer Release](https://github.com/authorizerdev/authorizer/releases)
|
||||
binaries are baked with required deployment files and bundled. You can download a specific version of it for the following operating systems:
|
||||
@@ -95,7 +100,7 @@ binaries are baked with required deployment files and bundled. You can download
|
||||
- Mac OSX
|
||||
- Linux
|
||||
|
||||
### Step 1: Download and unzip bundle
|
||||
#### Download and unzip bundle
|
||||
|
||||
- Download the Bundle for the specific OS from the [release page](https://github.com/authorizerdev/authorizer/releases)
|
||||
|
||||
@@ -115,11 +120,7 @@ binaries are baked with required deployment files and bundled. You can download
|
||||
cd authorizer
|
||||
```
|
||||
|
||||
### Step 2: Configure environment variables
|
||||
|
||||
Required environment variables are pre-configured in `.env` file. But based on the production requirements, please configure more environment variables. You can refer to [environment variables docs](/core/env) for more information.
|
||||
|
||||
### Step 3: Start Authorizer
|
||||
#### Step 3: Start Authorizer
|
||||
|
||||
- Run following command to start authorizer
|
||||
|
||||
@@ -131,20 +132,20 @@ 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`
|
||||
|
||||
Deploy production ready Authorizer instance using one click deployment options available below
|
||||
## Step 2: Setup Instance
|
||||
|
||||
| **Infra provider** | **One-click link** | **Additional information** |
|
||||
| :----------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------: |
|
||||
| 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) |
|
||||
| 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) |
|
||||
- Open authorizer instance endpoint in browser
|
||||
- Sign up as an admin with a secure password
|
||||
- Configure environment variables from authorizer dashboard. Check env [docs](/core/env) for more information
|
||||
|
||||
> Note: `DATABASE_URL`, `DATABASE_TYPE` and `DATABASE_NAME` are only configurable via platform envs
|
||||
|
||||
### Things to consider
|
||||
|
||||
- For social logins, you will need respective social platform key and secret
|
||||
- For having verified users, you will need an SMTP server with an email address and password using which system can send emails. The system will send a verification link to an email address. Once an email is verified then, only able to access it.
|
||||
> Note: One can always disable the email verification to allow open sign up, which is not recommended for production as anyone can use anyone's email address 😅
|
||||
- For persisting user sessions, you will need Redis URL (not in case of railway.app). If you do not configure a Redis server, sessions will be persisted until the instance is up or not restarted. For better response time on authorization requests/middleware, we recommend deploying Redis on the same infra/network as your authorizer server.
|
||||
- For persisting user sessions, you will need Redis URL (not in case of railway app). If you do not configure a Redis server, sessions will be persisted until the instance is up or not restarted. For better response time on authorization requests/middleware, we recommend deploying Redis on the same infra/network as your authorizer server.
|
||||
|
||||
## Testing
|
||||
|
||||
@@ -163,8 +164,9 @@ This example demonstrates how you can use [`@authorizerdev/authorizer-js`](/auth
|
||||
|
||||
<script type="text/javascript">
|
||||
const authorizerRef = new authorizerdev.Authorizer({
|
||||
authorizerURL: `AUTHORIZER_URL`,
|
||||
authorizerURL: `YOUR_AUTHORIZER_INSTANCE_URL`,
|
||||
redirectURL: window.location.origin,
|
||||
clientID: 'YOUR_CLIENT_ID', // obtain your client id from authorizer dashboard
|
||||
});
|
||||
|
||||
// use the button selector as per your application
|
||||
@@ -175,15 +177,19 @@ This example demonstrates how you can use [`@authorizerdev/authorizer-js`](/auth
|
||||
});
|
||||
|
||||
async function onLoad() {
|
||||
const res = await authorizerRef.browserLogin();
|
||||
if (res && res.user) {
|
||||
const res = await authorizerRef.authorize({
|
||||
response_type: 'code',
|
||||
use_refresh_token: false,
|
||||
});
|
||||
if (res && res.access_token) {
|
||||
// you can use user information here, eg:
|
||||
/**
|
||||
const userSection = document.getElementById('user');
|
||||
const logoutSection = document.getElementById('logout-section');
|
||||
logoutSection.classList.toggle('hide');
|
||||
userSection.innerHTML = `Welcome, ${res.user.email}`;
|
||||
*/
|
||||
const user = await authorizerRef.getProfile({
|
||||
Authorization: `Bearer ${res.access_token}`,
|
||||
});
|
||||
const userSection = document.getElementById('user');
|
||||
const logoutSection = document.getElementById('logout-section');
|
||||
logoutSection.classList.toggle('hide');
|
||||
userSection.innerHTML = `Welcome, ${user.email}`;
|
||||
}
|
||||
}
|
||||
onLoad();
|
||||
|
2976
app/package-lock.json
generated
2976
app/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,27 +1,29 @@
|
||||
{
|
||||
"name": "app",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "rm -rf build && NODE_ENV=production node ./esbuild.config.js",
|
||||
"start": "NODE_ENV=development node ./esbuild.config.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Lakhan Samani",
|
||||
"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"
|
||||
}
|
||||
"name": "app",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "rm -rf build && NODE_ENV=production node ./esbuild.config.js",
|
||||
"start": "NODE_ENV=development node ./esbuild.config.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Lakhan Samani",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@authorizerdev/authorizer-react": "^0.24.0-beta.2",
|
||||
"@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",
|
||||
"styled-components": "^5.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react-router-dom": "^5.1.8",
|
||||
"@types/styled-components": "^5.1.11"
|
||||
}
|
||||
}
|
||||
|
@@ -2,10 +2,33 @@ import React from 'react';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import { AuthorizerProvider } from '@authorizerdev/authorizer-react';
|
||||
import Root from './Root';
|
||||
import { createRandomString } from './utils/common';
|
||||
|
||||
export default function App() {
|
||||
// @ts-ignore
|
||||
const globalState: Record<string, string> = window['__authorizer__'];
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
const state = searchParams.get('state') || createRandomString();
|
||||
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 (
|
||||
<div
|
||||
style={{
|
||||
@@ -30,15 +53,7 @@ export default function App() {
|
||||
/>
|
||||
<h1>{globalState.organizationName}</h1>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
width: 400,
|
||||
margin: `10px auto`,
|
||||
border: `1px solid #D1D5DB`,
|
||||
padding: `25px 20px`,
|
||||
borderRadius: 5,
|
||||
}}
|
||||
>
|
||||
<div className="container">
|
||||
<BrowserRouter>
|
||||
<AuthorizerProvider
|
||||
config={{
|
||||
@@ -46,7 +61,7 @@ export default function App() {
|
||||
redirectURL: globalState.redirectURL,
|
||||
}}
|
||||
>
|
||||
<Root />
|
||||
<Root globalState={globalState} />
|
||||
</AuthorizerProvider>
|
||||
</BrowserRouter>
|
||||
</div>
|
||||
|
@@ -1,19 +1,76 @@
|
||||
import React, { useEffect, lazy, Suspense } from 'react';
|
||||
import { Switch, Route } from 'react-router-dom';
|
||||
import { useAuthorizer } from '@authorizerdev/authorizer-react';
|
||||
import styled, { ThemeProvider } from 'styled-components';
|
||||
import SetupPassword from './pages/setup-password';
|
||||
import { hasWindow, createRandomString } from './utils/common';
|
||||
import { theme } from './theme';
|
||||
|
||||
const ResetPassword = lazy(() => import('./pages/rest-password'));
|
||||
const Login = lazy(() => import('./pages/login'));
|
||||
const Dashboard = lazy(() => import('./pages/dashboard'));
|
||||
const SignUp = lazy(() => import('./pages/signup'));
|
||||
|
||||
export default function Root() {
|
||||
const Wrapper = styled.div`
|
||||
font-family: ${(props) => props.theme.fonts.fontStack};
|
||||
color: ${(props) => props.theme.colors.textColor};
|
||||
font-size: ${(props) => props.theme.fonts.mediumText};
|
||||
box-sizing: border-box;
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
`;
|
||||
|
||||
export default function Root({
|
||||
globalState,
|
||||
}: {
|
||||
globalState: Record<string, string>;
|
||||
}) {
|
||||
const { token, loading, config } = useAuthorizer();
|
||||
|
||||
const searchParams = new URLSearchParams(
|
||||
hasWindow() ? window.location.search : ``
|
||||
);
|
||||
const state = searchParams.get('state') || createRandomString();
|
||||
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 = hasWindow() ? window.location.origin : redirectURL;
|
||||
}
|
||||
|
||||
urlProps.redirect_uri = urlProps.redirectURL;
|
||||
|
||||
useEffect(() => {
|
||||
if (token) {
|
||||
const url = new URL(config.redirectURL || '/app');
|
||||
let redirectURL = 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) {
|
||||
window.location.href = config.redirectURL || '/app';
|
||||
sessionStorage.removeItem('authorizer_state');
|
||||
window.location.replace(redirectURL);
|
||||
}
|
||||
}
|
||||
return () => {};
|
||||
@@ -37,14 +94,24 @@ export default function Root() {
|
||||
|
||||
return (
|
||||
<Suspense fallback={<></>}>
|
||||
<Switch>
|
||||
<Route path="/app" exact>
|
||||
<Login />
|
||||
</Route>
|
||||
<Route path="/app/reset-password">
|
||||
<ResetPassword />
|
||||
</Route>
|
||||
</Switch>
|
||||
<ThemeProvider theme={theme}>
|
||||
<Wrapper>
|
||||
<Switch>
|
||||
<Route path="/app" exact>
|
||||
<Login urlProps={urlProps} />
|
||||
</Route>
|
||||
<Route path="/app/signup" exact>
|
||||
<SignUp urlProps={urlProps} />
|
||||
</Route>
|
||||
<Route path="/app/reset-password">
|
||||
<ResetPassword />
|
||||
</Route>
|
||||
<Route path="/app/setup-password">
|
||||
<SetupPassword />
|
||||
</Route>
|
||||
</Switch>
|
||||
</Wrapper>
|
||||
</ThemeProvider>
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
body {
|
||||
margin: 0;
|
||||
margin: 10;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
@@ -14,3 +14,17 @@ body {
|
||||
*:after {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@@ -1,10 +1,84 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { Authorizer } from '@authorizerdev/authorizer-react';
|
||||
import React, { Fragment, useState } from 'react';
|
||||
import {
|
||||
AuthorizerBasicAuthLogin,
|
||||
AuthorizerForgotPassword,
|
||||
AuthorizerMagicLinkLogin,
|
||||
AuthorizerSocialLogin,
|
||||
useAuthorizer,
|
||||
} from '@authorizerdev/authorizer-react';
|
||||
import styled from 'styled-components';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
export default function Login() {
|
||||
const enum VIEW_TYPES {
|
||||
LOGIN = 'login',
|
||||
FORGOT_PASSWORD = 'forgot-password',
|
||||
}
|
||||
|
||||
const Footer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-top: 15px;
|
||||
`;
|
||||
|
||||
const FooterContent = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-top: 10px;
|
||||
`;
|
||||
|
||||
export default function Login({ urlProps }: { urlProps: Record<string, any> }) {
|
||||
const { config } = useAuthorizer();
|
||||
const [view, setView] = useState<VIEW_TYPES>(VIEW_TYPES.LOGIN);
|
||||
return (
|
||||
<Fragment>
|
||||
<Authorizer />
|
||||
{view === VIEW_TYPES.LOGIN && (
|
||||
<Fragment>
|
||||
<h1 style={{ textAlign: 'center' }}>Login</h1>
|
||||
<br />
|
||||
<AuthorizerSocialLogin urlProps={urlProps} />
|
||||
{config.is_basic_authentication_enabled &&
|
||||
!config.is_magic_link_login_enabled && (
|
||||
<AuthorizerBasicAuthLogin urlProps={urlProps} />
|
||||
)}
|
||||
{config.is_magic_link_login_enabled && (
|
||||
<AuthorizerMagicLinkLogin urlProps={urlProps} />
|
||||
)}
|
||||
<Footer>
|
||||
<Link
|
||||
to="#"
|
||||
onClick={() => setView(VIEW_TYPES.FORGOT_PASSWORD)}
|
||||
style={{ marginBottom: 10 }}
|
||||
>
|
||||
Forgot Password?
|
||||
</Link>
|
||||
</Footer>
|
||||
</Fragment>
|
||||
)}
|
||||
{view === VIEW_TYPES.FORGOT_PASSWORD && (
|
||||
<Fragment>
|
||||
<h1 style={{ textAlign: 'center' }}>Forgot Password</h1>
|
||||
<AuthorizerForgotPassword urlProps={urlProps} />
|
||||
<Footer>
|
||||
<Link
|
||||
to="#"
|
||||
onClick={() => setView(VIEW_TYPES.LOGIN)}
|
||||
style={{ marginBottom: 10 }}
|
||||
>
|
||||
Back
|
||||
</Link>
|
||||
</Footer>
|
||||
</Fragment>
|
||||
)}
|
||||
{config.is_basic_authentication_enabled &&
|
||||
!config.is_magic_link_login_enabled &&
|
||||
config.is_sign_up_enabled && (
|
||||
<FooterContent>
|
||||
Don't have an account? <Link to="/app/signup"> Sign Up</Link>
|
||||
</FooterContent>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
12
app/src/pages/setup-password.tsx
Normal file
12
app/src/pages/setup-password.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
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>
|
||||
);
|
||||
}
|
28
app/src/pages/signup.tsx
Normal file
28
app/src/pages/signup.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { AuthorizerSignup } from '@authorizerdev/authorizer-react';
|
||||
import styled from 'styled-components';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const FooterContent = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-top: 20px;
|
||||
`;
|
||||
|
||||
export default function SignUp({
|
||||
urlProps,
|
||||
}: {
|
||||
urlProps: Record<string, any>;
|
||||
}) {
|
||||
return (
|
||||
<Fragment>
|
||||
<h1 style={{ textAlign: 'center' }}>Sign Up</h1>
|
||||
<br />
|
||||
<AuthorizerSignup urlProps={urlProps} />
|
||||
<FooterContent>
|
||||
Already have an account? <Link to="/app"> Login</Link>
|
||||
</FooterContent>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
28
app/src/theme.ts
Normal file
28
app/src/theme.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
// colors: https://tailwindcss.com/docs/customizing-colors
|
||||
|
||||
export const theme = {
|
||||
colors: {
|
||||
primary: '#3B82F6',
|
||||
primaryDisabled: '#60A5FA',
|
||||
gray: '#D1D5DB',
|
||||
danger: '#DC2626',
|
||||
success: '#10B981',
|
||||
textColor: '#374151',
|
||||
},
|
||||
fonts: {
|
||||
// typography
|
||||
fontStack: '-apple-system, system-ui, sans-serif',
|
||||
|
||||
// font sizes
|
||||
largeText: '18px',
|
||||
mediumText: '14px',
|
||||
smallText: '12px',
|
||||
tinyText: '10px',
|
||||
},
|
||||
|
||||
radius: {
|
||||
card: '5px',
|
||||
button: '5px',
|
||||
input: '5px',
|
||||
},
|
||||
};
|
24
app/src/utils/common.ts
Normal file
24
app/src/utils/common.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
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('&');
|
||||
};
|
||||
|
||||
export const hasWindow = (): boolean => typeof window !== 'undefined';
|
105
dashboard/package-lock.json
generated
105
dashboard/package-lock.json
generated
@@ -10,6 +10,7 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@chakra-ui/react": "^1.7.3",
|
||||
"@emotion/core": "^11.0.0",
|
||||
"@emotion/react": "^11.7.1",
|
||||
"@emotion/styled": "^11.6.0",
|
||||
"@types/react": "^17.0.38",
|
||||
@@ -17,11 +18,13 @@
|
||||
"@types/react-router-dom": "^5.3.2",
|
||||
"dayjs": "^1.10.7",
|
||||
"esbuild": "^0.14.9",
|
||||
"focus-visible": "^5.2.0",
|
||||
"framer-motion": "^5.5.5",
|
||||
"graphql": "^16.2.0",
|
||||
"lodash": "^4.17.21",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-dropzone": "^12.0.4",
|
||||
"react-icons": "^4.3.1",
|
||||
"react-router-dom": "^6.2.1",
|
||||
"typescript": "^4.5.4",
|
||||
@@ -977,6 +980,11 @@
|
||||
"stylis": "4.0.13"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/core": {
|
||||
"version": "11.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/core/-/core-11.0.0.tgz",
|
||||
"integrity": "sha512-w4sE3AmHmyG6RDKf6mIbtHpgJUSJ2uGvPQb8VXFL7hFjMPibE8IiehG8cMX3Ztm4svfCQV6KqusQbeIOkurBcA=="
|
||||
},
|
||||
"node_modules/@emotion/hash": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
|
||||
@@ -1251,6 +1259,14 @@
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/attr-accept": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz",
|
||||
"integrity": "sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/babel-plugin-macros": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz",
|
||||
@@ -1631,6 +1647,17 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/file-selector": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.4.0.tgz",
|
||||
"integrity": "sha512-iACCiXeMYOvZqlF1kTiYINzgepRBymz1wwjiuup9u9nayhb6g4fSwiyJ/6adli+EPwrWtpgQAh2PoS7HukEGEg==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/find-root": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
|
||||
@@ -1647,6 +1674,11 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/focus-visible": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/focus-visible/-/focus-visible-5.2.0.tgz",
|
||||
"integrity": "sha512-Rwix9pBtC1Nuy5wysTmKy+UjbDJpIfg8eHjw0rjZ1mX4GNLz1Bmd16uDpI3Gk1i70Fgcs8Csg2lPm8HULFg9DQ=="
|
||||
},
|
||||
"node_modules/framer-motion": {
|
||||
"version": "5.5.5",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-5.5.5.tgz",
|
||||
@@ -1914,9 +1946,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prop-types": {
|
||||
"version": "15.8.0",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.0.tgz",
|
||||
"integrity": "sha512-fDGekdaHh65eI3lMi5OnErU6a8Ighg2KjcjQxO7m8VHyWjcPyj5kiOgV1LQDOOOgVy3+5FgjXvdSSX7B8/5/4g==",
|
||||
"version": "15.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.4.0",
|
||||
"object-assign": "^4.1.1",
|
||||
@@ -1959,6 +1991,22 @@
|
||||
"react": "17.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dropzone": {
|
||||
"version": "12.0.4",
|
||||
"resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-12.0.4.tgz",
|
||||
"integrity": "sha512-fcqHEYe1MzAghU6/Hz86lHDlBNsA+lO48nAcm7/wA+kIzwS6uuJbUG33tBZjksj7GAZ1iUQ6NHwjUURPmSGang==",
|
||||
"dependencies": {
|
||||
"attr-accept": "^2.2.2",
|
||||
"file-selector": "^0.4.0",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.13"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">= 16.8"
|
||||
}
|
||||
},
|
||||
"node_modules/react-fast-compare": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz",
|
||||
@@ -2481,8 +2529,7 @@
|
||||
"@chakra-ui/css-reset": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@chakra-ui/css-reset/-/css-reset-1.1.1.tgz",
|
||||
"integrity": "sha512-+KNNHL4OWqeKia5SL858K3Qbd8WxMij9mWIilBzLD4j2KFrl/+aWFw8syMKth3NmgIibrjsljo+PU3fy2o50dg==",
|
||||
"requires": {}
|
||||
"integrity": "sha512-+KNNHL4OWqeKia5SL858K3Qbd8WxMij9mWIilBzLD4j2KFrl/+aWFw8syMKth3NmgIibrjsljo+PU3fy2o50dg=="
|
||||
},
|
||||
"@chakra-ui/descendant": {
|
||||
"version": "2.1.1",
|
||||
@@ -3002,6 +3049,11 @@
|
||||
"stylis": "4.0.13"
|
||||
}
|
||||
},
|
||||
"@emotion/core": {
|
||||
"version": "11.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/core/-/core-11.0.0.tgz",
|
||||
"integrity": "sha512-w4sE3AmHmyG6RDKf6mIbtHpgJUSJ2uGvPQb8VXFL7hFjMPibE8IiehG8cMX3Ztm4svfCQV6KqusQbeIOkurBcA=="
|
||||
},
|
||||
"@emotion/hash": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
|
||||
@@ -3081,8 +3133,7 @@
|
||||
"@graphql-typed-document-node/core": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.1.1.tgz",
|
||||
"integrity": "sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg==",
|
||||
"requires": {}
|
||||
"integrity": "sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg=="
|
||||
},
|
||||
"@popperjs/core": {
|
||||
"version": "2.11.0",
|
||||
@@ -3226,6 +3277,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"attr-accept": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz",
|
||||
"integrity": "sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg=="
|
||||
},
|
||||
"babel-plugin-macros": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz",
|
||||
@@ -3478,6 +3534,14 @@
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
|
||||
},
|
||||
"file-selector": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.4.0.tgz",
|
||||
"integrity": "sha512-iACCiXeMYOvZqlF1kTiYINzgepRBymz1wwjiuup9u9nayhb6g4fSwiyJ/6adli+EPwrWtpgQAh2PoS7HukEGEg==",
|
||||
"requires": {
|
||||
"tslib": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"find-root": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
|
||||
@@ -3491,6 +3555,11 @@
|
||||
"tslib": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"focus-visible": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/focus-visible/-/focus-visible-5.2.0.tgz",
|
||||
"integrity": "sha512-Rwix9pBtC1Nuy5wysTmKy+UjbDJpIfg8eHjw0rjZ1mX4GNLz1Bmd16uDpI3Gk1i70Fgcs8Csg2lPm8HULFg9DQ=="
|
||||
},
|
||||
"framer-motion": {
|
||||
"version": "5.5.5",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-5.5.5.tgz",
|
||||
@@ -3707,9 +3776,9 @@
|
||||
}
|
||||
},
|
||||
"prop-types": {
|
||||
"version": "15.8.0",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.0.tgz",
|
||||
"integrity": "sha512-fDGekdaHh65eI3lMi5OnErU6a8Ighg2KjcjQxO7m8VHyWjcPyj5kiOgV1LQDOOOgVy3+5FgjXvdSSX7B8/5/4g==",
|
||||
"version": "15.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.4.0",
|
||||
"object-assign": "^4.1.1",
|
||||
@@ -3743,6 +3812,16 @@
|
||||
"scheduler": "^0.20.2"
|
||||
}
|
||||
},
|
||||
"react-dropzone": {
|
||||
"version": "12.0.4",
|
||||
"resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-12.0.4.tgz",
|
||||
"integrity": "sha512-fcqHEYe1MzAghU6/Hz86lHDlBNsA+lO48nAcm7/wA+kIzwS6uuJbUG33tBZjksj7GAZ1iUQ6NHwjUURPmSGang==",
|
||||
"requires": {
|
||||
"attr-accept": "^2.2.2",
|
||||
"file-selector": "^0.4.0",
|
||||
"prop-types": "^15.8.1"
|
||||
}
|
||||
},
|
||||
"react-fast-compare": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz",
|
||||
@@ -3764,8 +3843,7 @@
|
||||
"react-icons": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.3.1.tgz",
|
||||
"integrity": "sha512-cB10MXLTs3gVuXimblAdI71jrJx8njrJZmNMEMC+sQu5B/BIOmlsAjskdqpn81y8UBVEGuHODd7/ci5DvoSzTQ==",
|
||||
"requires": {}
|
||||
"integrity": "sha512-cB10MXLTs3gVuXimblAdI71jrJx8njrJZmNMEMC+sQu5B/BIOmlsAjskdqpn81y8UBVEGuHODd7/ci5DvoSzTQ=="
|
||||
},
|
||||
"react-is": {
|
||||
"version": "16.13.1",
|
||||
@@ -3951,8 +4029,7 @@
|
||||
"use-callback-ref": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.2.5.tgz",
|
||||
"integrity": "sha512-gN3vgMISAgacF7sqsLPByqoePooY3n2emTH59Ur5d/M8eg4WTWu1xp8i8DHjohftIyEx0S08RiYxbffr4j8Peg==",
|
||||
"requires": {}
|
||||
"integrity": "sha512-gN3vgMISAgacF7sqsLPByqoePooY3n2emTH59Ur5d/M8eg4WTWu1xp8i8DHjohftIyEx0S08RiYxbffr4j8Peg=="
|
||||
},
|
||||
"use-sidecar": {
|
||||
"version": "1.0.5",
|
||||
|
@@ -12,6 +12,7 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@chakra-ui/react": "^1.7.3",
|
||||
"@emotion/core": "^11.0.0",
|
||||
"@emotion/react": "^11.7.1",
|
||||
"@emotion/styled": "^11.6.0",
|
||||
"@types/react": "^17.0.38",
|
||||
@@ -19,11 +20,13 @@
|
||||
"@types/react-router-dom": "^5.3.2",
|
||||
"dayjs": "^1.10.7",
|
||||
"esbuild": "^0.14.9",
|
||||
"focus-visible": "^5.2.0",
|
||||
"framer-motion": "^5.5.5",
|
||||
"graphql": "^16.2.0",
|
||||
"lodash": "^4.17.21",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-dropzone": "^12.0.4",
|
||||
"react-icons": "^4.3.1",
|
||||
"react-router-dom": "^6.2.1",
|
||||
"typescript": "^4.5.4",
|
||||
|
1
dashboard/public/sample.csv
Normal file
1
dashboard/public/sample.csv
Normal file
@@ -0,0 +1 @@
|
||||
foo@bar.com,test@authorizer.dev
|
|
@@ -1,4 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { Fragment } from 'react';
|
||||
import { ChakraProvider, extendTheme } from '@chakra-ui/react';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import { createClient, Provider } from 'urql';
|
||||
@@ -10,6 +11,9 @@ const queryClient = createClient({
|
||||
fetchOptions: () => {
|
||||
return {
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'x-authorizer-url': window.location.origin,
|
||||
},
|
||||
};
|
||||
},
|
||||
requestPolicy: 'network-only',
|
||||
@@ -19,8 +23,8 @@ const theme = extendTheme({
|
||||
styles: {
|
||||
global: {
|
||||
'html, body, #root': {
|
||||
fontFamily: 'Avenir, Helvetica, Arial, sans-serif',
|
||||
height: '100%',
|
||||
outline: 'none',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -33,14 +37,16 @@ const theme = extendTheme({
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<ChakraProvider theme={theme}>
|
||||
<Provider value={queryClient}>
|
||||
<BrowserRouter basename="/dashboard">
|
||||
<AuthContextProvider>
|
||||
<AppRoutes />
|
||||
</AuthContextProvider>
|
||||
</BrowserRouter>
|
||||
</Provider>
|
||||
</ChakraProvider>
|
||||
<Fragment>
|
||||
<ChakraProvider theme={theme}>
|
||||
<Provider value={queryClient}>
|
||||
<BrowserRouter basename="/dashboard">
|
||||
<AuthContextProvider>
|
||||
<AppRoutes />
|
||||
</AuthContextProvider>
|
||||
</BrowserRouter>
|
||||
</Provider>
|
||||
</ChakraProvider>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
65
dashboard/src/components/EnvComponents/AccessToken.tsx
Normal file
65
dashboard/src/components/EnvComponents/AccessToken.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
import React from "react";
|
||||
import { Flex, Stack, Text, useMediaQuery } from "@chakra-ui/react";
|
||||
import InputField from "../../components/InputField";
|
||||
import { TextInputType, TextAreaInputType } from "../../constants";
|
||||
|
||||
const AccessToken = ({ variables, setVariables }: any) => {
|
||||
const [isNotSmallerScreen] = useMediaQuery("(min-width:600px)");
|
||||
return (
|
||||
<div>
|
||||
{" "}
|
||||
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
|
||||
Access Token
|
||||
</Text>
|
||||
<Stack spacing={6} padding="2% 0%">
|
||||
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
||||
<Flex
|
||||
w={isNotSmallerScreen ? "30%" : "50%"}
|
||||
justifyContent="start"
|
||||
alignItems="center"
|
||||
>
|
||||
<Text fontSize="sm">Access Token Expiry Time:</Text>
|
||||
</Flex>
|
||||
<Flex
|
||||
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||
mt={isNotSmallerScreen ? "0" : "3"}
|
||||
>
|
||||
<InputField
|
||||
borderRadius={5}
|
||||
variables={variables}
|
||||
setVariables={setVariables}
|
||||
inputType={TextInputType.ACCESS_TOKEN_EXPIRY_TIME}
|
||||
placeholder="0h15m0s"
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
||||
<Flex
|
||||
w={isNotSmallerScreen ? "30%" : "60%"}
|
||||
justifyContent="start"
|
||||
direction="column"
|
||||
>
|
||||
<Text fontSize="sm">Custom Scripts:</Text>
|
||||
<Text fontSize="xs" color="blackAlpha.500">
|
||||
(Used to add custom fields in ID token)
|
||||
</Text>
|
||||
</Flex>
|
||||
<Flex
|
||||
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||
mt={isNotSmallerScreen ? "0" : "3"}
|
||||
>
|
||||
<InputField
|
||||
variables={variables}
|
||||
setVariables={setVariables}
|
||||
inputType={TextAreaInputType.CUSTOM_ACCESS_TOKEN_SCRIPT}
|
||||
placeholder="Add script here"
|
||||
minH="25vh"
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AccessToken;
|
@@ -0,0 +1,89 @@
|
||||
import React from 'react';
|
||||
import { Flex, Stack, Center, Text, useMediaQuery } from '@chakra-ui/react';
|
||||
|
||||
import InputField from '../../components/InputField';
|
||||
import { TextInputType } from '../../constants';
|
||||
|
||||
const DatabaseCredentials = ({ variables, setVariables }: any) => {
|
||||
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
|
||||
return (
|
||||
<div>
|
||||
{' '}
|
||||
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
|
||||
Database Credentials
|
||||
</Text>
|
||||
<Stack spacing={6} padding="3% 0">
|
||||
<Text fontStyle="italic" fontSize="sm" color="blackAlpha.500" mt={3}>
|
||||
Note: Database related environment variables cannot be updated from
|
||||
dashboard. Please use .env file or OS environment variables to update
|
||||
it.
|
||||
</Text>
|
||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
||||
<Flex
|
||||
w={isNotSmallerScreen ? '30%' : '40%'}
|
||||
justifyContent="start"
|
||||
alignItems="center"
|
||||
>
|
||||
<Text fontSize="sm">DataBase Name:</Text>
|
||||
</Flex>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
||||
mt={isNotSmallerScreen ? '0' : '3'}
|
||||
>
|
||||
<InputField
|
||||
borderRadius={5}
|
||||
variables={variables}
|
||||
setVariables={setVariables}
|
||||
inputType={TextInputType.DATABASE_NAME}
|
||||
isDisabled={true}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
||||
<Flex
|
||||
w={isNotSmallerScreen ? '30%' : '40%'}
|
||||
justifyContent="start"
|
||||
alignItems="center"
|
||||
>
|
||||
<Text fontSize="sm">DataBase Type:</Text>
|
||||
</Flex>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
||||
mt={isNotSmallerScreen ? '0' : '3'}
|
||||
>
|
||||
<InputField
|
||||
borderRadius={5}
|
||||
variables={variables}
|
||||
setVariables={setVariables}
|
||||
inputType={TextInputType.DATABASE_TYPE}
|
||||
isDisabled={true}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
||||
<Flex
|
||||
w={isNotSmallerScreen ? '30%' : '40%'}
|
||||
justifyContent="start"
|
||||
alignItems="center"
|
||||
>
|
||||
<Text fontSize="sm">DataBase URL:</Text>
|
||||
</Flex>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
||||
mt={isNotSmallerScreen ? '0' : '3'}
|
||||
>
|
||||
<InputField
|
||||
borderRadius={5}
|
||||
variables={variables}
|
||||
setVariables={setVariables}
|
||||
inputType={TextInputType.DATABASE_URL}
|
||||
isDisabled={true}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DatabaseCredentials;
|
@@ -0,0 +1,35 @@
|
||||
import React from "react";
|
||||
import { Flex, Stack, Center, Text, useMediaQuery } from "@chakra-ui/react";
|
||||
import InputField from "../../components/InputField";
|
||||
import { ArrayInputType} from "../../constants";
|
||||
|
||||
const DomainWhiteListing = ({ variables, setVariables }: any) => {
|
||||
const [isNotSmallerScreen] = useMediaQuery("(min-width:600px)");
|
||||
return (
|
||||
<div>
|
||||
{" "}
|
||||
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
|
||||
Domain White Listing
|
||||
</Text>
|
||||
<Stack spacing={6} padding="2% 0%">
|
||||
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Allowed Origins:</Text>
|
||||
</Flex>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||
mt={isNotSmallerScreen ? "0" : "3"}
|
||||
>
|
||||
<InputField
|
||||
variables={variables}
|
||||
setVariables={setVariables}
|
||||
inputType={ArrayInputType.ALLOWED_ORIGINS}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DomainWhiteListing;
|
114
dashboard/src/components/EnvComponents/EmailConfiguration.tsx
Normal file
114
dashboard/src/components/EnvComponents/EmailConfiguration.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
import React from "react";
|
||||
import { Flex, Stack, Center, Text, useMediaQuery } from "@chakra-ui/react";
|
||||
import InputField from "../../components/InputField";
|
||||
import { TextInputType, HiddenInputType} from "../../constants";
|
||||
const EmailConfigurations = ({
|
||||
variables,
|
||||
setVariables,
|
||||
fieldVisibility,
|
||||
setFieldVisibility,
|
||||
}: any) => {
|
||||
const [isNotSmallerScreen] = useMediaQuery("(min-width:600px)");
|
||||
return (
|
||||
<div>
|
||||
{" "}
|
||||
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
|
||||
Email Configurations
|
||||
</Text>
|
||||
<Stack spacing={6} padding="2% 0%">
|
||||
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">SMTP Host:</Text>
|
||||
</Flex>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||
mt={isNotSmallerScreen ? "0" : "3"}
|
||||
>
|
||||
<InputField
|
||||
borderRadius={5}
|
||||
variables={variables}
|
||||
setVariables={setVariables}
|
||||
inputType={TextInputType.SMTP_HOST}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">SMTP Port:</Text>
|
||||
</Flex>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||
mt={isNotSmallerScreen ? "0" : "3"}
|
||||
>
|
||||
<InputField
|
||||
borderRadius={5}
|
||||
variables={variables}
|
||||
setVariables={setVariables}
|
||||
inputType={TextInputType.SMTP_PORT}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
||||
<Flex
|
||||
w={isNotSmallerScreen ? "30%" : "40%"}
|
||||
justifyContent="start"
|
||||
alignItems="center"
|
||||
>
|
||||
<Text fontSize="sm">SMTP Username:</Text>
|
||||
</Flex>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||
mt={isNotSmallerScreen ? "0" : "3"}
|
||||
>
|
||||
<InputField
|
||||
borderRadius={5}
|
||||
variables={variables}
|
||||
setVariables={setVariables}
|
||||
inputType={TextInputType.SMTP_USERNAME}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
||||
<Flex
|
||||
w={isNotSmallerScreen ? "30%" : "40%"}
|
||||
justifyContent="start"
|
||||
alignItems="center"
|
||||
>
|
||||
<Text fontSize="sm">SMTP Password:</Text>
|
||||
</Flex>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||
mt={isNotSmallerScreen ? "0" : "3"}
|
||||
>
|
||||
<InputField
|
||||
borderRadius={5}
|
||||
variables={variables}
|
||||
setVariables={setVariables}
|
||||
fieldVisibility={fieldVisibility}
|
||||
setFieldVisibility={setFieldVisibility}
|
||||
inputType={HiddenInputType.SMTP_PASSWORD}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">From Email:</Text>
|
||||
</Flex>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||
mt={isNotSmallerScreen ? "0" : "3"}
|
||||
>
|
||||
<InputField
|
||||
borderRadius={5}
|
||||
variables={variables}
|
||||
setVariables={setVariables}
|
||||
inputType={TextInputType.SENDER_EMAIL}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmailConfigurations;
|
79
dashboard/src/components/EnvComponents/Features.tsx
Normal file
79
dashboard/src/components/EnvComponents/Features.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
import React from 'react';
|
||||
import { Flex, Stack, Text } from '@chakra-ui/react';
|
||||
import InputField from '../InputField';
|
||||
import { SwitchInputType } from '../../constants';
|
||||
|
||||
const Features = ({ variables, setVariables }: any) => {
|
||||
return (
|
||||
<div>
|
||||
{' '}
|
||||
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
|
||||
Disable Features
|
||||
</Text>
|
||||
<Stack spacing={6} padding="2% 0%">
|
||||
<Flex>
|
||||
<Flex w="100%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Disable Login Page:</Text>
|
||||
</Flex>
|
||||
<Flex justifyContent="start">
|
||||
<InputField
|
||||
variables={variables}
|
||||
setVariables={setVariables}
|
||||
inputType={SwitchInputType.DISABLE_LOGIN_PAGE}
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex>
|
||||
<Flex w="100%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Disable Email Verification:</Text>
|
||||
</Flex>
|
||||
<Flex justifyContent="start">
|
||||
<InputField
|
||||
variables={variables}
|
||||
setVariables={setVariables}
|
||||
inputType={SwitchInputType.DISABLE_EMAIL_VERIFICATION}
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex>
|
||||
<Flex w="100%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Disable Magic Login Link:</Text>
|
||||
</Flex>
|
||||
<Flex justifyContent="start">
|
||||
<InputField
|
||||
variables={variables}
|
||||
setVariables={setVariables}
|
||||
inputType={SwitchInputType.DISABLE_MAGIC_LINK_LOGIN}
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex>
|
||||
<Flex w="100%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Disable Basic Authentication:</Text>
|
||||
</Flex>
|
||||
<Flex justifyContent="start">
|
||||
<InputField
|
||||
variables={variables}
|
||||
setVariables={setVariables}
|
||||
inputType={SwitchInputType.DISABLE_BASIC_AUTHENTICATION}
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex>
|
||||
<Flex w="100%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Disable Sign Up:</Text>
|
||||
</Flex>
|
||||
<Flex justifyContent="start" mb={3}>
|
||||
<InputField
|
||||
variables={variables}
|
||||
setVariables={setVariables}
|
||||
inputType={SwitchInputType.DISABLE_SIGN_UP}
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Features;
|
154
dashboard/src/components/EnvComponents/JWTConfiguration.tsx
Normal file
154
dashboard/src/components/EnvComponents/JWTConfiguration.tsx
Normal file
@@ -0,0 +1,154 @@
|
||||
import React from "react";
|
||||
import { Flex, Stack, Center, Text, useMediaQuery } from "@chakra-ui/react";
|
||||
import {
|
||||
HiddenInputType,
|
||||
TextInputType,
|
||||
TextAreaInputType,
|
||||
} from "../../constants";
|
||||
import GenerateKeysModal from "../GenerateKeysModal";
|
||||
import InputField from "../InputField";
|
||||
|
||||
const JSTConfigurations = ({
|
||||
variables,
|
||||
setVariables,
|
||||
fieldVisibility,
|
||||
setFieldVisibility,
|
||||
SelectInputType,
|
||||
getData,
|
||||
HMACEncryptionType,
|
||||
RSAEncryptionType,
|
||||
ECDSAEncryptionType,
|
||||
}: any) => {
|
||||
const [isNotSmallerScreen] = useMediaQuery("(min-width:600px)");
|
||||
|
||||
return (
|
||||
<div>
|
||||
{" "}
|
||||
<Flex
|
||||
borderRadius={5}
|
||||
width="100%"
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
paddingTop="2%"
|
||||
>
|
||||
<Text
|
||||
fontSize={isNotSmallerScreen ? "md" : "sm"}
|
||||
fontWeight="bold"
|
||||
mb={5}
|
||||
>
|
||||
JWT (JSON Web Tokens) Configurations
|
||||
</Text>
|
||||
<Flex mb={7}>
|
||||
<GenerateKeysModal jwtType={variables.JWT_TYPE} getData={getData} />
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Stack spacing={6} padding="2% 0%">
|
||||
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">JWT Type:</Text>
|
||||
</Flex>
|
||||
<Flex
|
||||
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||
mt={isNotSmallerScreen ? "0" : "2"}
|
||||
>
|
||||
<InputField
|
||||
borderRadius={5}
|
||||
variables={variables}
|
||||
setVariables={setVariables}
|
||||
inputType={SelectInputType}
|
||||
value={SelectInputType}
|
||||
options={{
|
||||
...HMACEncryptionType,
|
||||
...RSAEncryptionType,
|
||||
...ECDSAEncryptionType,
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
{Object.values(HMACEncryptionType).includes(variables.JWT_TYPE) ? (
|
||||
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">JWT Secret</Text>
|
||||
</Flex>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||
mt={isNotSmallerScreen ? "0" : "2"}
|
||||
>
|
||||
<InputField
|
||||
borderRadius={5}
|
||||
variables={variables}
|
||||
setVariables={setVariables}
|
||||
fieldVisibility={fieldVisibility}
|
||||
setFieldVisibility={setFieldVisibility}
|
||||
inputType={HiddenInputType.JWT_SECRET}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
) : (
|
||||
<>
|
||||
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Public Key</Text>
|
||||
</Flex>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||
mt={isNotSmallerScreen ? "0" : "2"}
|
||||
>
|
||||
<InputField
|
||||
borderRadius={5}
|
||||
variables={variables}
|
||||
setVariables={setVariables}
|
||||
inputType={TextAreaInputType.JWT_PUBLIC_KEY}
|
||||
placeholder="Add public key here"
|
||||
minH="25vh"
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Private Key</Text>
|
||||
</Flex>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||
mt={isNotSmallerScreen ? "0" : "2"}
|
||||
>
|
||||
<InputField
|
||||
borderRadius={5}
|
||||
variables={variables}
|
||||
setVariables={setVariables}
|
||||
inputType={TextAreaInputType.JWT_PRIVATE_KEY}
|
||||
placeholder="Add private key here"
|
||||
minH="25vh"
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
||||
<Flex
|
||||
w={isNotSmallerScreen ? "30%" : "40%"}
|
||||
justifyContent="start"
|
||||
alignItems="center"
|
||||
>
|
||||
<Text fontSize="sm" orientation="vertical">
|
||||
JWT Role Claim:
|
||||
</Text>
|
||||
</Flex>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||
mt={isNotSmallerScreen ? "0" : "2"}
|
||||
>
|
||||
<InputField
|
||||
borderRadius={5}
|
||||
variables={variables}
|
||||
setVariables={setVariables}
|
||||
inputType={TextInputType.JWT_ROLE_CLAIM}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default JSTConfigurations;
|
273
dashboard/src/components/EnvComponents/OAuthConfig.tsx
Normal file
273
dashboard/src/components/EnvComponents/OAuthConfig.tsx
Normal file
@@ -0,0 +1,273 @@
|
||||
import React from 'react';
|
||||
import InputField from '../InputField';
|
||||
import {
|
||||
Flex,
|
||||
Stack,
|
||||
Center,
|
||||
Text,
|
||||
Box,
|
||||
Divider,
|
||||
useMediaQuery,
|
||||
} from '@chakra-ui/react';
|
||||
import {
|
||||
FaGoogle,
|
||||
FaGithub,
|
||||
FaFacebookF,
|
||||
FaLinkedin,
|
||||
FaApple,
|
||||
} from 'react-icons/fa';
|
||||
import { TextInputType, HiddenInputType } from '../../constants';
|
||||
|
||||
const OAuthConfig = ({
|
||||
envVariables,
|
||||
setVariables,
|
||||
fieldVisibility,
|
||||
setFieldVisibility,
|
||||
}: any) => {
|
||||
const [isNotSmallerScreen] = useMediaQuery('(min-width:667px)');
|
||||
return (
|
||||
<div>
|
||||
<Box>
|
||||
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={6}>
|
||||
Authorizer Config
|
||||
</Text>
|
||||
<Stack spacing={6} padding="2% 0%">
|
||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Client ID</Text>
|
||||
</Flex>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
||||
mt={isNotSmallerScreen ? '0' : '3'}
|
||||
>
|
||||
<InputField
|
||||
variables={envVariables}
|
||||
setVariables={() => {}}
|
||||
inputType={TextInputType.CLIENT_ID}
|
||||
placeholder="Client ID"
|
||||
readOnly={true}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Client Secret</Text>
|
||||
</Flex>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
||||
mt={isNotSmallerScreen ? '0' : '3'}
|
||||
>
|
||||
<InputField
|
||||
variables={envVariables}
|
||||
setVariables={setVariables}
|
||||
fieldVisibility={fieldVisibility}
|
||||
setFieldVisibility={setFieldVisibility}
|
||||
inputType={HiddenInputType.CLIENT_SECRET}
|
||||
placeholder="Client Secret"
|
||||
readOnly={true}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
</Stack>
|
||||
<Divider mt={5} mb={2} color="blackAlpha.700" />
|
||||
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={4}>
|
||||
Social Media Logins
|
||||
</Text>
|
||||
<Stack spacing={6} padding="2% 0%">
|
||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? '55px' : '35px'}
|
||||
h="35px"
|
||||
marginRight="1.5%"
|
||||
border="1px solid #ff3e30"
|
||||
borderRadius="5px"
|
||||
>
|
||||
<FaGoogle style={{ color: '#ff3e30' }} />
|
||||
</Center>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
||||
mt={isNotSmallerScreen ? '0' : '3'}
|
||||
marginRight="1.5%"
|
||||
>
|
||||
<InputField
|
||||
borderRadius={5}
|
||||
variables={envVariables}
|
||||
setVariables={setVariables}
|
||||
inputType={TextInputType.GOOGLE_CLIENT_ID}
|
||||
placeholder="Google Client ID"
|
||||
/>
|
||||
</Center>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
||||
mt={isNotSmallerScreen ? '0' : '3'}
|
||||
>
|
||||
<InputField
|
||||
borderRadius={5}
|
||||
variables={envVariables}
|
||||
setVariables={setVariables}
|
||||
fieldVisibility={fieldVisibility}
|
||||
setFieldVisibility={setFieldVisibility}
|
||||
inputType={HiddenInputType.GOOGLE_CLIENT_SECRET}
|
||||
placeholder="Google Secret"
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? '55px' : '35px'}
|
||||
h="35px"
|
||||
marginRight="1.5%"
|
||||
border="1px solid #171515"
|
||||
borderRadius="5px"
|
||||
>
|
||||
<FaGithub style={{ color: '#171515' }} />
|
||||
</Center>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
||||
mt={isNotSmallerScreen ? '0' : '3'}
|
||||
marginRight="1.5%"
|
||||
>
|
||||
<InputField
|
||||
borderRadius={5}
|
||||
variables={envVariables}
|
||||
setVariables={setVariables}
|
||||
inputType={TextInputType.GITHUB_CLIENT_ID}
|
||||
placeholder="Github Client ID"
|
||||
/>
|
||||
</Center>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
||||
mt={isNotSmallerScreen ? '0' : '3'}
|
||||
>
|
||||
<InputField
|
||||
borderRadius={5}
|
||||
variables={envVariables}
|
||||
setVariables={setVariables}
|
||||
fieldVisibility={fieldVisibility}
|
||||
setFieldVisibility={setFieldVisibility}
|
||||
inputType={HiddenInputType.GITHUB_CLIENT_SECRET}
|
||||
placeholder="Github Secret"
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? '55px' : '35px'}
|
||||
h="35px"
|
||||
marginRight="1.5%"
|
||||
border="1px solid #3b5998"
|
||||
borderRadius="5px"
|
||||
>
|
||||
<FaFacebookF style={{ color: '#3b5998' }} />
|
||||
</Center>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
||||
mt={isNotSmallerScreen ? '0' : '3'}
|
||||
marginRight="1.5%"
|
||||
>
|
||||
<InputField
|
||||
borderRadius={5}
|
||||
variables={envVariables}
|
||||
setVariables={setVariables}
|
||||
inputType={TextInputType.FACEBOOK_CLIENT_ID}
|
||||
placeholder="Facebook Client ID"
|
||||
/>
|
||||
</Center>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
||||
mt={isNotSmallerScreen ? '0' : '3'}
|
||||
>
|
||||
<InputField
|
||||
borderRadius={5}
|
||||
variables={envVariables}
|
||||
setVariables={setVariables}
|
||||
fieldVisibility={fieldVisibility}
|
||||
setFieldVisibility={setFieldVisibility}
|
||||
inputType={HiddenInputType.FACEBOOK_CLIENT_SECRET}
|
||||
placeholder="Facebook Secret"
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? '55px' : '35px'}
|
||||
h="35px"
|
||||
marginRight="1.5%"
|
||||
border="1px solid #3b5998"
|
||||
borderRadius="5px"
|
||||
>
|
||||
<FaLinkedin style={{ color: '#3b5998' }} />
|
||||
</Center>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
||||
mt={isNotSmallerScreen ? '0' : '3'}
|
||||
marginRight="1.5%"
|
||||
>
|
||||
<InputField
|
||||
borderRadius={5}
|
||||
variables={envVariables}
|
||||
setVariables={setVariables}
|
||||
inputType={TextInputType.LINKEDIN_CLIENT_ID}
|
||||
placeholder="LinkedIn Client ID"
|
||||
/>
|
||||
</Center>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
||||
mt={isNotSmallerScreen ? '0' : '3'}
|
||||
>
|
||||
<InputField
|
||||
borderRadius={5}
|
||||
variables={envVariables}
|
||||
setVariables={setVariables}
|
||||
fieldVisibility={fieldVisibility}
|
||||
setFieldVisibility={setFieldVisibility}
|
||||
inputType={HiddenInputType.LINKEDIN_CLIENT_SECRET}
|
||||
placeholder="LinkedIn Client Secret"
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? '55px' : '35px'}
|
||||
h="35px"
|
||||
marginRight="1.5%"
|
||||
border="1px solid #3b5998"
|
||||
borderRadius="5px"
|
||||
>
|
||||
<FaApple style={{ color: '#3b5998' }} />
|
||||
</Center>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
||||
mt={isNotSmallerScreen ? '0' : '3'}
|
||||
marginRight="1.5%"
|
||||
>
|
||||
<InputField
|
||||
borderRadius={5}
|
||||
variables={envVariables}
|
||||
setVariables={setVariables}
|
||||
inputType={TextInputType.APPLE_CLIENT_ID}
|
||||
placeholder="Apple Client ID"
|
||||
/>
|
||||
</Center>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
||||
mt={isNotSmallerScreen ? '0' : '3'}
|
||||
>
|
||||
<InputField
|
||||
borderRadius={5}
|
||||
variables={envVariables}
|
||||
setVariables={setVariables}
|
||||
fieldVisibility={fieldVisibility}
|
||||
setFieldVisibility={setFieldVisibility}
|
||||
inputType={HiddenInputType.APPLE_CLIENT_SECRET}
|
||||
placeholder="Apple CLient Secret"
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
</Stack>
|
||||
</Box>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default OAuthConfig;
|
60
dashboard/src/components/EnvComponents/OrganizationInfo.tsx
Normal file
60
dashboard/src/components/EnvComponents/OrganizationInfo.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import React from "react";
|
||||
import { Flex, Stack, Center, Text, useMediaQuery } from "@chakra-ui/react";
|
||||
import InputField from "../InputField";
|
||||
import { TextInputType } from "../../constants";
|
||||
|
||||
const OrganizationInfo = ({ variables, setVariables }: any) => {
|
||||
const [isNotSmallerScreen] = useMediaQuery("(min-width:600px)");
|
||||
return (
|
||||
<div>
|
||||
{" "}
|
||||
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
|
||||
Organization Information
|
||||
</Text>
|
||||
<Stack spacing={6} padding="2% 0%">
|
||||
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
||||
<Flex
|
||||
w={isNotSmallerScreen ? "30%" : "40%"}
|
||||
justifyContent="start"
|
||||
alignItems="center"
|
||||
>
|
||||
<Text fontSize="sm">Organization Name:</Text>
|
||||
</Flex>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||
mt={isNotSmallerScreen ? "0" : "3"}
|
||||
>
|
||||
<InputField
|
||||
borderRadius={5}
|
||||
variables={variables}
|
||||
setVariables={setVariables}
|
||||
inputType={TextInputType.ORGANIZATION_NAME}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
||||
<Flex
|
||||
w={isNotSmallerScreen ? "30%" : "40%"}
|
||||
justifyContent="start"
|
||||
alignItems="center"
|
||||
>
|
||||
<Text fontSize="sm">Organization Logo:</Text>
|
||||
</Flex>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||
mt={isNotSmallerScreen ? "0" : "3"}
|
||||
>
|
||||
<InputField
|
||||
borderRadius={5}
|
||||
variables={variables}
|
||||
setVariables={setVariables}
|
||||
inputType={TextInputType.ORGANIZATION_LOGO}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default OrganizationInfo;
|
68
dashboard/src/components/EnvComponents/Roles.tsx
Normal file
68
dashboard/src/components/EnvComponents/Roles.tsx
Normal file
@@ -0,0 +1,68 @@
|
||||
import React from 'react';
|
||||
import { Flex, Stack, Center, Text, useMediaQuery } from '@chakra-ui/react';
|
||||
import { ArrayInputType } from '../../constants';
|
||||
import InputField from '../InputField';
|
||||
|
||||
const Roles = ({ variables, setVariables }: any) => {
|
||||
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
|
||||
|
||||
return (
|
||||
<div>
|
||||
{' '}
|
||||
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
|
||||
Roles
|
||||
</Text>
|
||||
<Stack spacing={6} padding="2% 0%">
|
||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Roles:</Text>
|
||||
</Flex>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
||||
mt={isNotSmallerScreen ? '0' : '2'}
|
||||
overflow="hidden"
|
||||
>
|
||||
<InputField
|
||||
borderRadius={7}
|
||||
variables={variables}
|
||||
setVariables={setVariables}
|
||||
inputType={ArrayInputType.ROLES}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Default Roles:</Text>
|
||||
</Flex>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
||||
mt={isNotSmallerScreen ? '0' : '2'}
|
||||
>
|
||||
<InputField
|
||||
variables={variables}
|
||||
setVariables={setVariables}
|
||||
inputType={ArrayInputType.DEFAULT_ROLES}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Protected Roles:</Text>
|
||||
</Flex>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
||||
mt={isNotSmallerScreen ? '0' : '2'}
|
||||
>
|
||||
<InputField
|
||||
variables={variables}
|
||||
setVariables={setVariables}
|
||||
inputType={ArrayInputType.PROTECTED_ROLES}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Roles;
|
138
dashboard/src/components/EnvComponents/SecurityAdminSecret.tsx
Normal file
138
dashboard/src/components/EnvComponents/SecurityAdminSecret.tsx
Normal file
@@ -0,0 +1,138 @@
|
||||
import React from "react";
|
||||
import {
|
||||
Flex,
|
||||
Stack,
|
||||
Center,
|
||||
Text,
|
||||
Input,
|
||||
InputGroup,
|
||||
InputRightElement,
|
||||
useMediaQuery,
|
||||
} from "@chakra-ui/react";
|
||||
import { FaRegEyeSlash, FaRegEye } from "react-icons/fa";
|
||||
import InputField from "../InputField";
|
||||
import { HiddenInputType } from "../../constants";
|
||||
const SecurityAdminSecret = ({
|
||||
variables,
|
||||
setVariables,
|
||||
fieldVisibility,
|
||||
setFieldVisibility,
|
||||
validateAdminSecretHandler,
|
||||
adminSecret,
|
||||
}: any) => {
|
||||
const [isNotSmallerScreen] = useMediaQuery("(min-width:600px)");
|
||||
return (
|
||||
<div>
|
||||
{" "}
|
||||
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
|
||||
Security (Admin Secret)
|
||||
</Text>
|
||||
<Stack
|
||||
spacing={6}
|
||||
padding="0 5%"
|
||||
marginTop="3%"
|
||||
border="1px solid #ff7875"
|
||||
borderRadius="5px"
|
||||
>
|
||||
<Flex
|
||||
marginTop={isNotSmallerScreen ? "3%" : "5%"}
|
||||
direction={isNotSmallerScreen ? "row" : "column"}
|
||||
>
|
||||
<Flex
|
||||
mt={3}
|
||||
w={isNotSmallerScreen ? "30%" : "40%"}
|
||||
justifyContent="start"
|
||||
alignItems="center"
|
||||
>
|
||||
<Text fontSize="sm">Old Admin Secret:</Text>
|
||||
</Flex>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||
mt={isNotSmallerScreen ? "0" : "3"}
|
||||
>
|
||||
<InputGroup size="sm">
|
||||
<Input
|
||||
borderRadius={5}
|
||||
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%"
|
||||
direction={isNotSmallerScreen ? "row" : "column"}
|
||||
>
|
||||
<Flex
|
||||
w={isNotSmallerScreen ? "30%" : "50%"}
|
||||
justifyContent="start"
|
||||
alignItems="center"
|
||||
>
|
||||
<Text fontSize="sm">New Admin Secret:</Text>
|
||||
</Flex>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||
mt={isNotSmallerScreen ? "0" : "3"}
|
||||
>
|
||||
<InputField
|
||||
borderRadius={5}
|
||||
mb={3}
|
||||
variables={variables}
|
||||
setVariables={setVariables}
|
||||
inputType={HiddenInputType.ADMIN_SECRET}
|
||||
fieldVisibility={fieldVisibility}
|
||||
setFieldVisibility={setFieldVisibility}
|
||||
isDisabled={adminSecret.disableInputField}
|
||||
placeholder="Enter New Admin Secret"
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SecurityAdminSecret;
|
42
dashboard/src/components/EnvComponents/SessionStorage.tsx
Normal file
42
dashboard/src/components/EnvComponents/SessionStorage.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import React from 'react';
|
||||
import { Flex, Stack, Center, Text, useMediaQuery } from '@chakra-ui/react';
|
||||
import InputField from '../InputField';
|
||||
|
||||
const SessionStorage = ({ variables, setVariables, RedisURL }: any) => {
|
||||
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
|
||||
return (
|
||||
<div>
|
||||
{' '}
|
||||
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
|
||||
Session Storage
|
||||
</Text>
|
||||
<Text fontStyle="italic" fontSize="sm" color="blackAlpha.500" mt={3}>
|
||||
Note: Redis related environment variables cannot be updated from
|
||||
dashboard. Please use .env file or OS environment variables to update
|
||||
it.
|
||||
</Text>
|
||||
<Stack spacing={6} padding="2% 0%">
|
||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Redis URL:</Text>
|
||||
</Flex>
|
||||
<Center
|
||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
||||
mt={isNotSmallerScreen ? '0' : '3'}
|
||||
>
|
||||
<InputField
|
||||
disabled
|
||||
borderRadius={5}
|
||||
variables={variables}
|
||||
setVariables={setVariables}
|
||||
inputType={RedisURL}
|
||||
placeholder="Redis URL"
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SessionStorage;
|
247
dashboard/src/components/GenerateKeysModal.tsx
Normal file
247
dashboard/src/components/GenerateKeysModal.tsx
Normal file
@@ -0,0 +1,247 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Button,
|
||||
Center,
|
||||
Flex,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
useDisclosure,
|
||||
Text,
|
||||
useToast,
|
||||
Input,
|
||||
Spinner,
|
||||
} from '@chakra-ui/react';
|
||||
import { useClient } from 'urql';
|
||||
import { FaSave } from 'react-icons/fa';
|
||||
import {
|
||||
ECDSAEncryptionType,
|
||||
HMACEncryptionType,
|
||||
RSAEncryptionType,
|
||||
SelectInputType,
|
||||
TextAreaInputType,
|
||||
} from '../constants';
|
||||
import InputField from './InputField';
|
||||
import { GenerateKeys, UpdateEnvVariables } from '../graphql/mutation';
|
||||
|
||||
interface propTypes {
|
||||
jwtType: string;
|
||||
getData: Function;
|
||||
}
|
||||
|
||||
interface stateVarTypes {
|
||||
JWT_TYPE: string;
|
||||
JWT_SECRET: string;
|
||||
JWT_PRIVATE_KEY: string;
|
||||
JWT_PUBLIC_KEY: string;
|
||||
}
|
||||
|
||||
const initState: stateVarTypes = {
|
||||
JWT_TYPE: '',
|
||||
JWT_SECRET: '',
|
||||
JWT_PRIVATE_KEY: '',
|
||||
JWT_PUBLIC_KEY: '',
|
||||
};
|
||||
|
||||
const GenerateKeysModal = ({ jwtType, getData }: propTypes) => {
|
||||
const client = useClient();
|
||||
const toast = useToast();
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const [stateVariables, setStateVariables] = React.useState<stateVarTypes>({
|
||||
...initState,
|
||||
});
|
||||
const [isLoading, setIsLoading] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isOpen) {
|
||||
setStateVariables({ ...initState, JWT_TYPE: jwtType });
|
||||
}
|
||||
}, [isOpen]);
|
||||
|
||||
const fetchKeys = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const res = await client
|
||||
.mutation(GenerateKeys, { params: { type: stateVariables.JWT_TYPE } })
|
||||
.toPromise();
|
||||
if (res?.error) {
|
||||
toast({
|
||||
title: 'Error occurred generating jwt keys',
|
||||
isClosable: true,
|
||||
status: 'error',
|
||||
position: 'bottom-right',
|
||||
});
|
||||
closeHandler();
|
||||
} else {
|
||||
setStateVariables({
|
||||
...stateVariables,
|
||||
JWT_SECRET: res?.data?._generate_jwt_keys?.secret || '',
|
||||
JWT_PRIVATE_KEY: res?.data?._generate_jwt_keys?.private_key || '',
|
||||
JWT_PUBLIC_KEY: res?.data?._generate_jwt_keys?.public_key || '',
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isOpen && stateVariables.JWT_TYPE) {
|
||||
fetchKeys();
|
||||
}
|
||||
}, [stateVariables.JWT_TYPE]);
|
||||
|
||||
const saveHandler = async () => {
|
||||
const res = await client
|
||||
.mutation(UpdateEnvVariables, { params: { ...stateVariables } })
|
||||
.toPromise();
|
||||
|
||||
if (res.error) {
|
||||
toast({
|
||||
title: 'Error occurred setting jwt keys',
|
||||
isClosable: true,
|
||||
status: 'error',
|
||||
position: 'bottom-right',
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
toast({
|
||||
title: 'JWT keys updated successfully',
|
||||
isClosable: true,
|
||||
status: 'success',
|
||||
position: 'bottom-right',
|
||||
});
|
||||
closeHandler();
|
||||
};
|
||||
|
||||
const closeHandler = () => {
|
||||
setStateVariables({ ...initState });
|
||||
getData();
|
||||
onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
colorScheme="blue"
|
||||
h="1.75rem"
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
onClick={onOpen}
|
||||
>
|
||||
Generate new keys
|
||||
</Button>
|
||||
<Modal isOpen={isOpen} onClose={closeHandler}>
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalHeader>New JWT keys</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<ModalBody>
|
||||
<Flex>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">JWT Type:</Text>
|
||||
</Flex>
|
||||
<InputField
|
||||
variables={stateVariables}
|
||||
setVariables={setStateVariables}
|
||||
inputType={SelectInputType.JWT_TYPE}
|
||||
value={SelectInputType.JWT_TYPE}
|
||||
options={{
|
||||
...HMACEncryptionType,
|
||||
...RSAEncryptionType,
|
||||
...ECDSAEncryptionType,
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
{isLoading ? (
|
||||
<Center minH="25vh">
|
||||
<Spinner />
|
||||
</Center>
|
||||
) : (
|
||||
<>
|
||||
{Object.values(HMACEncryptionType).includes(
|
||||
stateVariables.JWT_TYPE
|
||||
) ? (
|
||||
<Flex marginTop="8">
|
||||
<Flex w="23%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">JWT Secret</Text>
|
||||
</Flex>
|
||||
<Center w="77%">
|
||||
<Input
|
||||
size="sm"
|
||||
value={stateVariables.JWT_SECRET}
|
||||
onChange={(event: any) =>
|
||||
setStateVariables({
|
||||
...stateVariables,
|
||||
JWT_SECRET: event.target.value,
|
||||
})
|
||||
}
|
||||
readOnly
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
) : (
|
||||
<>
|
||||
<Flex marginTop="8">
|
||||
<Flex w="23%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Public Key</Text>
|
||||
</Flex>
|
||||
<Center w="77%">
|
||||
<InputField
|
||||
variables={stateVariables}
|
||||
setVariables={setStateVariables}
|
||||
inputType={TextAreaInputType.JWT_PUBLIC_KEY}
|
||||
placeholder="Add public key here"
|
||||
minH="25vh"
|
||||
readOnly
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex marginTop="8">
|
||||
<Flex w="23%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Private Key</Text>
|
||||
</Flex>
|
||||
<Center w="77%">
|
||||
<InputField
|
||||
variables={stateVariables}
|
||||
setVariables={setStateVariables}
|
||||
inputType={TextAreaInputType.JWT_PRIVATE_KEY}
|
||||
placeholder="Add private key here"
|
||||
minH="25vh"
|
||||
readOnly
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button
|
||||
leftIcon={<FaSave />}
|
||||
colorScheme="blue"
|
||||
variant="solid"
|
||||
onClick={saveHandler}
|
||||
isDisabled={isLoading}
|
||||
>
|
||||
<Center h="100%" pt="5%">
|
||||
Apply
|
||||
</Center>
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default GenerateKeysModal;
|
@@ -13,6 +13,7 @@ import {
|
||||
Textarea,
|
||||
Switch,
|
||||
Code,
|
||||
Text,
|
||||
} from '@chakra-ui/react';
|
||||
import {
|
||||
FaRegClone,
|
||||
@@ -116,7 +117,7 @@ const InputField = ({
|
||||
<InputGroup size="sm">
|
||||
<Input
|
||||
{...props}
|
||||
value={variables[inputType]}
|
||||
value={variables[inputType] ?? ''}
|
||||
onChange={(
|
||||
event: Event & {
|
||||
target: HTMLInputElement;
|
||||
@@ -181,8 +182,9 @@ const InputField = ({
|
||||
<Flex
|
||||
border="1px solid #e2e8f0"
|
||||
w="100%"
|
||||
borderRadius={5}
|
||||
paddingTop="0.5%"
|
||||
overflowX="scroll"
|
||||
overflowX={variables[inputType].length > 3 ? 'scroll' : 'hidden'}
|
||||
overflowY="hidden"
|
||||
justifyContent="start"
|
||||
alignItems="center"
|
||||
@@ -220,7 +222,7 @@ const InputField = ({
|
||||
size="xs"
|
||||
minW="150px"
|
||||
placeholder="add a new value"
|
||||
value={inputData[inputType]}
|
||||
value={inputData[inputType] ?? ''}
|
||||
onChange={(e: any) => {
|
||||
setInputData({ ...inputData, [inputType]: e.target.value });
|
||||
}}
|
||||
@@ -300,7 +302,9 @@ const InputField = ({
|
||||
if (Object.values(SwitchInputType).includes(inputType)) {
|
||||
return (
|
||||
<Flex w="25%" justifyContent="space-between">
|
||||
<Code h="75%">Off</Code>
|
||||
<Text h="75%" fontWeight="bold" marginRight="2">
|
||||
Off
|
||||
</Text>
|
||||
<Switch
|
||||
size="md"
|
||||
isChecked={variables[inputType]}
|
||||
@@ -311,7 +315,9 @@ const InputField = ({
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Code h="75%">On</Code>
|
||||
<Text h="75%" fontWeight="bold" marginLeft="2">
|
||||
On
|
||||
</Text>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
385
dashboard/src/components/InviteMembersModal.tsx
Normal file
385
dashboard/src/components/InviteMembersModal.tsx
Normal file
@@ -0,0 +1,385 @@
|
||||
import React, { useState, useCallback, useEffect } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Center,
|
||||
Flex,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
Tabs,
|
||||
TabList,
|
||||
Tab,
|
||||
TabPanels,
|
||||
TabPanel,
|
||||
InputGroup,
|
||||
Input,
|
||||
InputRightElement,
|
||||
Text,
|
||||
Link,
|
||||
Tooltip,
|
||||
} from '@chakra-ui/react';
|
||||
import { useClient } from 'urql';
|
||||
import { FaUserPlus, FaMinusCircle, FaPlus, FaUpload } from 'react-icons/fa';
|
||||
import { useDropzone } from 'react-dropzone';
|
||||
import { validateEmail, validateURI } from '../utils';
|
||||
import { InviteMembers } from '../graphql/mutation';
|
||||
import { ArrayInputOperations } from '../constants';
|
||||
import parseCSV from '../utils/parseCSV';
|
||||
|
||||
interface stateDataTypes {
|
||||
value: string;
|
||||
isInvalid: boolean;
|
||||
}
|
||||
|
||||
interface requestParamTypes {
|
||||
emails: string[];
|
||||
redirect_uri?: string;
|
||||
}
|
||||
|
||||
const initData: stateDataTypes = {
|
||||
value: '',
|
||||
isInvalid: false,
|
||||
};
|
||||
|
||||
const InviteMembersModal = ({
|
||||
updateUserList,
|
||||
disabled = true,
|
||||
}: {
|
||||
updateUserList: Function;
|
||||
disabled: boolean;
|
||||
}) => {
|
||||
const client = useClient();
|
||||
const toast = useToast();
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const [tabIndex, setTabIndex] = useState<number>(0);
|
||||
const [redirectURI, setRedirectURI] = useState<stateDataTypes>({
|
||||
...initData,
|
||||
});
|
||||
const [emails, setEmails] = useState<stateDataTypes[]>([{ ...initData }]);
|
||||
const [disableSendButton, setDisableSendButton] = useState<boolean>(false);
|
||||
const [loading, setLoading] = React.useState<boolean>(false);
|
||||
useEffect(() => {
|
||||
if (redirectURI.isInvalid) {
|
||||
setDisableSendButton(true);
|
||||
} else if (emails.some((emailData) => emailData.isInvalid)) {
|
||||
setDisableSendButton(true);
|
||||
} else {
|
||||
setDisableSendButton(false);
|
||||
}
|
||||
}, [redirectURI, emails]);
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
setRedirectURI({ ...initData });
|
||||
setEmails([{ ...initData }]);
|
||||
};
|
||||
}, []);
|
||||
const sendInviteHandler = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const emailList = emails
|
||||
.filter((emailData) => !emailData.isInvalid)
|
||||
.map((emailData) => emailData.value);
|
||||
const params: requestParamTypes = {
|
||||
emails: emailList,
|
||||
};
|
||||
if (redirectURI.value !== '' && !redirectURI.isInvalid) {
|
||||
params.redirect_uri = redirectURI.value;
|
||||
}
|
||||
if (emailList.length > 0) {
|
||||
const res = await client
|
||||
.mutation(InviteMembers, {
|
||||
params,
|
||||
})
|
||||
.toPromise();
|
||||
if (res.error) {
|
||||
throw new Error('Internal server error');
|
||||
return;
|
||||
}
|
||||
toast({
|
||||
title: 'Invites sent successfully!',
|
||||
isClosable: true,
|
||||
status: 'success',
|
||||
position: 'bottom-right',
|
||||
});
|
||||
setLoading(false);
|
||||
updateUserList();
|
||||
} else {
|
||||
throw new Error('Please add emails');
|
||||
}
|
||||
} catch (error: any) {
|
||||
toast({
|
||||
title: error?.message || 'Error occurred, try again!',
|
||||
isClosable: true,
|
||||
status: 'error',
|
||||
position: 'bottom-right',
|
||||
});
|
||||
setLoading(false);
|
||||
}
|
||||
closeModalHandler();
|
||||
};
|
||||
const updateEmailListHandler = (operation: string, index: number = 0) => {
|
||||
switch (operation) {
|
||||
case ArrayInputOperations.APPEND:
|
||||
setEmails([...emails, { ...initData }]);
|
||||
break;
|
||||
case ArrayInputOperations.REMOVE:
|
||||
const updatedEmailList = [...emails];
|
||||
updatedEmailList.splice(index, 1);
|
||||
setEmails(updatedEmailList);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
const inputChangeHandler = (value: string, index: number) => {
|
||||
const updatedEmailList = [...emails];
|
||||
updatedEmailList[index].value = value;
|
||||
updatedEmailList[index].isInvalid = !validateEmail(value);
|
||||
setEmails(updatedEmailList);
|
||||
};
|
||||
const changeTabsHandler = (index: number) => {
|
||||
setTabIndex(index);
|
||||
};
|
||||
const onDrop = useCallback(async (acceptedFiles) => {
|
||||
const result = await parseCSV(acceptedFiles[0], ',');
|
||||
setEmails(result);
|
||||
changeTabsHandler(0);
|
||||
}, []);
|
||||
const setRedirectURIHandler = (value: string) => {
|
||||
const updatedRedirectURI: stateDataTypes = {
|
||||
value: '',
|
||||
isInvalid: false,
|
||||
};
|
||||
updatedRedirectURI.value = value;
|
||||
updatedRedirectURI.isInvalid = !validateURI(value);
|
||||
setRedirectURI(updatedRedirectURI);
|
||||
};
|
||||
const { getRootProps, getInputProps, isDragActive } = useDropzone({
|
||||
onDrop,
|
||||
accept: 'text/csv',
|
||||
});
|
||||
const closeModalHandler = () => {
|
||||
setRedirectURI({
|
||||
value: '',
|
||||
isInvalid: false,
|
||||
});
|
||||
setEmails([
|
||||
{
|
||||
value: '',
|
||||
isInvalid: false,
|
||||
},
|
||||
]);
|
||||
onClose();
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
leftIcon={<FaUserPlus />}
|
||||
colorScheme="blue"
|
||||
variant="solid"
|
||||
onClick={onOpen}
|
||||
isDisabled={disabled}
|
||||
size="sm"
|
||||
>
|
||||
<Center h="100%">
|
||||
{disabled ? (
|
||||
<Tooltip
|
||||
mr={8}
|
||||
mt={1}
|
||||
hasArrow
|
||||
bg="gray.300"
|
||||
color="black"
|
||||
label="Email verification is disabled, refer to 'Features' tab within 'Environment' to enable it."
|
||||
>
|
||||
Invite Members
|
||||
</Tooltip>
|
||||
) : (
|
||||
'Invite Members'
|
||||
)}
|
||||
</Center>{' '}
|
||||
</Button>
|
||||
<Modal isOpen={isOpen} onClose={closeModalHandler} size="xl">
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalHeader>Invite Members</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<ModalBody>
|
||||
<Tabs
|
||||
isFitted
|
||||
variant="enclosed"
|
||||
index={tabIndex}
|
||||
onChange={changeTabsHandler}
|
||||
>
|
||||
<TabList>
|
||||
<Tab>Enter emails</Tab>
|
||||
<Tab>Upload CSV</Tab>
|
||||
</TabList>
|
||||
<TabPanels
|
||||
border="1px"
|
||||
borderTop="0"
|
||||
borderBottomRadius="5px"
|
||||
borderColor="inherit"
|
||||
>
|
||||
<TabPanel>
|
||||
<Flex flexDirection="column">
|
||||
<Flex
|
||||
width="100%"
|
||||
justifyContent="start"
|
||||
alignItems="center"
|
||||
marginBottom="2%"
|
||||
>
|
||||
<Flex marginLeft="2.5%">Redirect URI</Flex>
|
||||
</Flex>
|
||||
<Flex
|
||||
width="100%"
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
marginBottom="2%"
|
||||
>
|
||||
<InputGroup size="md" marginBottom="2.5%">
|
||||
<Input
|
||||
pr="4.5rem"
|
||||
type="text"
|
||||
placeholder="https://domain.com/sign-up"
|
||||
value={redirectURI.value}
|
||||
isInvalid={redirectURI.isInvalid}
|
||||
onChange={(e) =>
|
||||
setRedirectURIHandler(e.currentTarget.value)
|
||||
}
|
||||
/>
|
||||
</InputGroup>
|
||||
</Flex>
|
||||
<Flex
|
||||
width="100%"
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
marginBottom="2%"
|
||||
>
|
||||
<Flex marginLeft="2.5%">Emails</Flex>
|
||||
<Flex>
|
||||
<Button
|
||||
leftIcon={<FaPlus />}
|
||||
colorScheme="blue"
|
||||
h="1.75rem"
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
onClick={() =>
|
||||
updateEmailListHandler(ArrayInputOperations.APPEND)
|
||||
}
|
||||
>
|
||||
Add more emails
|
||||
</Button>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex flexDirection="column" maxH={250} overflowY="scroll">
|
||||
{emails.map((emailData, index) => (
|
||||
<Flex
|
||||
key={`email-data-${index}`}
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
>
|
||||
<InputGroup size="md" marginBottom="2.5%">
|
||||
<Input
|
||||
pr="4.5rem"
|
||||
type="text"
|
||||
placeholder="name@domain.com"
|
||||
value={emailData.value}
|
||||
isInvalid={emailData.isInvalid}
|
||||
onChange={(e) =>
|
||||
inputChangeHandler(e.currentTarget.value, index)
|
||||
}
|
||||
/>
|
||||
<InputRightElement width="3rem">
|
||||
<Button
|
||||
h="1.75rem"
|
||||
size="sm"
|
||||
colorScheme="blackAlpha"
|
||||
variant="ghost"
|
||||
onClick={() =>
|
||||
updateEmailListHandler(
|
||||
ArrayInputOperations.REMOVE,
|
||||
index
|
||||
)
|
||||
}
|
||||
>
|
||||
<FaMinusCircle />
|
||||
</Button>
|
||||
</InputRightElement>
|
||||
</InputGroup>
|
||||
</Flex>
|
||||
))}
|
||||
</Flex>
|
||||
</Flex>
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<Flex
|
||||
justify="center"
|
||||
align="center"
|
||||
textAlign="center"
|
||||
bg="#f0f0f0"
|
||||
h={230}
|
||||
p={50}
|
||||
m={2}
|
||||
borderRadius={5}
|
||||
{...getRootProps()}
|
||||
>
|
||||
<input {...getInputProps()} />
|
||||
{isDragActive ? (
|
||||
<Text>Drop the files here...</Text>
|
||||
) : (
|
||||
<Flex
|
||||
flexDirection="column"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
>
|
||||
<Center boxSize="20" color="blackAlpha.500">
|
||||
<FaUpload fontSize="40" />
|
||||
</Center>
|
||||
<Text>
|
||||
Drag 'n' drop the csv file here, or click to select.
|
||||
</Text>
|
||||
<Text size="xs">
|
||||
Download{' '}
|
||||
<Link
|
||||
href={`/dashboard/public/sample.csv`}
|
||||
download="sample.csv"
|
||||
color="blue.600"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{' '}
|
||||
sample.csv
|
||||
</Link>{' '}
|
||||
and modify it.{' '}
|
||||
</Text>
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
colorScheme="blue"
|
||||
variant="solid"
|
||||
onClick={sendInviteHandler}
|
||||
isDisabled={disableSendButton || loading}
|
||||
>
|
||||
<Center h="100%" pt="5%">
|
||||
Send
|
||||
</Center>
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default InviteMembersModal;
|
@@ -1,4 +1,4 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import React, { Fragment, ReactNode } from 'react';
|
||||
import {
|
||||
IconButton,
|
||||
Box,
|
||||
@@ -17,31 +17,99 @@ import {
|
||||
MenuButton,
|
||||
MenuItem,
|
||||
MenuList,
|
||||
Accordion,
|
||||
AccordionButton,
|
||||
AccordionPanel,
|
||||
AccordionItem,
|
||||
useMediaQuery,
|
||||
} from '@chakra-ui/react';
|
||||
import {
|
||||
FiHome,
|
||||
FiUser,
|
||||
FiCode,
|
||||
FiSettings,
|
||||
FiMenu,
|
||||
FiUser,
|
||||
FiUsers,
|
||||
FiChevronDown,
|
||||
} from 'react-icons/fi';
|
||||
import { BiCustomize } from 'react-icons/bi';
|
||||
import { AiOutlineKey } from 'react-icons/ai';
|
||||
import { SiOpenaccess, SiJsonwebtokens } from 'react-icons/si';
|
||||
import { MdSecurity } from 'react-icons/md';
|
||||
import { RiDatabase2Line } from 'react-icons/ri';
|
||||
import { BsCheck2Circle } from 'react-icons/bs';
|
||||
import { HiOutlineMail, HiOutlineOfficeBuilding } from 'react-icons/hi';
|
||||
import { IconType } from 'react-icons';
|
||||
import { ReactText } from 'react';
|
||||
import { useMutation } from 'urql';
|
||||
import { useMutation, useQuery } from 'urql';
|
||||
import { NavLink, useNavigate, useLocation } from 'react-router-dom';
|
||||
import { useAuthContext } from '../contexts/AuthContext';
|
||||
import { AdminLogout } from '../graphql/mutation';
|
||||
import { MetaQuery } from '../graphql/queries';
|
||||
|
||||
interface SubRoutes {
|
||||
name: string;
|
||||
icon: IconType;
|
||||
route: string;
|
||||
}
|
||||
|
||||
interface LinkItemProps {
|
||||
name: string;
|
||||
icon: IconType;
|
||||
route: string;
|
||||
subRoutes?: SubRoutes[];
|
||||
}
|
||||
const LinkItems: Array<LinkItemProps> = [
|
||||
// { name: 'Home', icon: FiHome, route: '/' },
|
||||
{ name: 'Environment Variables', icon: FiSettings, route: '/' },
|
||||
{
|
||||
name: 'Environment ',
|
||||
icon: FiSettings,
|
||||
route: '/',
|
||||
subRoutes: [
|
||||
{
|
||||
name: 'OAuth Config',
|
||||
icon: AiOutlineKey,
|
||||
route: '/oauth-setting',
|
||||
},
|
||||
|
||||
{ name: 'Roles', icon: FiUser, route: '/roles' },
|
||||
{
|
||||
name: 'JWT Secrets',
|
||||
icon: SiJsonwebtokens,
|
||||
route: '/jwt-config',
|
||||
},
|
||||
{
|
||||
name: 'Session Storage',
|
||||
icon: RiDatabase2Line,
|
||||
route: '/session-storage',
|
||||
},
|
||||
{
|
||||
name: 'Email Configurations',
|
||||
icon: HiOutlineMail,
|
||||
route: '/email-config',
|
||||
},
|
||||
{
|
||||
name: 'Domain White Listing',
|
||||
icon: BsCheck2Circle,
|
||||
route: '/whitelist-variables',
|
||||
},
|
||||
{
|
||||
name: 'Organization Info',
|
||||
icon: HiOutlineOfficeBuilding,
|
||||
route: '/organization-info',
|
||||
},
|
||||
{ name: 'Access Token', icon: SiOpenaccess, route: '/access-token' },
|
||||
{
|
||||
name: 'Features',
|
||||
icon: BiCustomize,
|
||||
route: '/features',
|
||||
},
|
||||
{ name: 'Database', icon: RiDatabase2Line, route: '/db-cred' },
|
||||
{
|
||||
name: ' Security',
|
||||
icon: MdSecurity,
|
||||
route: '/admin-secret',
|
||||
},
|
||||
],
|
||||
},
|
||||
{ name: 'Users', icon: FiUsers, route: '/users' },
|
||||
];
|
||||
|
||||
@@ -51,20 +119,28 @@ interface SidebarProps extends BoxProps {
|
||||
|
||||
export const Sidebar = ({ onClose, ...rest }: SidebarProps) => {
|
||||
const { pathname } = useLocation();
|
||||
const [{ data }] = useQuery({ query: MetaQuery });
|
||||
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
|
||||
return (
|
||||
<Box
|
||||
transition="3s ease"
|
||||
bg={useColorModeValue('white', 'gray.900')}
|
||||
borderRight="1px"
|
||||
borderRightColor={useColorModeValue('gray.200', 'gray.700')}
|
||||
w={{ base: 'full', md: 60 }}
|
||||
w={{ base: 'full', md: '64' }}
|
||||
pos="fixed"
|
||||
h="full"
|
||||
{...rest}
|
||||
>
|
||||
<Flex h="20" alignItems="center" mx="8" justifyContent="space-between">
|
||||
<Flex
|
||||
h="20"
|
||||
alignItems="center"
|
||||
mx="18"
|
||||
justifyContent="space-between"
|
||||
flexDirection="row"
|
||||
>
|
||||
<NavLink to="/">
|
||||
<Flex alignItems="center">
|
||||
<Flex alignItems="center" mt="6">
|
||||
<Image
|
||||
src="https://authorizer.dev/images/logo.png"
|
||||
alt="logo"
|
||||
@@ -77,34 +153,104 @@ export const Sidebar = ({ onClose, ...rest }: SidebarProps) => {
|
||||
</NavLink>
|
||||
<CloseButton display={{ base: 'flex', md: 'none' }} onClick={onClose} />
|
||||
</Flex>
|
||||
{LinkItems.map((link) => (
|
||||
<NavLink key={link.name} to={link.route}>
|
||||
<NavItem
|
||||
icon={link.icon}
|
||||
color={pathname === link.route ? 'blue.500' : ''}
|
||||
>
|
||||
{link.name}
|
||||
</NavItem>
|
||||
</NavLink>
|
||||
))}
|
||||
|
||||
<Link
|
||||
href="/playground"
|
||||
target="_blank"
|
||||
style={{
|
||||
textDecoration: 'none',
|
||||
}}
|
||||
_focus={{ _boxShadow: 'none' }}
|
||||
>
|
||||
<NavItem icon={FiCode}>API Playground</NavItem>
|
||||
</Link>
|
||||
<Accordion defaultIndex={[0]} allowMultiple>
|
||||
<AccordionItem textAlign="center" border="none" w="100%">
|
||||
{LinkItems.map((link) =>
|
||||
link?.subRoutes ? (
|
||||
<div key={link.name}>
|
||||
<AccordionButton _focus={{ boxShadow: 'none' }}>
|
||||
<Text as="div" fontSize="md">
|
||||
<NavItem
|
||||
icon={link.icon}
|
||||
color={pathname === link.route ? 'blue.500' : ''}
|
||||
style={{ outline: 'unset' }}
|
||||
height={12}
|
||||
ml={-1}
|
||||
mb={isNotSmallerScreen ? -1 : -4}
|
||||
w={isNotSmallerScreen ? '100%' : '310%'}
|
||||
>
|
||||
<Fragment>
|
||||
{link.name}
|
||||
<Box display={{ base: 'none', md: 'flex' }} ml={12}>
|
||||
<FiChevronDown />
|
||||
</Box>
|
||||
</Fragment>
|
||||
</NavItem>
|
||||
</Text>
|
||||
</AccordionButton>
|
||||
<AccordionPanel>
|
||||
{link.subRoutes?.map((sublink) => (
|
||||
<NavLink
|
||||
key={sublink.name}
|
||||
to={sublink.route}
|
||||
onClick={onClose}
|
||||
>
|
||||
{' '}
|
||||
<Text as="div" fontSize="xs" ml={2}>
|
||||
<NavItem
|
||||
icon={sublink.icon}
|
||||
color={pathname === sublink.route ? 'blue.500' : ''}
|
||||
height={8}
|
||||
>
|
||||
{sublink.name}
|
||||
</NavItem>{' '}
|
||||
</Text>
|
||||
</NavLink>
|
||||
))}
|
||||
</AccordionPanel>
|
||||
</div>
|
||||
) : (
|
||||
<NavLink key={link.name} to={link.route}>
|
||||
{' '}
|
||||
<Text as="div" fontSize="md" w="100%" mt={-2}>
|
||||
<NavItem
|
||||
icon={link.icon}
|
||||
color={pathname === link.route ? 'blue.500' : ''}
|
||||
height={12}
|
||||
onClick={onClose}
|
||||
>
|
||||
{link.name}
|
||||
</NavItem>{' '}
|
||||
</Text>
|
||||
</NavLink>
|
||||
)
|
||||
)}
|
||||
<Link
|
||||
href="/playground"
|
||||
target="_blank"
|
||||
style={{
|
||||
textDecoration: 'none',
|
||||
}}
|
||||
_focus={{ _boxShadow: 'none' }}
|
||||
>
|
||||
<NavItem icon={FiCode}>API Playground</NavItem>
|
||||
</Link>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
|
||||
{data?.meta?.version && (
|
||||
<Flex alignContent="center">
|
||||
{' '}
|
||||
<Text
|
||||
color="gray.400"
|
||||
fontSize="sm"
|
||||
textAlign="center"
|
||||
position="absolute"
|
||||
bottom="5"
|
||||
left="7"
|
||||
>
|
||||
Current Version: {data.meta.version}
|
||||
</Text>
|
||||
</Flex>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
interface NavItemProps extends FlexProps {
|
||||
icon: IconType;
|
||||
children: ReactText;
|
||||
children: ReactText | JSX.Element | JSX.Element[];
|
||||
}
|
||||
export const NavItem = ({ icon, children, ...rest }: NavItemProps) => {
|
||||
return (
|
||||
@@ -152,7 +298,7 @@ export const MobileNav = ({ onOpen, ...rest }: MobileProps) => {
|
||||
|
||||
return (
|
||||
<Flex
|
||||
ml={{ base: 0, md: 60 }}
|
||||
ml={{ base: 0, md: 64 }}
|
||||
px={{ base: 4, md: 4 }}
|
||||
height="20"
|
||||
position="fixed"
|
||||
@@ -189,7 +335,7 @@ export const MobileNav = ({ onOpen, ...rest }: MobileProps) => {
|
||||
transition="all 0.3s"
|
||||
_focus={{ boxShadow: 'none' }}
|
||||
>
|
||||
<HStack>
|
||||
<HStack mr={5}>
|
||||
<FiUser />
|
||||
<VStack
|
||||
display={{ base: 'none', md: 'flex' }}
|
||||
|
@@ -2,10 +2,13 @@ export const LOGO_URL =
|
||||
'https://user-images.githubusercontent.com/6964334/147834043-fc384cab-e7ca-40f8-9663-38fc25fd5f3a.png';
|
||||
|
||||
export const TextInputType = {
|
||||
ACCESS_TOKEN_EXPIRY_TIME: 'ACCESS_TOKEN_EXPIRY_TIME',
|
||||
CLIENT_ID: 'CLIENT_ID',
|
||||
GOOGLE_CLIENT_ID: 'GOOGLE_CLIENT_ID',
|
||||
GITHUB_CLIENT_ID: 'GITHUB_CLIENT_ID',
|
||||
FACEBOOK_CLIENT_ID: 'FACEBOOK_CLIENT_ID',
|
||||
LINKEDIN_CLIENT_ID: 'LINKEDIN_CLIENT_ID',
|
||||
APPLE_CLIENT_ID: 'APPLE_CLIENT_ID',
|
||||
JWT_ROLE_CLAIM: 'JWT_ROLE_CLAIM',
|
||||
REDIS_URL: 'REDIS_URL',
|
||||
SMTP_HOST: 'SMTP_HOST',
|
||||
@@ -30,6 +33,8 @@ export const HiddenInputType = {
|
||||
GOOGLE_CLIENT_SECRET: 'GOOGLE_CLIENT_SECRET',
|
||||
GITHUB_CLIENT_SECRET: 'GITHUB_CLIENT_SECRET',
|
||||
FACEBOOK_CLIENT_SECRET: 'FACEBOOK_CLIENT_SECRET',
|
||||
LINKEDIN_CLIENT_SECRET: 'LINKEDIN_CLIENT_SECRET',
|
||||
APPLE_CLIENT_SECRET: 'APPLE_CLIENT_SECRET',
|
||||
JWT_SECRET: 'JWT_SECRET',
|
||||
SMTP_PASSWORD: 'SMTP_PASSWORD',
|
||||
ADMIN_SECRET: 'ADMIN_SECRET',
|
||||
@@ -60,6 +65,8 @@ export const SwitchInputType = {
|
||||
DISABLE_MAGIC_LINK_LOGIN: 'DISABLE_MAGIC_LINK_LOGIN',
|
||||
DISABLE_EMAIL_VERIFICATION: 'DISABLE_EMAIL_VERIFICATION',
|
||||
DISABLE_BASIC_AUTHENTICATION: 'DISABLE_BASIC_AUTHENTICATION',
|
||||
DISABLE_SIGN_UP: 'DISABLE_SIGN_UP',
|
||||
DISABLE_REDIS_FOR_ENV: 'DISABLE_REDIS_FOR_ENV',
|
||||
};
|
||||
|
||||
export const DateInputType = {
|
||||
@@ -88,3 +95,59 @@ export const ECDSAEncryptionType = {
|
||||
ES384: 'ES384',
|
||||
ES512: 'ES512',
|
||||
};
|
||||
|
||||
export 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;
|
||||
LINKEDIN_CLIENT_ID: string;
|
||||
LINKEDIN_CLIENT_SECRET: string;
|
||||
APPLE_CLIENT_ID: string;
|
||||
APPLE_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;
|
||||
DISABLE_SIGN_UP: boolean;
|
||||
OLD_ADMIN_SECRET: string;
|
||||
DATABASE_NAME: string;
|
||||
DATABASE_TYPE: string;
|
||||
DATABASE_URL: string;
|
||||
ACCESS_TOKEN_EXPIRY_TIME: string;
|
||||
}
|
||||
|
||||
export const envSubViews = {
|
||||
INSTANCE_INFO: 'instance-info',
|
||||
ROLES: 'roles',
|
||||
JWT_CONFIG: 'jwt-config',
|
||||
SESSION_STORAGE: 'session-storage',
|
||||
EMAIL_CONFIG: 'email-config',
|
||||
WHITELIST_VARIABLES: 'whitelist-variables',
|
||||
ORGANIZATION_INFO: 'organization-info',
|
||||
ACCESS_TOKEN: 'access-token',
|
||||
FEATURES: 'features',
|
||||
ADMIN_SECRET: 'admin-secret',
|
||||
DB_CRED: 'db-cred',
|
||||
};
|
||||
|
@@ -45,3 +45,37 @@ export const DeleteUser = `
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const InviteMembers = `
|
||||
mutation inviteMembers($params: InviteMemberInput!) {
|
||||
_invite_members(params: $params) {
|
||||
message
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const RevokeAccess = `
|
||||
mutation revokeAccess($param: UpdateAccessInput!) {
|
||||
_revoke_access(param: $param) {
|
||||
message
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const EnableAccess = `
|
||||
mutation revokeAccess($param: UpdateAccessInput!) {
|
||||
_enable_access(param: $param) {
|
||||
message
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const GenerateKeys = `
|
||||
mutation generateKeys($params: GenerateJWTKeysInput!) {
|
||||
_generate_jwt_keys(params: $params) {
|
||||
secret
|
||||
public_key
|
||||
private_key
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@@ -1,3 +1,12 @@
|
||||
export const MetaQuery = `
|
||||
query MetaQuery {
|
||||
meta {
|
||||
version
|
||||
client_id
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const AdminSessionQuery = `
|
||||
query {
|
||||
_admin_session{
|
||||
@@ -17,9 +26,13 @@ export const EnvVariablesQuery = `
|
||||
GITHUB_CLIENT_SECRET,
|
||||
FACEBOOK_CLIENT_ID,
|
||||
FACEBOOK_CLIENT_SECRET,
|
||||
ROLES,
|
||||
LINKEDIN_CLIENT_ID,
|
||||
LINKEDIN_CLIENT_SECRET,
|
||||
APPLE_CLIENT_ID,
|
||||
APPLE_CLIENT_SECRET,
|
||||
DEFAULT_ROLES,
|
||||
PROTECTED_ROLES,
|
||||
ROLES,
|
||||
JWT_TYPE,
|
||||
JWT_SECRET,
|
||||
JWT_ROLE_CLAIM,
|
||||
@@ -39,10 +52,13 @@ export const EnvVariablesQuery = `
|
||||
DISABLE_MAGIC_LINK_LOGIN,
|
||||
DISABLE_EMAIL_VERIFICATION,
|
||||
DISABLE_BASIC_AUTHENTICATION,
|
||||
DISABLE_SIGN_UP,
|
||||
DISABLE_REDIS_FOR_ENV,
|
||||
CUSTOM_ACCESS_TOKEN_SCRIPT,
|
||||
DATABASE_NAME,
|
||||
DATABASE_TYPE,
|
||||
DATABASE_URL,
|
||||
ACCESS_TOKEN_EXPIRY_TIME,
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -71,7 +87,16 @@ export const UserDetailsQuery = `
|
||||
signup_methods
|
||||
roles
|
||||
created_at
|
||||
revoked_timestamp
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const EmailVerificationQuery = `
|
||||
query {
|
||||
_env{
|
||||
DISABLE_EMAIL_VERIFICATION
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@@ -2,4 +2,9 @@ import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById('root'));
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
<App />
|
||||
</div>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
|
@@ -1,18 +1,28 @@
|
||||
import { Box, Center, Flex, Image, Text } from '@chakra-ui/react';
|
||||
import {
|
||||
Box,
|
||||
Flex,
|
||||
Image,
|
||||
Text,
|
||||
Spinner,
|
||||
useMediaQuery,
|
||||
} from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
import { LOGO_URL } from '../constants';
|
||||
import { useQuery } from 'urql';
|
||||
import { MetaQuery } from '../graphql/queries';
|
||||
|
||||
export function AuthLayout({ children }: { children: React.ReactNode }) {
|
||||
const [{ fetching, data }] = useQuery({ query: MetaQuery });
|
||||
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
|
||||
return (
|
||||
<Flex
|
||||
flexWrap="wrap"
|
||||
h="100%"
|
||||
h="100vh"
|
||||
bg="gray.100"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
flexDirection="column"
|
||||
direction={['column', 'column']}
|
||||
padding={['2%', '2%', '2%', '2%']}
|
||||
>
|
||||
<Flex alignItems="center">
|
||||
<Flex alignItems="center" maxW="100%">
|
||||
<Image
|
||||
src="https://authorizer.dev/images/logo.png"
|
||||
alt="logo"
|
||||
@@ -23,9 +33,26 @@ export function AuthLayout({ children }: { children: React.ReactNode }) {
|
||||
</Text>
|
||||
</Flex>
|
||||
|
||||
<Box p="6" m="5" rounded="5" bg="white" w="500px" shadow="xl">
|
||||
{children}
|
||||
</Box>
|
||||
{fetching ? (
|
||||
<Spinner />
|
||||
) : (
|
||||
<>
|
||||
<Box
|
||||
p="6"
|
||||
m="5"
|
||||
rounded="5"
|
||||
bg="white"
|
||||
w={isNotSmallerScreen ? '500px' : '450px'}
|
||||
shadow="xl"
|
||||
maxW="100%"
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
<Text color="gray.600" fontSize="sm">
|
||||
Current Version: {data.meta.version}
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
@@ -31,7 +31,7 @@ export function DashboardLayout({ children }: { children: ReactNode }) {
|
||||
</Drawer>
|
||||
{/* mobilenav */}
|
||||
<MobileNav onOpen={onOpen} />
|
||||
<Box ml={{ base: 0, md: 60 }} p="4" pt="24">
|
||||
<Box ml={{ base: 0, md: '64' }} p="4" pt="24">
|
||||
{children}
|
||||
</Box>
|
||||
</Box>
|
||||
|
@@ -6,7 +6,6 @@ import {
|
||||
useToast,
|
||||
VStack,
|
||||
Text,
|
||||
Divider,
|
||||
} from '@chakra-ui/react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useMutation } from 'urql';
|
||||
@@ -102,10 +101,10 @@ export default function Auth() {
|
||||
</FormControl>
|
||||
<Button
|
||||
isLoading={signUpResult.fetching || loginResult.fetching}
|
||||
loadingText="Submitting"
|
||||
colorScheme="blue"
|
||||
size="lg"
|
||||
w="100%"
|
||||
d="block"
|
||||
type="submit"
|
||||
>
|
||||
{isLogin ? 'Login' : 'Sign up'}
|
||||
|
@@ -1,80 +1,35 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Divider,
|
||||
Flex,
|
||||
Stack,
|
||||
Center,
|
||||
Text,
|
||||
Button,
|
||||
Input,
|
||||
InputGroup,
|
||||
InputRightElement,
|
||||
useToast,
|
||||
} from '@chakra-ui/react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { Box, Flex, Stack, Button, useToast } from '@chakra-ui/react';
|
||||
import { useClient } from 'urql';
|
||||
import {
|
||||
FaGoogle,
|
||||
FaGithub,
|
||||
FaFacebookF,
|
||||
FaSave,
|
||||
FaRegEyeSlash,
|
||||
FaRegEye,
|
||||
} from 'react-icons/fa';
|
||||
import { FaSave } 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,
|
||||
envVarTypes,
|
||||
envSubViews,
|
||||
} from '../constants';
|
||||
import { UpdateEnvVariables } from '../graphql/mutation';
|
||||
import { getObjectDiff, capitalizeFirstLetter } from '../utils';
|
||||
import OAuthConfig from '../components/EnvComponents/OAuthConfig';
|
||||
import Roles from '../components/EnvComponents/Roles';
|
||||
import JWTConfigurations from '../components/EnvComponents/JWTConfiguration';
|
||||
import SessionStorage from '../components/EnvComponents/SessionStorage';
|
||||
import EmailConfigurations from '../components/EnvComponents/EmailConfiguration';
|
||||
import DomainWhiteListing from '../components/EnvComponents/DomainWhitelisting';
|
||||
import OrganizationInfo from '../components/EnvComponents/OrganizationInfo';
|
||||
import AccessToken from '../components/EnvComponents/AccessToken';
|
||||
import Features from '../components/EnvComponents/Features';
|
||||
import SecurityAdminSecret from '../components/EnvComponents/SecurityAdminSecret';
|
||||
import DatabaseCredentials from '../components/EnvComponents/DatabaseCredentials';
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
export default function Environment() {
|
||||
const Environment = () => {
|
||||
const client = useClient();
|
||||
const toast = useToast();
|
||||
const [adminSecret, setAdminSecret] = React.useState<
|
||||
@@ -91,6 +46,10 @@ export default function Environment() {
|
||||
GITHUB_CLIENT_SECRET: '',
|
||||
FACEBOOK_CLIENT_ID: '',
|
||||
FACEBOOK_CLIENT_SECRET: '',
|
||||
LINKEDIN_CLIENT_ID: '',
|
||||
LINKEDIN_CLIENT_SECRET: '',
|
||||
APPLE_CLIENT_ID: '',
|
||||
APPLE_CLIENT_SECRET: '',
|
||||
ROLES: [],
|
||||
DEFAULT_ROLES: [],
|
||||
PROTECTED_ROLES: [],
|
||||
@@ -114,10 +73,12 @@ export default function Environment() {
|
||||
DISABLE_MAGIC_LINK_LOGIN: false,
|
||||
DISABLE_EMAIL_VERIFICATION: false,
|
||||
DISABLE_BASIC_AUTHENTICATION: false,
|
||||
DISABLE_SIGN_UP: false,
|
||||
OLD_ADMIN_SECRET: '',
|
||||
DATABASE_NAME: '',
|
||||
DATABASE_TYPE: '',
|
||||
DATABASE_URL: '',
|
||||
ACCESS_TOKEN_EXPIRY_TIME: '',
|
||||
});
|
||||
|
||||
const [fieldVisibility, setFieldVisibility] = React.useState<
|
||||
@@ -126,39 +87,36 @@ export default function Environment() {
|
||||
GOOGLE_CLIENT_SECRET: false,
|
||||
GITHUB_CLIENT_SECRET: false,
|
||||
FACEBOOK_CLIENT_SECRET: false,
|
||||
LINKEDIN_CLIENT_SECRET: false,
|
||||
APPLE_CLIENT_SECRET: false,
|
||||
JWT_SECRET: false,
|
||||
SMTP_PASSWORD: false,
|
||||
ADMIN_SECRET: false,
|
||||
OLD_ADMIN_SECRET: false,
|
||||
});
|
||||
|
||||
const { sec } = useParams();
|
||||
|
||||
async function getData() {
|
||||
const {
|
||||
data: { _env: envData },
|
||||
} = await client.query(EnvVariablesQuery).toPromise();
|
||||
setLoading(false);
|
||||
|
||||
setEnvVariables({
|
||||
...envData,
|
||||
OLD_ADMIN_SECRET: envData.ADMIN_SECRET,
|
||||
ADMIN_SECRET: '',
|
||||
});
|
||||
setAdminSecret({
|
||||
value: '',
|
||||
disableInputField: true,
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
}, []);
|
||||
}, [sec]);
|
||||
|
||||
const validateAdminSecretHandler = (event: any) => {
|
||||
if (envVariables.OLD_ADMIN_SECRET === event.target.value) {
|
||||
@@ -228,6 +186,8 @@ export default function Environment() {
|
||||
disableInputField: true,
|
||||
});
|
||||
|
||||
getData();
|
||||
|
||||
toast({
|
||||
title: `Successfully updated ${
|
||||
Object.keys(updatedEnvVariables).length
|
||||
@@ -238,594 +198,110 @@ export default function Environment() {
|
||||
});
|
||||
};
|
||||
|
||||
const renderComponent = (tab: any) => {
|
||||
switch (tab) {
|
||||
case envSubViews.INSTANCE_INFO:
|
||||
return (
|
||||
<OAuthConfig
|
||||
envVariables={envVariables}
|
||||
setVariables={setEnvVariables}
|
||||
fieldVisibility={fieldVisibility}
|
||||
setFieldVisibility={setFieldVisibility}
|
||||
/>
|
||||
);
|
||||
case envSubViews.ROLES:
|
||||
return (
|
||||
<Roles variables={envVariables} setVariables={setEnvVariables} />
|
||||
);
|
||||
case envSubViews.JWT_CONFIG:
|
||||
return (
|
||||
<JWTConfigurations
|
||||
variables={envVariables}
|
||||
setVariables={setEnvVariables}
|
||||
fieldVisibility={fieldVisibility}
|
||||
setFieldVisibility={setFieldVisibility}
|
||||
SelectInputType={SelectInputType.JWT_TYPE}
|
||||
HMACEncryptionType={HMACEncryptionType}
|
||||
RSAEncryptionType={RSAEncryptionType}
|
||||
ECDSAEncryptionType={ECDSAEncryptionType}
|
||||
getData={getData}
|
||||
/>
|
||||
);
|
||||
case envSubViews.SESSION_STORAGE:
|
||||
return (
|
||||
<SessionStorage
|
||||
variables={envVariables}
|
||||
setVariables={setEnvVariables}
|
||||
RedisURL={TextInputType.REDIS_URL}
|
||||
/>
|
||||
);
|
||||
case envSubViews.EMAIL_CONFIG:
|
||||
return (
|
||||
<EmailConfigurations
|
||||
variables={envVariables}
|
||||
setVariables={setEnvVariables}
|
||||
fieldVisibility={fieldVisibility}
|
||||
setFieldVisibility={setFieldVisibility}
|
||||
/>
|
||||
);
|
||||
case envSubViews.WHITELIST_VARIABLES:
|
||||
return (
|
||||
<DomainWhiteListing
|
||||
variables={envVariables}
|
||||
setVariables={setEnvVariables}
|
||||
/>
|
||||
);
|
||||
case envSubViews.ORGANIZATION_INFO:
|
||||
return (
|
||||
<OrganizationInfo
|
||||
variables={envVariables}
|
||||
setVariables={setEnvVariables}
|
||||
/>
|
||||
);
|
||||
case envSubViews.ACCESS_TOKEN:
|
||||
return (
|
||||
<AccessToken
|
||||
variables={envVariables}
|
||||
setVariables={setEnvVariables}
|
||||
/>
|
||||
);
|
||||
case envSubViews.FEATURES:
|
||||
return (
|
||||
<Features variables={envVariables} setVariables={setEnvVariables} />
|
||||
);
|
||||
case envSubViews.ADMIN_SECRET:
|
||||
return (
|
||||
<SecurityAdminSecret
|
||||
variables={envVariables}
|
||||
setVariables={setEnvVariables}
|
||||
fieldVisibility={fieldVisibility}
|
||||
setFieldVisibility={setFieldVisibility}
|
||||
validateAdminSecretHandler={validateAdminSecretHandler}
|
||||
adminSecret={adminSecret}
|
||||
/>
|
||||
);
|
||||
case envSubViews.DB_CRED:
|
||||
return (
|
||||
<DatabaseCredentials
|
||||
variables={envVariables}
|
||||
setVariables={setEnvVariables}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<OAuthConfig
|
||||
envVariables={envVariables}
|
||||
setVariables={setEnvVariables}
|
||||
fieldVisibility={fieldVisibility}
|
||||
setFieldVisibility={setFieldVisibility}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<Box m="5" py="5" px="10" bg="white" rounded="md">
|
||||
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
|
||||
Your instance information
|
||||
</Text>
|
||||
<Stack spacing={6} padding="2% 0%">
|
||||
<Flex>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Client ID</Text>
|
||||
</Flex>
|
||||
<Center w="70%">
|
||||
<InputField
|
||||
variables={envVariables}
|
||||
setVariables={() => {}}
|
||||
inputType={TextInputType.CLIENT_ID}
|
||||
placeholder="Client ID"
|
||||
isDisabled={true}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Client Secret</Text>
|
||||
</Flex>
|
||||
<Center w="70%">
|
||||
<InputField
|
||||
variables={envVariables}
|
||||
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">
|
||||
{renderComponent(sec)}
|
||||
<Stack spacing={6} padding="1% 0" mt={4}>
|
||||
<Flex justifyContent="end" alignItems="center">
|
||||
<Button
|
||||
leftIcon={<FaSave />}
|
||||
@@ -840,4 +316,6 @@ export default function Environment() {
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default Environment;
|
||||
|
@@ -38,10 +38,11 @@ import {
|
||||
FaExclamationCircle,
|
||||
FaAngleDown,
|
||||
} from 'react-icons/fa';
|
||||
import { UserDetailsQuery } from '../graphql/queries';
|
||||
import { UpdateUser } from '../graphql/mutation';
|
||||
import { EmailVerificationQuery, UserDetailsQuery } from '../graphql/queries';
|
||||
import { EnableAccess, RevokeAccess, UpdateUser } from '../graphql/mutation';
|
||||
import EditUserModal from '../components/EditUserModal';
|
||||
import DeleteUserModal from '../components/DeleteUserModal';
|
||||
import InviteMembersModal from '../components/InviteMembersModal';
|
||||
|
||||
interface paginationPropTypes {
|
||||
limit: number;
|
||||
@@ -66,6 +67,12 @@ interface userDataTypes {
|
||||
signup_methods: string;
|
||||
roles: [string];
|
||||
created_at: number;
|
||||
revoked_timestamp: number;
|
||||
}
|
||||
|
||||
const enum updateAccessActions {
|
||||
REVOKE = 'REVOKE',
|
||||
ENABLE = 'ENABLE',
|
||||
}
|
||||
|
||||
const getMaxPages = (pagination: paginationPropTypes) => {
|
||||
@@ -101,6 +108,8 @@ export default function Users() {
|
||||
});
|
||||
const [userList, setUserList] = React.useState<userDataTypes[]>([]);
|
||||
const [loading, setLoading] = React.useState<boolean>(false);
|
||||
const [disableInviteMembers, setDisableInviteMembers] =
|
||||
React.useState<boolean>(true);
|
||||
const updateUserList = async () => {
|
||||
setLoading(true);
|
||||
const { data } = await client
|
||||
@@ -132,8 +141,18 @@ export default function Users() {
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
const checkEmailVerification = async () => {
|
||||
setLoading(true);
|
||||
const { data } = await client.query(EmailVerificationQuery).toPromise();
|
||||
if (data?._env) {
|
||||
const { DISABLE_EMAIL_VERIFICATION } = data._env;
|
||||
setDisableInviteMembers(DISABLE_EMAIL_VERIFICATION);
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
React.useEffect(() => {
|
||||
updateUserList();
|
||||
checkEmailVerification();
|
||||
}, []);
|
||||
React.useEffect(() => {
|
||||
updateUserList();
|
||||
@@ -171,12 +190,77 @@ export default function Users() {
|
||||
}
|
||||
updateUserList();
|
||||
};
|
||||
|
||||
const updateAccessHandler = async (
|
||||
id: string,
|
||||
action: updateAccessActions
|
||||
) => {
|
||||
switch (action) {
|
||||
case updateAccessActions.ENABLE:
|
||||
const enableAccessRes = await client
|
||||
.mutation(EnableAccess, {
|
||||
param: {
|
||||
user_id: id,
|
||||
},
|
||||
})
|
||||
.toPromise();
|
||||
if (enableAccessRes.error) {
|
||||
toast({
|
||||
title: 'User access enable failed',
|
||||
isClosable: true,
|
||||
status: 'error',
|
||||
position: 'bottom-right',
|
||||
});
|
||||
} else {
|
||||
toast({
|
||||
title: 'User access enabled successfully',
|
||||
isClosable: true,
|
||||
status: 'success',
|
||||
position: 'bottom-right',
|
||||
});
|
||||
}
|
||||
updateUserList();
|
||||
break;
|
||||
case updateAccessActions.REVOKE:
|
||||
const revokeAccessRes = await client
|
||||
.mutation(RevokeAccess, {
|
||||
param: {
|
||||
user_id: id,
|
||||
},
|
||||
})
|
||||
.toPromise();
|
||||
if (revokeAccessRes.error) {
|
||||
toast({
|
||||
title: 'User access revoke failed',
|
||||
isClosable: true,
|
||||
status: 'error',
|
||||
position: 'bottom-right',
|
||||
});
|
||||
} else {
|
||||
toast({
|
||||
title: 'User access revoked successfully',
|
||||
isClosable: true,
|
||||
status: 'success',
|
||||
position: 'bottom-right',
|
||||
});
|
||||
}
|
||||
updateUserList();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
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>
|
||||
<InviteMembersModal
|
||||
disabled={disableInviteMembers}
|
||||
updateUserList={updateUserList}
|
||||
/>
|
||||
</Flex>
|
||||
{!loading ? (
|
||||
userList.length > 0 ? (
|
||||
@@ -188,6 +272,7 @@ export default function Users() {
|
||||
<Th>Signup Methods</Th>
|
||||
<Th>Roles</Th>
|
||||
<Th>Verified</Th>
|
||||
<Th>Access</Th>
|
||||
<Th>Actions</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
@@ -196,7 +281,7 @@ export default function Users() {
|
||||
const { email_verified, created_at, ...rest }: any = user;
|
||||
return (
|
||||
<Tr key={user.id} style={{ fontSize: 14 }}>
|
||||
<Td>{user.email}</Td>
|
||||
<Td maxW="300">{user.email}</Td>
|
||||
<Td>
|
||||
{dayjs(user.created_at * 1000).format('MMM DD, YYYY')}
|
||||
</Td>
|
||||
@@ -211,6 +296,15 @@ export default function Users() {
|
||||
{user.email_verified.toString()}
|
||||
</Tag>
|
||||
</Td>
|
||||
<Td>
|
||||
<Tag
|
||||
size="sm"
|
||||
variant="outline"
|
||||
colorScheme={user.revoked_timestamp ? 'red' : 'green'}
|
||||
>
|
||||
{user.revoked_timestamp ? 'Revoked' : 'Enabled'}
|
||||
</Tag>
|
||||
</Td>
|
||||
<Td>
|
||||
<Menu>
|
||||
<MenuButton as={Button} variant="unstyled" size="sm">
|
||||
@@ -240,6 +334,29 @@ export default function Users() {
|
||||
user={rest}
|
||||
updateUserList={updateUserList}
|
||||
/>
|
||||
{user.revoked_timestamp ? (
|
||||
<MenuItem
|
||||
onClick={() =>
|
||||
updateAccessHandler(
|
||||
user.id,
|
||||
updateAccessActions.ENABLE
|
||||
)
|
||||
}
|
||||
>
|
||||
Enable Access
|
||||
</MenuItem>
|
||||
) : (
|
||||
<MenuItem
|
||||
onClick={() =>
|
||||
updateAccessHandler(
|
||||
user.id,
|
||||
updateAccessActions.REVOKE
|
||||
)
|
||||
}
|
||||
>
|
||||
Revoke Access
|
||||
</MenuItem>
|
||||
)}
|
||||
</MenuList>
|
||||
</Menu>
|
||||
</Td>
|
||||
|
@@ -14,6 +14,7 @@ export const AppRoutes = () => {
|
||||
|
||||
if (isLoggedIn) {
|
||||
return (
|
||||
<div>
|
||||
<Suspense fallback={<></>}>
|
||||
<Routes>
|
||||
<Route
|
||||
@@ -23,13 +24,16 @@ export const AppRoutes = () => {
|
||||
</DashboardLayout>
|
||||
}
|
||||
>
|
||||
<Route path="/" element={<Environment />} />
|
||||
<Route path="users" element={<Users />} />
|
||||
<Route path="environment" element={<Environment />} />
|
||||
<Route path="*" element={<Home />} />
|
||||
<Route path="/" element={<Outlet />}>
|
||||
<Route index element={<Environment />} />
|
||||
<Route path="/:sec" element={<Environment />} />
|
||||
</Route>
|
||||
<Route path="users" element={<Users />} />
|
||||
<Route path="*" element={<Home />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
|
@@ -64,3 +64,25 @@ export const getObjectDiff = (obj1: any, obj2: any) => {
|
||||
|
||||
return diff;
|
||||
};
|
||||
|
||||
export const validateEmail = (email: string) => {
|
||||
if (!email || email === '') return true;
|
||||
return email
|
||||
.toLowerCase()
|
||||
.match(
|
||||
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
|
||||
)
|
||||
? true
|
||||
: false;
|
||||
};
|
||||
|
||||
export const validateURI = (uri: string) => {
|
||||
if (!uri || uri === '') return true;
|
||||
return uri
|
||||
.toLowerCase()
|
||||
.match(
|
||||
/(?:^|\s)((https?:\/\/)?(?:localhost|[\w-]+(?:\.[\w-]+)+)(:\d+)?(\/\S*)?)/
|
||||
)
|
||||
? true
|
||||
: false;
|
||||
};
|
||||
|
39
dashboard/src/utils/parseCSV.ts
Normal file
39
dashboard/src/utils/parseCSV.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import _flatten from 'lodash/flatten';
|
||||
import { validateEmail } from '.';
|
||||
|
||||
interface dataTypes {
|
||||
value: string;
|
||||
isInvalid: boolean;
|
||||
}
|
||||
|
||||
const parseCSV = (file: File, delimiter: string): Promise<dataTypes[]> => {
|
||||
return new Promise((resolve) => {
|
||||
const reader = new FileReader();
|
||||
|
||||
// When the FileReader has loaded the file...
|
||||
reader.onload = (e: any) => {
|
||||
// Split the result to an array of lines
|
||||
const lines = e.target.result.split('\n');
|
||||
// Split the lines themselves by the specified
|
||||
// delimiter, such as a comma
|
||||
let result = lines.map((line: string) => line.split(delimiter));
|
||||
// As the FileReader reads asynchronously,
|
||||
// we can't just return the result; instead,
|
||||
// we're passing it to a callback function
|
||||
result = _flatten(result);
|
||||
resolve(
|
||||
result.map((email: string) => {
|
||||
return {
|
||||
value: email.trim(),
|
||||
isInvalid: !validateEmail(email.trim()),
|
||||
};
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
// Read the file content as a single string
|
||||
reader.readAsText(file);
|
||||
});
|
||||
};
|
||||
|
||||
export default parseCSV;
|
6
package-lock.json
generated
Normal file
6
package-lock.json
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "authorizer",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
14
server/cli/cli.go
Normal file
14
server/cli/cli.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package cli
|
||||
|
||||
var (
|
||||
// ARG_DB_URL is the cli arg variable for the database url
|
||||
ARG_DB_URL *string
|
||||
// ARG_DB_TYPE is the cli arg variable for the database type
|
||||
ARG_DB_TYPE *string
|
||||
// ARG_ENV_FILE is the cli arg variable for the env file
|
||||
ARG_ENV_FILE *string
|
||||
// ARG_LOG_LEVEL is the cli arg variable for the log level
|
||||
ARG_LOG_LEVEL *string
|
||||
// ARG_REDIS_URL is the cli arg variable for the redis url
|
||||
ARG_REDIS_URL *string
|
||||
)
|
8
server/constants/cookie.go
Normal file
8
server/constants/cookie.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
// AppCookieName is the name of the cookie that is used to store the application token
|
||||
AppCookieName = "cookie"
|
||||
// AdminCookieName is the name of the cookie that is used to store the admin token
|
||||
AdminCookieName = "authorizer-admin"
|
||||
)
|
@@ -17,4 +17,10 @@ const (
|
||||
DbTypeYugabyte = "yugabyte"
|
||||
// DbTypeMariaDB is the mariadb database type
|
||||
DbTypeMariaDB = "mariadb"
|
||||
// DbTypeCassandra is the cassandra database type
|
||||
DbTypeCassandraDB = "cassandradb"
|
||||
// DbTypeScyllaDB is the scylla database type
|
||||
DbTypeScyllaDB = "scylladb"
|
||||
// DbTypeCockroachDB is the cockroach database type
|
||||
DbTypeCockroachDB = "cockroachdb"
|
||||
)
|
||||
|
@@ -1,26 +1,20 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
// Envstore identifier
|
||||
// StringStore string store identifier
|
||||
StringStoreIdentifier = "stringStore"
|
||||
// BoolStore bool store identifier
|
||||
BoolStoreIdentifier = "boolStore"
|
||||
// SliceStore slice store identifier
|
||||
SliceStoreIdentifier = "sliceStore"
|
||||
var VERSION = "0.0.1"
|
||||
|
||||
const (
|
||||
// TestEnv is used for testing
|
||||
TestEnv = "test"
|
||||
// EnvKeyEnv key for env variable ENV
|
||||
EnvKeyEnv = "ENV"
|
||||
// EnvKeyEnvPath key for cli arg variable ENV_PATH
|
||||
EnvKeyEnvPath = "ENV_PATH"
|
||||
// EnvKeyVersion key for build arg version
|
||||
EnvKeyVersion = "VERSION"
|
||||
// EnvKeyAuthorizerURL key for env variable AUTHORIZER_URL
|
||||
// TODO: remove support AUTHORIZER_URL env
|
||||
EnvKeyAuthorizerURL = "AUTHORIZER_URL"
|
||||
// EnvKeyPort key for env variable PORT
|
||||
EnvKeyPort = "PORT"
|
||||
|
||||
// EnvKeyAccessTokenExpiryTime key for env variable ACCESS_TOKEN_EXPIRY_TIME
|
||||
EnvKeyAccessTokenExpiryTime = "ACCESS_TOKEN_EXPIRY_TIME"
|
||||
// EnvKeyAdminSecret key for env variable ADMIN_SECRET
|
||||
EnvKeyAdminSecret = "ADMIN_SECRET"
|
||||
// EnvKeyDatabaseType key for env variable DATABASE_TYPE
|
||||
@@ -29,6 +23,20 @@ const (
|
||||
EnvKeyDatabaseURL = "DATABASE_URL"
|
||||
// EnvKeyDatabaseName key for env variable DATABASE_NAME
|
||||
EnvKeyDatabaseName = "DATABASE_NAME"
|
||||
// EnvKeyDatabaseUsername key for env variable DATABASE_USERNAME
|
||||
EnvKeyDatabaseUsername = "DATABASE_USERNAME"
|
||||
// EnvKeyDatabasePassword key for env variable DATABASE_PASSWORD
|
||||
EnvKeyDatabasePassword = "DATABASE_PASSWORD"
|
||||
// EnvKeyDatabasePort key for env variable DATABASE_PORT
|
||||
EnvKeyDatabasePort = "DATABASE_PORT"
|
||||
// EnvKeyDatabaseHost key for env variable DATABASE_HOST
|
||||
EnvKeyDatabaseHost = "DATABASE_HOST"
|
||||
// EnvKeyDatabaseCert key for env variable DATABASE_CERT
|
||||
EnvKeyDatabaseCert = "DATABASE_CERT"
|
||||
// EnvKeyDatabaseCertKey key for env variable DATABASE_KEY
|
||||
EnvKeyDatabaseCertKey = "DATABASE_CERT_KEY"
|
||||
// EnvKeyDatabaseCACert key for env variable DATABASE_CA_CERT
|
||||
EnvKeyDatabaseCACert = "DATABASE_CA_CERT"
|
||||
// EnvKeySmtpHost key for env variable SMTP_HOST
|
||||
EnvKeySmtpHost = "SMTP_HOST"
|
||||
// EnvKeySmtpPort key for env variable SMTP_PORT
|
||||
@@ -47,32 +55,12 @@ const (
|
||||
EnvKeyJwtPrivateKey = "JWT_PRIVATE_KEY"
|
||||
// EnvKeyJwtPublicKey key for env variable JWT_PUBLIC_KEY
|
||||
EnvKeyJwtPublicKey = "JWT_PUBLIC_KEY"
|
||||
// EnvKeyAllowedOrigins key for env variable ALLOWED_ORIGINS
|
||||
EnvKeyAllowedOrigins = "ALLOWED_ORIGINS"
|
||||
// EnvKeyAppURL key for env variable APP_URL
|
||||
EnvKeyAppURL = "APP_URL"
|
||||
// EnvKeyRedisURL key for env variable REDIS_URL
|
||||
EnvKeyRedisURL = "REDIS_URL"
|
||||
// EnvKeyCookieName key for env variable COOKIE_NAME
|
||||
EnvKeyCookieName = "COOKIE_NAME"
|
||||
// EnvKeyAdminCookieName key for env variable ADMIN_COOKIE_NAME
|
||||
EnvKeyAdminCookieName = "ADMIN_COOKIE_NAME"
|
||||
// EnvKeyResetPasswordURL key for env variable RESET_PASSWORD_URL
|
||||
EnvKeyResetPasswordURL = "RESET_PASSWORD_URL"
|
||||
// EnvKeyDisableEmailVerification key for env variable DISABLE_EMAIL_VERIFICATION
|
||||
EnvKeyDisableEmailVerification = "DISABLE_EMAIL_VERIFICATION"
|
||||
// EnvKeyDisableBasicAuthentication key for env variable DISABLE_BASIC_AUTH
|
||||
EnvKeyDisableBasicAuthentication = "DISABLE_BASIC_AUTHENTICATION"
|
||||
// EnvKeyDisableMagicLinkLogin key for env variable DISABLE_MAGIC_LINK_LOGIN
|
||||
EnvKeyDisableMagicLinkLogin = "DISABLE_MAGIC_LINK_LOGIN"
|
||||
// EnvKeyDisableLoginPage key for env variable DISABLE_LOGIN_PAGE
|
||||
EnvKeyDisableLoginPage = "DISABLE_LOGIN_PAGE"
|
||||
// EnvKeyRoles key for env variable ROLES
|
||||
EnvKeyRoles = "ROLES"
|
||||
// EnvKeyProtectedRoles key for env variable PROTECTED_ROLES
|
||||
EnvKeyProtectedRoles = "PROTECTED_ROLES"
|
||||
// EnvKeyDefaultRoles key for env variable DEFAULT_ROLES
|
||||
EnvKeyDefaultRoles = "DEFAULT_ROLES"
|
||||
// EnvKeyJwtRoleClaim key for env variable JWT_ROLE_CLAIM
|
||||
EnvKeyJwtRoleClaim = "JWT_ROLE_CLAIM"
|
||||
// EnvKeyGoogleClientID key for env variable GOOGLE_CLIENT_ID
|
||||
@@ -87,6 +75,14 @@ const (
|
||||
EnvKeyFacebookClientID = "FACEBOOK_CLIENT_ID"
|
||||
// EnvKeyFacebookClientSecret key for env variable FACEBOOK_CLIENT_SECRET
|
||||
EnvKeyFacebookClientSecret = "FACEBOOK_CLIENT_SECRET"
|
||||
// EnvKeyLinkedinClientID key for env variable LINKEDIN_CLIENT_ID
|
||||
EnvKeyLinkedInClientID = "LINKEDIN_CLIENT_ID"
|
||||
// EnvKeyLinkedinClientSecret key for env variable LINKEDIN_CLIENT_SECRET
|
||||
EnvKeyLinkedInClientSecret = "LINKEDIN_CLIENT_SECRET"
|
||||
// EnvKeyAppleClientID key for env variable APPLE_CLIENT_ID
|
||||
EnvKeyAppleClientID = "APPLE_CLIENT_ID"
|
||||
// EnvKeyAppleClientSecret key for env variable APPLE_CLIENT_SECRET
|
||||
EnvKeyAppleClientSecret = "APPLE_CLIENT_SECRET"
|
||||
// EnvKeyOrganizationName key for env variable ORGANIZATION_NAME
|
||||
EnvKeyOrganizationName = "ORGANIZATION_NAME"
|
||||
// EnvKeyOrganizationLogo key for env variable ORGANIZATION_LOGO
|
||||
@@ -103,6 +99,30 @@ const (
|
||||
EnvKeyEncryptionKey = "ENCRYPTION_KEY"
|
||||
// EnvKeyJWK key for env variable JWK
|
||||
EnvKeyJWK = "JWK"
|
||||
|
||||
// Boolean variables
|
||||
// EnvKeyIsProd key for env variable IS_PROD
|
||||
EnvKeyIsProd = "IS_PROD"
|
||||
// EnvKeyDisableEmailVerification key for env variable DISABLE_EMAIL_VERIFICATION
|
||||
EnvKeyDisableEmailVerification = "DISABLE_EMAIL_VERIFICATION"
|
||||
// EnvKeyDisableBasicAuthentication key for env variable DISABLE_BASIC_AUTH
|
||||
EnvKeyDisableBasicAuthentication = "DISABLE_BASIC_AUTHENTICATION"
|
||||
// EnvKeyDisableMagicLinkLogin key for env variable DISABLE_MAGIC_LINK_LOGIN
|
||||
EnvKeyDisableMagicLinkLogin = "DISABLE_MAGIC_LINK_LOGIN"
|
||||
// EnvKeyDisableLoginPage key for env variable DISABLE_LOGIN_PAGE
|
||||
EnvKeyDisableLoginPage = "DISABLE_LOGIN_PAGE"
|
||||
// EnvKeyDisableSignUp key for env variable DISABLE_SIGN_UP
|
||||
EnvKeyDisableSignUp = "DISABLE_SIGN_UP"
|
||||
// EnvKeyDisableRedisForEnv key for env variable DISABLE_REDIS_FOR_ENV
|
||||
EnvKeyDisableRedisForEnv = "DISABLE_REDIS_FOR_ENV"
|
||||
|
||||
// Slice variables
|
||||
// EnvKeyRoles key for env variable ROLES
|
||||
EnvKeyRoles = "ROLES"
|
||||
// EnvKeyProtectedRoles key for env variable PROTECTED_ROLES
|
||||
EnvKeyProtectedRoles = "PROTECTED_ROLES"
|
||||
// EnvKeyDefaultRoles key for env variable DEFAULT_ROLES
|
||||
EnvKeyDefaultRoles = "DEFAULT_ROLES"
|
||||
// EnvKeyAllowedOrigins key for env variable ALLOWED_ORIGINS
|
||||
EnvKeyAllowedOrigins = "ALLOWED_ORIGINS"
|
||||
)
|
||||
|
@@ -8,4 +8,7 @@ const (
|
||||
FacebookUserInfoURL = "https://graph.facebook.com/me?fields=id,first_name,last_name,name,email,picture&access_token="
|
||||
// Ref: https://docs.github.com/en/developers/apps/building-github-apps/identifying-and-authorizing-users-for-github-apps#3-your-github-app-accesses-the-api-with-the-users-access-token
|
||||
GithubUserInfoURL = "https://api.github.com/user"
|
||||
// Ref: https://docs.microsoft.com/en-us/linkedin/shared/integrations/people/profile-api
|
||||
LinkedInUserInfoURL = "https://api.linkedin.com/v2/me?projection=(id,localizedFirstName,localizedLastName,emailAddress,profilePicture(displayImage~:playableStreams))"
|
||||
LinkedInEmailURL = "https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))"
|
||||
)
|
||||
|
@@ -11,4 +11,8 @@ const (
|
||||
SignupMethodGithub = "github"
|
||||
// SignupMethodFacebook is the facebook signup method
|
||||
SignupMethodFacebook = "facebook"
|
||||
// SignupMethodLinkedin is the linkedin signup method
|
||||
SignupMethodLinkedIn = "linkedin"
|
||||
// SignupMethodApple is the apple signup method
|
||||
SignupMethodApple = "apple"
|
||||
)
|
||||
|
@@ -7,4 +7,6 @@ const (
|
||||
TokenTypeAccessToken = "access_token"
|
||||
// TokenTypeIdentityToken is the identity_token token type
|
||||
TokenTypeIdentityToken = "id_token"
|
||||
// TokenTypeSessionToken is the session_token type used for browser session
|
||||
TokenTypeSessionToken = "session_token"
|
||||
)
|
||||
|
@@ -4,8 +4,7 @@ import (
|
||||
"net/url"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
"github.com/authorizerdev/authorizer/server/parsers"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -13,15 +12,14 @@ import (
|
||||
func SetAdminCookie(gc *gin.Context, token string) {
|
||||
secure := true
|
||||
httpOnly := true
|
||||
hostname := utils.GetHost(gc)
|
||||
host, _ := utils.GetHostParts(hostname)
|
||||
|
||||
gc.SetCookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminCookieName), token, 3600, "/", host, secure, httpOnly)
|
||||
hostname := parsers.GetHost(gc)
|
||||
host, _ := parsers.GetHostParts(hostname)
|
||||
gc.SetCookie(constants.AdminCookieName, token, 3600, "/", host, secure, httpOnly)
|
||||
}
|
||||
|
||||
// GetAdminCookie gets the admin cookie from the request
|
||||
func GetAdminCookie(gc *gin.Context) (string, error) {
|
||||
cookie, err := gc.Request.Cookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminCookieName))
|
||||
cookie, err := gc.Request.Cookie(constants.AdminCookieName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -39,8 +37,7 @@ func GetAdminCookie(gc *gin.Context) (string, error) {
|
||||
func DeleteAdminCookie(gc *gin.Context) {
|
||||
secure := true
|
||||
httpOnly := true
|
||||
hostname := utils.GetHost(gc)
|
||||
host, _ := utils.GetHostParts(hostname)
|
||||
|
||||
gc.SetCookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminCookieName), "", -1, "/", host, secure, httpOnly)
|
||||
hostname := parsers.GetHost(gc)
|
||||
host, _ := parsers.GetHostParts(hostname)
|
||||
gc.SetCookie(constants.AdminCookieName, "", -1, "/", host, secure, httpOnly)
|
||||
}
|
||||
|
@@ -5,8 +5,7 @@ import (
|
||||
"net/url"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
"github.com/authorizerdev/authorizer/server/parsers"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -14,9 +13,9 @@ import (
|
||||
func SetSession(gc *gin.Context, sessionID string) {
|
||||
secure := true
|
||||
httpOnly := true
|
||||
hostname := utils.GetHost(gc)
|
||||
host, _ := utils.GetHostParts(hostname)
|
||||
domain := utils.GetDomainName(hostname)
|
||||
hostname := parsers.GetHost(gc)
|
||||
host, _ := parsers.GetHostParts(hostname)
|
||||
domain := parsers.GetDomainName(hostname)
|
||||
if domain != "localhost" {
|
||||
domain = "." + domain
|
||||
}
|
||||
@@ -25,33 +24,33 @@ func SetSession(gc *gin.Context, sessionID string) {
|
||||
year := 60 * 60 * 24 * 365
|
||||
|
||||
gc.SetSameSite(http.SameSiteNoneMode)
|
||||
gc.SetCookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+"_session", sessionID, year, "/", host, secure, httpOnly)
|
||||
gc.SetCookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+"_session_domain", sessionID, year, "/", domain, secure, httpOnly)
|
||||
gc.SetCookie(constants.AppCookieName+"_session", sessionID, year, "/", host, secure, httpOnly)
|
||||
gc.SetCookie(constants.AppCookieName+"_session_domain", sessionID, year, "/", domain, secure, httpOnly)
|
||||
}
|
||||
|
||||
// DeleteSession sets session cookies to expire
|
||||
func DeleteSession(gc *gin.Context) {
|
||||
secure := true
|
||||
httpOnly := true
|
||||
hostname := utils.GetHost(gc)
|
||||
host, _ := utils.GetHostParts(hostname)
|
||||
domain := utils.GetDomainName(hostname)
|
||||
hostname := parsers.GetHost(gc)
|
||||
host, _ := parsers.GetHostParts(hostname)
|
||||
domain := parsers.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)
|
||||
gc.SetCookie(constants.AppCookieName+"_session", "", -1, "/", host, secure, httpOnly)
|
||||
gc.SetCookie(constants.AppCookieName+"_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")
|
||||
cookie, err = gc.Request.Cookie(constants.AppCookieName + "_session")
|
||||
if err != nil {
|
||||
cookie, err = gc.Request.Cookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName) + "_session_domain")
|
||||
cookie, err = gc.Request.Cookie(constants.AppCookieName + "_session_domain")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@@ -7,14 +7,18 @@ import (
|
||||
"io"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
)
|
||||
|
||||
var bytes = []byte{35, 46, 57, 24, 85, 35, 24, 74, 87, 35, 88, 98, 66, 32, 14, 0o5}
|
||||
|
||||
// EncryptAES method is to encrypt or hide any classified text
|
||||
func EncryptAES(text string) (string, error) {
|
||||
key := []byte(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyEncryptionKey))
|
||||
k, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyEncryptionKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
key := []byte(k)
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -28,7 +32,11 @@ func EncryptAES(text string) (string, error) {
|
||||
|
||||
// DecryptAES method is to extract back the encrypted text
|
||||
func DecryptAES(text string) (string, error) {
|
||||
key := []byte(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyEncryptionKey))
|
||||
k, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyEncryptionKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
key := []byte(k)
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -46,9 +54,13 @@ func DecryptAES(text string) (string, error) {
|
||||
// EncryptAESEnv encrypts data using AES algorithm
|
||||
// kept for the backward compatibility of env data encryption
|
||||
func EncryptAESEnv(text []byte) ([]byte, error) {
|
||||
key := []byte(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyEncryptionKey))
|
||||
c, err := aes.NewCipher(key)
|
||||
var res []byte
|
||||
k, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyEncryptionKey)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
key := []byte(k)
|
||||
c, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
@@ -81,9 +93,13 @@ func EncryptAESEnv(text []byte) ([]byte, error) {
|
||||
// DecryptAES decrypts data using AES algorithm
|
||||
// Kept for the backward compatibility of env data decryption
|
||||
func DecryptAESEnv(ciphertext []byte) ([]byte, error) {
|
||||
key := []byte(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyEncryptionKey))
|
||||
c, err := aes.NewCipher(key)
|
||||
var res []byte
|
||||
k, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyEncryptionKey)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
key := []byte(k)
|
||||
c, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
@@ -5,7 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
)
|
||||
@@ -37,20 +37,35 @@ func GetPubJWK(algo, keyID string, publicKey interface{}) (string, error) {
|
||||
// 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)
|
||||
algo, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyJwtType)
|
||||
if err != nil {
|
||||
return jwk, err
|
||||
}
|
||||
clientID, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyClientID)
|
||||
if err != nil {
|
||||
return jwk, err
|
||||
}
|
||||
|
||||
jwtSecret, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyJwtSecret)
|
||||
if err != nil {
|
||||
return jwk, err
|
||||
}
|
||||
|
||||
var err error
|
||||
// check if jwt secret is provided
|
||||
if IsHMACA(algo) {
|
||||
jwk, err = GetPubJWK(algo, clientID, []byte(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtSecret)))
|
||||
jwk, err = GetPubJWK(algo, clientID, []byte(jwtSecret))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
jwtPublicKey, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyJwtPublicKey)
|
||||
if err != nil {
|
||||
return jwk, err
|
||||
}
|
||||
|
||||
if IsRSA(algo) {
|
||||
publicKeyInstance, err := ParseRsaPublicKeyFromPemStr(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtPublicKey))
|
||||
publicKeyInstance, err := ParseRsaPublicKeyFromPemStr(jwtPublicKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -62,7 +77,11 @@ func GenerateJWKBasedOnEnv() (string, error) {
|
||||
}
|
||||
|
||||
if IsECDSA(algo) {
|
||||
publicKeyInstance, err := ParseEcdsaPublicKeyFromPemStr(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtPublicKey))
|
||||
jwtPublicKey, err = memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyJwtPublicKey)
|
||||
if err != nil {
|
||||
return jwk, err
|
||||
}
|
||||
publicKeyInstance, err := ParseEcdsaPublicKeyFromPemStr(jwtPublicKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -77,13 +96,16 @@ func GenerateJWKBasedOnEnv() (string, error) {
|
||||
}
|
||||
|
||||
// EncryptEnvData is used to encrypt the env data
|
||||
func EncryptEnvData(data envstore.Store) (string, error) {
|
||||
func EncryptEnvData(data map[string]interface{}) (string, error) {
|
||||
jsonBytes, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
storeData := envstore.EnvStoreObj.GetEnvStoreClone()
|
||||
storeData, err := memorystore.Provider.GetEnvStore()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(jsonBytes, &storeData)
|
||||
if err != nil {
|
||||
|
@@ -1,12 +1,15 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db/providers"
|
||||
"github.com/authorizerdev/authorizer/server/db/providers/arangodb"
|
||||
"github.com/authorizerdev/authorizer/server/db/providers/cassandradb"
|
||||
"github.com/authorizerdev/authorizer/server/db/providers/mongodb"
|
||||
"github.com/authorizerdev/authorizer/server/db/providers/sql"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
)
|
||||
|
||||
// Provider returns the current database provider
|
||||
@@ -15,27 +18,45 @@ var Provider providers.Provider
|
||||
func InitDB() error {
|
||||
var err error
|
||||
|
||||
isSQL := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) != constants.DbTypeArangodb && envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) != constants.DbTypeMongodb
|
||||
isArangoDB := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) == constants.DbTypeArangodb
|
||||
isMongoDB := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) == constants.DbTypeMongodb
|
||||
envs := memorystore.RequiredEnvStoreObj.GetRequiredEnv()
|
||||
|
||||
isSQL := envs.DatabaseType != constants.DbTypeArangodb && envs.DatabaseType != constants.DbTypeMongodb && envs.DatabaseType != constants.DbTypeCassandraDB && envs.DatabaseType != constants.DbTypeScyllaDB
|
||||
isArangoDB := envs.DatabaseType == constants.DbTypeArangodb
|
||||
isMongoDB := envs.DatabaseType == constants.DbTypeMongodb
|
||||
isCassandra := envs.DatabaseType == constants.DbTypeCassandraDB || envs.DatabaseType == constants.DbTypeScyllaDB
|
||||
|
||||
if isSQL {
|
||||
log.Info("Initializing SQL Driver for: ", envs.DatabaseType)
|
||||
Provider, err = sql.NewProvider()
|
||||
if err != nil {
|
||||
log.Fatal("Failed to initialize SQL driver: ", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if isArangoDB {
|
||||
log.Info("Initializing ArangoDB Driver")
|
||||
Provider, err = arangodb.NewProvider()
|
||||
if err != nil {
|
||||
log.Fatal("Failed to initialize ArangoDB driver: ", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if isMongoDB {
|
||||
log.Info("Initializing MongoDB Driver")
|
||||
Provider, err = mongodb.NewProvider()
|
||||
if err != nil {
|
||||
log.Fatal("Failed to initialize MongoDB driver: ", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if isCassandra {
|
||||
log.Info("Initializing CassandraDB Driver")
|
||||
Provider, err = cassandradb.NewProvider()
|
||||
if err != nil {
|
||||
log.Fatal("Failed to initialize CassandraDB driver: ", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,13 @@
|
||||
package models
|
||||
|
||||
// Note: any change here should be reflected in providers/casandra/provider.go as it does not have model support in collection creation
|
||||
|
||||
// Env model for db
|
||||
type Env struct {
|
||||
Key string `json:"_key,omitempty" bson:"_key"` // for arangodb
|
||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id"`
|
||||
EnvData string `gorm:"type:text" json:"env" bson:"env"`
|
||||
Hash string `gorm:"type:text" json:"hash" bson:"hash"`
|
||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at"`
|
||||
CreatedAt int64 `json:"created_at" bson:"created_at"`
|
||||
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty"` // for arangodb
|
||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
|
||||
EnvData string `gorm:"type:text" json:"env" bson:"env" cql:"env"`
|
||||
Hash string `gorm:"type:text" json:"hash" bson:"hash" cql:"hash"`
|
||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
|
||||
}
|
||||
|
@@ -1,13 +1,15 @@
|
||||
package models
|
||||
|
||||
// Note: any change here should be reflected in providers/casandra/provider.go as it does not have model support in collection creation
|
||||
|
||||
// Session model for db
|
||||
type Session struct {
|
||||
Key string `json:"_key,omitempty" bson:"_key,omitempty"` // for arangodb
|
||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id"`
|
||||
UserID string `gorm:"type:char(36),index:" json:"user_id" bson:"user_id"`
|
||||
User User `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" bson:"-"`
|
||||
UserAgent string `json:"user_agent" bson:"user_agent"`
|
||||
IP string `json:"ip" bson:"ip"`
|
||||
CreatedAt int64 `json:"created_at" bson:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at"`
|
||||
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty"` // for arangodb
|
||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
|
||||
UserID string `gorm:"type:char(36),index:" json:"user_id" bson:"user_id" cql:"user_id"`
|
||||
User User `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" bson:"-" cql:"-"`
|
||||
UserAgent string `json:"user_agent" bson:"user_agent" cql:"user_agent"`
|
||||
IP string `json:"ip" bson:"ip" cql:"ip"`
|
||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
||||
}
|
||||
|
@@ -6,32 +6,38 @@ import (
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
)
|
||||
|
||||
// Note: any change here should be reflected in providers/casandra/provider.go as it does not have model support in collection creation
|
||||
|
||||
// User model for db
|
||||
type User struct {
|
||||
Key string `json:"_key,omitempty" bson:"_key"` // for arangodb
|
||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id"`
|
||||
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty"` // for arangodb
|
||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
|
||||
|
||||
Email string `gorm:"unique" json:"email" bson:"email"`
|
||||
EmailVerifiedAt *int64 `json:"email_verified_at" bson:"email_verified_at"`
|
||||
Password *string `gorm:"type:text" json:"password" bson:"password"`
|
||||
SignupMethods string `json:"signup_methods" bson:"signup_methods"`
|
||||
GivenName *string `json:"given_name" bson:"given_name"`
|
||||
FamilyName *string `json:"family_name" bson:"family_name"`
|
||||
MiddleName *string `json:"middle_name" bson:"middle_name"`
|
||||
Nickname *string `json:"nickname" bson:"nickname"`
|
||||
Gender *string `json:"gender" bson:"gender"`
|
||||
Birthdate *string `json:"birthdate" bson:"birthdate"`
|
||||
PhoneNumber *string `gorm:"unique" json:"phone_number" bson:"phone_number"`
|
||||
PhoneNumberVerifiedAt *int64 `json:"phone_number_verified_at" bson:"phone_number_verified_at"`
|
||||
Picture *string `gorm:"type:text" json:"picture" bson:"picture"`
|
||||
Roles string `json:"roles" bson:"roles"`
|
||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at"`
|
||||
CreatedAt int64 `json:"created_at" bson:"created_at"`
|
||||
Email string `gorm:"unique" json:"email" bson:"email" cql:"email"`
|
||||
EmailVerifiedAt *int64 `json:"email_verified_at" bson:"email_verified_at" cql:"email_verified_at"`
|
||||
Password *string `gorm:"type:text" json:"password" bson:"password" cql:"password"`
|
||||
SignupMethods string `json:"signup_methods" bson:"signup_methods" cql:"signup_methods"`
|
||||
GivenName *string `json:"given_name" bson:"given_name" cql:"given_name"`
|
||||
FamilyName *string `json:"family_name" bson:"family_name" cql:"family_name"`
|
||||
MiddleName *string `json:"middle_name" bson:"middle_name" cql:"middle_name"`
|
||||
Nickname *string `json:"nickname" bson:"nickname" cql:"nickname"`
|
||||
Gender *string `json:"gender" bson:"gender" cql:"gender"`
|
||||
Birthdate *string `json:"birthdate" bson:"birthdate" cql:"birthdate"`
|
||||
PhoneNumber *string `gorm:"unique" json:"phone_number" bson:"phone_number" cql:"phone_number"`
|
||||
PhoneNumberVerifiedAt *int64 `json:"phone_number_verified_at" bson:"phone_number_verified_at" cql:"phone_number_verified_at"`
|
||||
Picture *string `gorm:"type:text" json:"picture" bson:"picture" cql:"picture"`
|
||||
Roles string `json:"roles" bson:"roles" cql:"roles"`
|
||||
RevokedTimestamp *int64 `json:"revoked_timestamp" bson:"revoked_timestamp" cql:"revoked_timestamp"`
|
||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
|
||||
}
|
||||
|
||||
func (user *User) AsAPIUser() *model.User {
|
||||
isEmailVerified := user.EmailVerifiedAt != nil
|
||||
isPhoneVerified := user.PhoneNumberVerifiedAt != nil
|
||||
email := user.Email
|
||||
createdAt := user.CreatedAt
|
||||
updatedAt := user.UpdatedAt
|
||||
return &model.User{
|
||||
ID: user.ID,
|
||||
Email: user.Email,
|
||||
@@ -41,14 +47,15 @@ func (user *User) AsAPIUser() *model.User {
|
||||
FamilyName: user.FamilyName,
|
||||
MiddleName: user.MiddleName,
|
||||
Nickname: user.Nickname,
|
||||
PreferredUsername: &user.Email,
|
||||
PreferredUsername: &email,
|
||||
Gender: user.Gender,
|
||||
Birthdate: user.Birthdate,
|
||||
PhoneNumber: user.PhoneNumber,
|
||||
PhoneNumberVerified: &isPhoneVerified,
|
||||
Picture: user.Picture,
|
||||
Roles: strings.Split(user.Roles, ","),
|
||||
CreatedAt: &user.CreatedAt,
|
||||
UpdatedAt: &user.UpdatedAt,
|
||||
RevokedTimestamp: user.RevokedTimestamp,
|
||||
CreatedAt: &createdAt,
|
||||
UpdatedAt: &updatedAt,
|
||||
}
|
||||
}
|
||||
|
@@ -2,27 +2,40 @@ package models
|
||||
|
||||
import "github.com/authorizerdev/authorizer/server/graph/model"
|
||||
|
||||
// Note: any change here should be reflected in providers/casandra/provider.go as it does not have model support in collection creation
|
||||
|
||||
// VerificationRequest model for db
|
||||
type VerificationRequest struct {
|
||||
Key string `json:"_key,omitempty" bson:"_key"` // for arangodb
|
||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id"`
|
||||
Token string `gorm:"type:text" json:"token" bson:"token"`
|
||||
Identifier string `gorm:"uniqueIndex:idx_email_identifier" json:"identifier" bson:"identifier"`
|
||||
ExpiresAt int64 `json:"expires_at" bson:"expires_at"`
|
||||
CreatedAt int64 `json:"created_at" bson:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at"`
|
||||
Email string `gorm:"uniqueIndex:idx_email_identifier" json:"email" bson:"email"`
|
||||
Nonce string `gorm:"type:char(36)" json:"nonce" bson:"nonce"`
|
||||
Key string `json:"_key,omitempty" bson:"_key" cql:"_key,omitempty"` // for arangodb
|
||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
|
||||
Token string `gorm:"type:text" json:"token" bson:"token" cql:"jwt_token"` // token is reserved keyword in cassandra
|
||||
Identifier string `gorm:"uniqueIndex:idx_email_identifier;type:varchar(64)" json:"identifier" bson:"identifier" cql:"identifier"`
|
||||
ExpiresAt int64 `json:"expires_at" bson:"expires_at" cql:"expires_at"`
|
||||
Email string `gorm:"uniqueIndex:idx_email_identifier;type:varchar(256)" json:"email" bson:"email" cql:"email"`
|
||||
Nonce string `gorm:"type:text" json:"nonce" bson:"nonce" cql:"nonce"`
|
||||
RedirectURI string `gorm:"type:text" json:"redirect_uri" bson:"redirect_uri" cql:"redirect_uri"`
|
||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
||||
}
|
||||
|
||||
func (v *VerificationRequest) AsAPIVerificationRequest() *model.VerificationRequest {
|
||||
token := v.Token
|
||||
createdAt := v.CreatedAt
|
||||
updatedAt := v.UpdatedAt
|
||||
email := v.Email
|
||||
nonce := v.Nonce
|
||||
redirectURI := v.RedirectURI
|
||||
expires := v.ExpiresAt
|
||||
identifier := v.Identifier
|
||||
return &model.VerificationRequest{
|
||||
ID: v.ID,
|
||||
Token: &v.Token,
|
||||
Identifier: &v.Identifier,
|
||||
Expires: &v.ExpiresAt,
|
||||
CreatedAt: &v.CreatedAt,
|
||||
UpdatedAt: &v.UpdatedAt,
|
||||
Email: &v.Email,
|
||||
ID: v.ID,
|
||||
Token: &token,
|
||||
Identifier: &identifier,
|
||||
Expires: &expires,
|
||||
Email: &email,
|
||||
Nonce: &nonce,
|
||||
RedirectURI: &redirectURI,
|
||||
CreatedAt: &createdAt,
|
||||
UpdatedAt: &updatedAt,
|
||||
}
|
||||
}
|
||||
|
@@ -2,7 +2,6 @@ package arangodb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
arangoDriver "github.com/arangodb/go-driver"
|
||||
@@ -22,7 +21,6 @@ func (p *provider) AddEnv(env models.Env) (models.Env, error) {
|
||||
configCollection, _ := p.db.Collection(nil, models.Collections.Env)
|
||||
meta, err := configCollection.CreateDocument(arangoDriver.WithOverwrite(nil), env)
|
||||
if err != nil {
|
||||
log.Println("error adding config:", err)
|
||||
return env, err
|
||||
}
|
||||
env.Key = meta.Key
|
||||
@@ -36,7 +34,6 @@ func (p *provider) UpdateEnv(env models.Env) (models.Env, error) {
|
||||
collection, _ := p.db.Collection(nil, models.Collections.Env)
|
||||
meta, err := collection.UpdateDocument(nil, env.Key, env)
|
||||
if err != nil {
|
||||
log.Println("error updating config:", err)
|
||||
return env, err
|
||||
}
|
||||
|
||||
|
@@ -2,14 +2,12 @@ package arangodb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
"github.com/arangodb/go-driver"
|
||||
arangoDriver "github.com/arangodb/go-driver"
|
||||
"github.com/arangodb/go-driver/http"
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
)
|
||||
|
||||
type provider struct {
|
||||
@@ -23,8 +21,9 @@ type provider struct {
|
||||
// NewProvider to initialize arangodb connection
|
||||
func NewProvider() (*provider, error) {
|
||||
ctx := context.Background()
|
||||
dbURL := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabaseURL
|
||||
conn, err := http.NewConnection(http.ConnectionConfig{
|
||||
Endpoints: []string{envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseURL)},
|
||||
Endpoints: []string{dbURL},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -38,29 +37,26 @@ func NewProvider() (*provider, error) {
|
||||
}
|
||||
|
||||
var arangodb driver.Database
|
||||
|
||||
arangodb_exists, err := arangoClient.DatabaseExists(nil, envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseName))
|
||||
dbName := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabaseName
|
||||
arangodb_exists, err := arangoClient.DatabaseExists(nil, dbName)
|
||||
|
||||
if arangodb_exists {
|
||||
log.Println(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseName) + " db exists already")
|
||||
arangodb, err = arangoClient.Database(nil, envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseName))
|
||||
arangodb, err = arangoClient.Database(nil, dbName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
arangodb, err = arangoClient.CreateDatabase(nil, envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseName), nil)
|
||||
arangodb, err = arangoClient.CreateDatabase(nil, dbName, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
userCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.User)
|
||||
if userCollectionExists {
|
||||
log.Println(models.Collections.User + " collection exists already")
|
||||
} else {
|
||||
if !userCollectionExists {
|
||||
_, err = arangodb.CreateCollection(ctx, models.Collections.User, nil)
|
||||
if err != nil {
|
||||
log.Println("error creating collection("+models.Collections.User+"):", err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
userCollection, _ := arangodb.Collection(nil, models.Collections.User)
|
||||
@@ -74,12 +70,10 @@ func NewProvider() (*provider, error) {
|
||||
})
|
||||
|
||||
verificationRequestCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.VerificationRequest)
|
||||
if verificationRequestCollectionExists {
|
||||
log.Println(models.Collections.VerificationRequest + " collection exists already")
|
||||
} else {
|
||||
if !verificationRequestCollectionExists {
|
||||
_, err = arangodb.CreateCollection(ctx, models.Collections.VerificationRequest, nil)
|
||||
if err != nil {
|
||||
log.Println("error creating collection("+models.Collections.VerificationRequest+"):", err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,12 +87,10 @@ func NewProvider() (*provider, error) {
|
||||
})
|
||||
|
||||
sessionCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.Session)
|
||||
if sessionCollectionExists {
|
||||
log.Println(models.Collections.Session + " collection exists already")
|
||||
} else {
|
||||
if !sessionCollectionExists {
|
||||
_, err = arangodb.CreateCollection(ctx, models.Collections.Session, nil)
|
||||
if err != nil {
|
||||
log.Println("error creating collection("+models.Collections.Session+"):", err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,12 +100,10 @@ func NewProvider() (*provider, error) {
|
||||
})
|
||||
|
||||
configCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.Env)
|
||||
if configCollectionExists {
|
||||
log.Println(models.Collections.Env + " collection exists already")
|
||||
} else {
|
||||
if !configCollectionExists {
|
||||
_, err = arangodb.CreateCollection(ctx, models.Collections.Env, nil)
|
||||
if err != nil {
|
||||
log.Println("error creating collection("+models.Collections.Env+"):", err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
@@ -2,7 +2,6 @@ package arangodb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
@@ -20,7 +19,6 @@ func (p *provider) AddSession(session models.Session) error {
|
||||
sessionCollection, _ := p.db.Collection(nil, models.Collections.Session)
|
||||
_, err := sessionCollection.CreateDocument(nil, session)
|
||||
if err != nil {
|
||||
log.Println(`error saving session`, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@@ -3,16 +3,14 @@ package arangodb
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/arangodb/go-driver"
|
||||
arangoDriver "github.com/arangodb/go-driver"
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
@@ -23,7 +21,11 @@ func (p *provider) AddUser(user models.User) (models.User, error) {
|
||||
}
|
||||
|
||||
if user.Roles == "" {
|
||||
user.Roles = strings.Join(envstore.EnvStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyDefaultRoles), ",")
|
||||
defaultRoles, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyDefaultRoles)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
user.Roles = defaultRoles
|
||||
}
|
||||
|
||||
user.CreatedAt = time.Now().Unix()
|
||||
@@ -31,7 +33,6 @@ func (p *provider) AddUser(user models.User) (models.User, error) {
|
||||
userCollection, _ := p.db.Collection(nil, models.Collections.User)
|
||||
meta, err := userCollection.CreateDocument(arangoDriver.WithOverwrite(nil), user)
|
||||
if err != nil {
|
||||
log.Println("error adding user:", err)
|
||||
return user, err
|
||||
}
|
||||
user.Key = meta.Key
|
||||
@@ -46,7 +47,6 @@ func (p *provider) UpdateUser(user models.User) (models.User, error) {
|
||||
collection, _ := p.db.Collection(nil, models.Collections.User)
|
||||
meta, err := collection.UpdateDocument(nil, user.Key, user)
|
||||
if err != nil {
|
||||
log.Println("error updating user:", err)
|
||||
return user, err
|
||||
}
|
||||
|
||||
@@ -60,7 +60,6 @@ func (p *provider) DeleteUser(user models.User) error {
|
||||
collection, _ := p.db.Collection(nil, models.Collections.User)
|
||||
_, err := collection.RemoveDocument(nil, user.Key)
|
||||
if err != nil {
|
||||
log.Println(`error deleting user:`, err)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@@ -3,7 +3,6 @@ package arangodb
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/arangodb/go-driver"
|
||||
@@ -23,7 +22,6 @@ func (p *provider) AddVerificationRequest(verificationRequest models.Verificatio
|
||||
verificationRequestRequestCollection, _ := p.db.Collection(nil, models.Collections.VerificationRequest)
|
||||
meta, err := verificationRequestRequestCollection.CreateDocument(nil, verificationRequest)
|
||||
if err != nil {
|
||||
log.Println("error saving verificationRequest record:", err)
|
||||
return verificationRequest, err
|
||||
}
|
||||
verificationRequest.Key = meta.Key
|
||||
@@ -136,7 +134,6 @@ func (p *provider) DeleteVerificationRequest(verificationRequest models.Verifica
|
||||
collection, _ := p.db.Collection(nil, models.Collections.VerificationRequest)
|
||||
_, err := collection.RemoveDocument(nil, verificationRequest.Key)
|
||||
if err != nil {
|
||||
log.Println(`error deleting verification request:`, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
52
server/db/providers/cassandradb/env.go
Normal file
52
server/db/providers/cassandradb/env.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package cassandradb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/gocql/gocql"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// 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.CreatedAt = time.Now().Unix()
|
||||
env.UpdatedAt = time.Now().Unix()
|
||||
insertEnvQuery := fmt.Sprintf("INSERT INTO %s (id, env, hash, created_at, updated_at) VALUES ('%s', '%s', '%s', %d, %d)", KeySpace+"."+models.Collections.Env, env.ID, env.EnvData, env.Hash, env.CreatedAt, env.UpdatedAt)
|
||||
err := p.db.Query(insertEnvQuery).Exec()
|
||||
if err != nil {
|
||||
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()
|
||||
|
||||
updateEnvQuery := fmt.Sprintf("UPDATE %s SET env = '%s', updated_at = %d WHERE id = '%s'", KeySpace+"."+models.Collections.Env, env.EnvData, env.UpdatedAt, env.ID)
|
||||
err := p.db.Query(updateEnvQuery).Exec()
|
||||
if err != nil {
|
||||
return env, err
|
||||
}
|
||||
return env, nil
|
||||
}
|
||||
|
||||
// GetEnv to get environment information from database
|
||||
func (p *provider) GetEnv() (models.Env, error) {
|
||||
var env models.Env
|
||||
|
||||
query := fmt.Sprintf("SELECT id, env, hash, created_at, updated_at FROM %s LIMIT 1", KeySpace+"."+models.Collections.Env)
|
||||
err := p.db.Query(query).Consistency(gocql.One).Scan(&env.ID, &env.EnvData, &env.Hash, &env.CreatedAt, &env.UpdatedAt)
|
||||
if err != nil {
|
||||
return env, err
|
||||
}
|
||||
|
||||
return env, nil
|
||||
}
|
180
server/db/providers/cassandradb/provider.go
Normal file
180
server/db/providers/cassandradb/provider.go
Normal file
@@ -0,0 +1,180 @@
|
||||
package cassandradb
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/crypto"
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"github.com/gocql/gocql"
|
||||
cansandraDriver "github.com/gocql/gocql"
|
||||
)
|
||||
|
||||
type provider struct {
|
||||
db *cansandraDriver.Session
|
||||
}
|
||||
|
||||
// KeySpace for the cassandra database
|
||||
var KeySpace string
|
||||
|
||||
// NewProvider to initialize arangodb connection
|
||||
func NewProvider() (*provider, error) {
|
||||
dbURL := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabaseURL
|
||||
if dbURL == "" {
|
||||
dbHost := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabaseHost
|
||||
dbPort := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabasePort
|
||||
if dbPort != "" && dbHost != "" {
|
||||
dbURL = fmt.Sprintf("%s:%s", dbHost, dbPort)
|
||||
} else if dbHost != "" {
|
||||
dbURL = dbHost
|
||||
}
|
||||
}
|
||||
|
||||
KeySpace = memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabaseName
|
||||
if KeySpace == "" {
|
||||
KeySpace = constants.EnvKeyDatabaseName
|
||||
}
|
||||
clusterURL := []string{}
|
||||
if strings.Contains(dbURL, ",") {
|
||||
clusterURL = strings.Split(dbURL, ",")
|
||||
} else {
|
||||
clusterURL = append(clusterURL, dbURL)
|
||||
}
|
||||
cassandraClient := cansandraDriver.NewCluster(clusterURL...)
|
||||
dbUsername := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabaseUsername
|
||||
dbPassword := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabasePassword
|
||||
|
||||
if dbUsername != "" && dbPassword != "" {
|
||||
cassandraClient.Authenticator = &cansandraDriver.PasswordAuthenticator{
|
||||
Username: dbUsername,
|
||||
Password: dbPassword,
|
||||
}
|
||||
}
|
||||
|
||||
dbCert := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabaseCert
|
||||
dbCACert := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabaseCACert
|
||||
dbCertKey := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabaseCertKey
|
||||
if dbCert != "" && dbCACert != "" && dbCertKey != "" {
|
||||
certString, err := crypto.DecryptB64(dbCert)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keyString, err := crypto.DecryptB64(dbCertKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
caString, err := crypto.DecryptB64(dbCACert)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cert, err := tls.X509KeyPair([]byte(certString), []byte(keyString))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
caCertPool := x509.NewCertPool()
|
||||
caCertPool.AppendCertsFromPEM([]byte(caString))
|
||||
|
||||
cassandraClient.SslOpts = &cansandraDriver.SslOptions{
|
||||
Config: &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
RootCAs: caCertPool,
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
EnableHostVerification: false,
|
||||
}
|
||||
}
|
||||
|
||||
cassandraClient.RetryPolicy = &cansandraDriver.SimpleRetryPolicy{
|
||||
NumRetries: 3,
|
||||
}
|
||||
cassandraClient.Consistency = gocql.LocalQuorum
|
||||
|
||||
session, err := cassandraClient.CreateSession()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Note for astra keyspaces can only be created from there console
|
||||
// https://docs.datastax.com/en/astra/docs/datastax-astra-faq.html#_i_am_trying_to_create_a_keyspace_in_the_cql_shell_and_i_am_running_into_an_error_how_do_i_fix_this
|
||||
getKeyspaceQuery := fmt.Sprintf("SELECT keyspace_name FROM system_schema.keyspaces;")
|
||||
scanner := session.Query(getKeyspaceQuery).Iter().Scanner()
|
||||
hasAuthorizerKeySpace := false
|
||||
for scanner.Next() {
|
||||
var keySpace string
|
||||
err := scanner.Scan(&keySpace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if keySpace == KeySpace {
|
||||
hasAuthorizerKeySpace = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !hasAuthorizerKeySpace {
|
||||
createKeySpaceQuery := fmt.Sprintf("CREATE KEYSPACE %s WITH REPLICATION = {'class': 'SimpleStrategy', 'replication_factor': 1};", KeySpace)
|
||||
err = session.Query(createKeySpaceQuery).Exec()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// make sure collections are present
|
||||
envCollectionQuery := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s.%s (id text, env text, hash text, updated_at bigint, created_at bigint, PRIMARY KEY (id))",
|
||||
KeySpace, models.Collections.Env)
|
||||
err = session.Query(envCollectionQuery).Exec()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sessionCollectionQuery := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s.%s (id text, user_id text, user_agent text, ip text, updated_at bigint, created_at bigint, PRIMARY KEY (id))", KeySpace, models.Collections.Session)
|
||||
err = session.Query(sessionCollectionQuery).Exec()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userCollectionQuery := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s.%s (id text, email text, email_verified_at bigint, password text, signup_methods text, given_name text, family_name text, middle_name text, nickname text, gender text, birthdate text, phone_number text, phone_number_verified_at bigint, picture text, roles text, updated_at bigint, created_at bigint, revoked_timestamp bigint, PRIMARY KEY (id))", KeySpace, models.Collections.User)
|
||||
err = session.Query(userCollectionQuery).Exec()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userIndexQuery := fmt.Sprintf("CREATE INDEX IF NOT EXISTS authorizer_user_email ON %s.%s (email)", KeySpace, models.Collections.User)
|
||||
err = session.Query(userIndexQuery).Exec()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// token is reserved keyword in cassandra, hence we need to use jwt_token
|
||||
verificationRequestCollectionQuery := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s.%s (id text, jwt_token text, identifier text, expires_at bigint, email text, nonce text, redirect_uri text, created_at bigint, updated_at bigint, PRIMARY KEY (id))", KeySpace, models.Collections.VerificationRequest)
|
||||
err = session.Query(verificationRequestCollectionQuery).Exec()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
verificationRequestIndexQuery := fmt.Sprintf("CREATE INDEX IF NOT EXISTS authorizer_verification_request_email ON %s.%s (email)", KeySpace, models.Collections.VerificationRequest)
|
||||
err = session.Query(verificationRequestIndexQuery).Exec()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
verificationRequestIndexQuery = fmt.Sprintf("CREATE INDEX IF NOT EXISTS authorizer_verification_request_identifier ON %s.%s (identifier)", KeySpace, models.Collections.VerificationRequest)
|
||||
err = session.Query(verificationRequestIndexQuery).Exec()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
verificationRequestIndexQuery = fmt.Sprintf("CREATE INDEX IF NOT EXISTS authorizer_verification_request_jwt_token ON %s.%s (jwt_token)", KeySpace, models.Collections.VerificationRequest)
|
||||
err = session.Query(verificationRequestIndexQuery).Exec()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &provider{
|
||||
db: session,
|
||||
}, err
|
||||
}
|
36
server/db/providers/cassandradb/session.go
Normal file
36
server/db/providers/cassandradb/session.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package cassandradb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"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()
|
||||
|
||||
insertSessionQuery := fmt.Sprintf("INSERT INTO %s (id, user_id, user_agent, ip, created_at, updated_at) VALUES ('%s', '%s', '%s', '%s', %d, %d)", KeySpace+"."+models.Collections.Session, session.ID, session.UserID, session.UserAgent, session.IP, session.CreatedAt, session.UpdatedAt)
|
||||
err := p.db.Query(insertSessionQuery).Exec()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteSession to delete session information from database
|
||||
func (p *provider) DeleteSession(userId string) error {
|
||||
deleteSessionQuery := fmt.Sprintf("DELETE FROM %s WHERE user_id = '%s'", KeySpace+"."+models.Collections.Session, userId)
|
||||
err := p.db.Query(deleteSessionQuery).Exec()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
193
server/db/providers/cassandradb/user.go
Normal file
193
server/db/providers/cassandradb/user.go
Normal file
@@ -0,0 +1,193 @@
|
||||
package cassandradb
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"github.com/gocql/gocql"
|
||||
"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 == "" {
|
||||
defaultRoles, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyDefaultRoles)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
user.Roles = defaultRoles
|
||||
}
|
||||
|
||||
user.CreatedAt = time.Now().Unix()
|
||||
user.UpdatedAt = time.Now().Unix()
|
||||
|
||||
bytes, err := json.Marshal(user)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
||||
// use decoder instead of json.Unmarshall, because it converts int64 -> float64 after unmarshalling
|
||||
decoder := json.NewDecoder(strings.NewReader(string(bytes)))
|
||||
decoder.UseNumber()
|
||||
userMap := map[string]interface{}{}
|
||||
err = decoder.Decode(&userMap)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
||||
fields := "("
|
||||
values := "("
|
||||
for key, value := range userMap {
|
||||
if value != nil {
|
||||
if key == "_id" {
|
||||
fields += "id,"
|
||||
} else {
|
||||
fields += key + ","
|
||||
}
|
||||
|
||||
valueType := reflect.TypeOf(value)
|
||||
if valueType.Name() == "string" {
|
||||
values += fmt.Sprintf("'%s',", value.(string))
|
||||
} else {
|
||||
values += fmt.Sprintf("%v,", value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fields = fields[:len(fields)-1] + ")"
|
||||
values = values[:len(values)-1] + ")"
|
||||
|
||||
query := fmt.Sprintf("INSERT INTO %s %s VALUES %s IF NOT EXISTS", KeySpace+"."+models.Collections.User, fields, values)
|
||||
|
||||
err = p.db.Query(query).Exec()
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
bytes, err := json.Marshal(user)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
// use decoder instead of json.Unmarshall, because it converts int64 -> float64 after unmarshalling
|
||||
decoder := json.NewDecoder(strings.NewReader(string(bytes)))
|
||||
decoder.UseNumber()
|
||||
userMap := map[string]interface{}{}
|
||||
err = decoder.Decode(&userMap)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
||||
updateFields := ""
|
||||
for key, value := range userMap {
|
||||
if value != nil && key != "_id" {
|
||||
}
|
||||
|
||||
if key == "_id" {
|
||||
continue
|
||||
}
|
||||
|
||||
if value == nil {
|
||||
updateFields += fmt.Sprintf("%s = null,", key)
|
||||
continue
|
||||
}
|
||||
|
||||
valueType := reflect.TypeOf(value)
|
||||
if valueType.Name() == "string" {
|
||||
updateFields += fmt.Sprintf("%s = '%s', ", key, value.(string))
|
||||
} else {
|
||||
updateFields += fmt.Sprintf("%s = %v, ", key, value)
|
||||
}
|
||||
}
|
||||
updateFields = strings.Trim(updateFields, " ")
|
||||
updateFields = strings.TrimSuffix(updateFields, ",")
|
||||
|
||||
query := fmt.Sprintf("UPDATE %s SET %s WHERE id = '%s'", KeySpace+"."+models.Collections.User, updateFields, user.ID)
|
||||
|
||||
err = p.db.Query(query).Exec()
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// DeleteUser to delete user information from database
|
||||
func (p *provider) DeleteUser(user models.User) error {
|
||||
query := fmt.Sprintf("DELETE FROM %s WHERE id = '%s'", KeySpace+"."+models.Collections.User, user.ID)
|
||||
err := p.db.Query(query).Exec()
|
||||
return err
|
||||
}
|
||||
|
||||
// ListUsers to get list of users from database
|
||||
func (p *provider) ListUsers(pagination model.Pagination) (*model.Users, error) {
|
||||
responseUsers := []*model.User{}
|
||||
paginationClone := pagination
|
||||
totalCountQuery := fmt.Sprintf(`SELECT COUNT(*) FROM %s`, KeySpace+"."+models.Collections.User)
|
||||
err := p.db.Query(totalCountQuery).Consistency(gocql.One).Scan(&paginationClone.Total)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// there is no offset in cassandra
|
||||
// so we fetch till limit + offset
|
||||
// and return the results from offset to limit
|
||||
query := fmt.Sprintf("SELECT id, email, email_verified_at, password, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, created_at, updated_at FROM %s LIMIT %d", KeySpace+"."+models.Collections.User, pagination.Limit+pagination.Offset)
|
||||
|
||||
scanner := p.db.Query(query).Iter().Scanner()
|
||||
counter := int64(0)
|
||||
for scanner.Next() {
|
||||
if counter >= pagination.Offset {
|
||||
var user models.User
|
||||
err := scanner.Scan(&user.ID, &user.Email, &user.EmailVerifiedAt, &user.Password, &user.SignupMethods, &user.GivenName, &user.FamilyName, &user.MiddleName, &user.Nickname, &user.Birthdate, &user.PhoneNumber, &user.PhoneNumberVerifiedAt, &user.Picture, &user.Roles, &user.RevokedTimestamp, &user.CreatedAt, &user.UpdatedAt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
responseUsers = append(responseUsers, user.AsAPIUser())
|
||||
}
|
||||
counter++
|
||||
}
|
||||
return &model.Users{
|
||||
Users: responseUsers,
|
||||
Pagination: &paginationClone,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetUserByEmail to get user information from database using email address
|
||||
func (p *provider) GetUserByEmail(email string) (models.User, error) {
|
||||
var user models.User
|
||||
query := fmt.Sprintf("SELECT id, email, email_verified_at, password, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, created_at, updated_at FROM %s WHERE email = '%s' LIMIT 1", KeySpace+"."+models.Collections.User, email)
|
||||
err := p.db.Query(query).Consistency(gocql.One).Scan(&user.ID, &user.Email, &user.EmailVerifiedAt, &user.Password, &user.SignupMethods, &user.GivenName, &user.FamilyName, &user.MiddleName, &user.Nickname, &user.Birthdate, &user.PhoneNumber, &user.PhoneNumberVerifiedAt, &user.Picture, &user.Roles, &user.RevokedTimestamp, &user.CreatedAt, &user.UpdatedAt)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
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
|
||||
query := fmt.Sprintf("SELECT id, email, email_verified_at, password, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, created_at, updated_at FROM %s WHERE id = '%s' LIMIT 1", KeySpace+"."+models.Collections.User, id)
|
||||
err := p.db.Query(query).Consistency(gocql.One).Scan(&user.ID, &user.Email, &user.EmailVerifiedAt, &user.Password, &user.SignupMethods, &user.GivenName, &user.FamilyName, &user.MiddleName, &user.Nickname, &user.Birthdate, &user.PhoneNumber, &user.PhoneNumberVerifiedAt, &user.Picture, &user.Roles, &user.RevokedTimestamp, &user.CreatedAt, &user.UpdatedAt)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
return user, nil
|
||||
}
|
99
server/db/providers/cassandradb/verification_requests.go
Normal file
99
server/db/providers/cassandradb/verification_requests.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package cassandradb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/gocql/gocql"
|
||||
"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()
|
||||
|
||||
query := fmt.Sprintf("INSERT INTO %s (id, jwt_token, identifier, expires_at, email, nonce, redirect_uri, created_at, updated_at) VALUES ('%s', '%s', '%s', %d, '%s', '%s', '%s', %d, %d)", KeySpace+"."+models.Collections.VerificationRequest, verificationRequest.ID, verificationRequest.Token, verificationRequest.Identifier, verificationRequest.ExpiresAt, verificationRequest.Email, verificationRequest.Nonce, verificationRequest.RedirectURI, verificationRequest.CreatedAt, verificationRequest.UpdatedAt)
|
||||
err := p.db.Query(query).Exec()
|
||||
if err != nil {
|
||||
return verificationRequest, err
|
||||
}
|
||||
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
|
||||
query := fmt.Sprintf(`SELECT id, jwt_token, identifier, expires_at, email, nonce, redirect_uri, created_at, updated_at FROM %s WHERE jwt_token = '%s' LIMIT 1`, KeySpace+"."+models.Collections.VerificationRequest, token)
|
||||
|
||||
err := p.db.Query(query).Consistency(gocql.One).Scan(&verificationRequest.ID, &verificationRequest.Token, &verificationRequest.Identifier, &verificationRequest.ExpiresAt, &verificationRequest.Email, &verificationRequest.Nonce, &verificationRequest.RedirectURI, &verificationRequest.CreatedAt, &verificationRequest.UpdatedAt)
|
||||
if err != nil {
|
||||
return verificationRequest, err
|
||||
}
|
||||
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
|
||||
query := fmt.Sprintf(`SELECT id, jwt_token, identifier, expires_at, email, nonce, redirect_uri, created_at, updated_at FROM %s WHERE email = '%s' AND identifier = '%s' LIMIT 1 ALLOW FILTERING`, KeySpace+"."+models.Collections.VerificationRequest, email, identifier)
|
||||
|
||||
err := p.db.Query(query).Consistency(gocql.One).Scan(&verificationRequest.ID, &verificationRequest.Token, &verificationRequest.Identifier, &verificationRequest.ExpiresAt, &verificationRequest.Email, &verificationRequest.Nonce, &verificationRequest.RedirectURI, &verificationRequest.CreatedAt, &verificationRequest.UpdatedAt)
|
||||
if err != nil {
|
||||
return verificationRequest, err
|
||||
}
|
||||
|
||||
return verificationRequest, nil
|
||||
}
|
||||
|
||||
// ListVerificationRequests to get list of verification requests from database
|
||||
func (p *provider) ListVerificationRequests(pagination model.Pagination) (*model.VerificationRequests, error) {
|
||||
var verificationRequests []*model.VerificationRequest
|
||||
|
||||
paginationClone := pagination
|
||||
totalCountQuery := fmt.Sprintf(`SELECT COUNT(*) FROM %s`, KeySpace+"."+models.Collections.VerificationRequest)
|
||||
err := p.db.Query(totalCountQuery).Consistency(gocql.One).Scan(&paginationClone.Total)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// there is no offset in cassandra
|
||||
// so we fetch till limit + offset
|
||||
// and return the results from offset to limit
|
||||
query := fmt.Sprintf(`SELECT id, jwt_token, identifier, expires_at, email, nonce, redirect_uri, created_at, updated_at FROM %s LIMIT %d`, KeySpace+"."+models.Collections.VerificationRequest, pagination.Limit+pagination.Offset)
|
||||
|
||||
scanner := p.db.Query(query).Iter().Scanner()
|
||||
counter := int64(0)
|
||||
for scanner.Next() {
|
||||
if counter >= pagination.Offset {
|
||||
var verificationRequest models.VerificationRequest
|
||||
err := scanner.Scan(&verificationRequest.ID, &verificationRequest.Token, &verificationRequest.Identifier, &verificationRequest.ExpiresAt, &verificationRequest.Email, &verificationRequest.Nonce, &verificationRequest.RedirectURI, &verificationRequest.CreatedAt, &verificationRequest.UpdatedAt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
verificationRequests = append(verificationRequests, verificationRequest.AsAPIVerificationRequest())
|
||||
}
|
||||
counter++
|
||||
}
|
||||
|
||||
return &model.VerificationRequests{
|
||||
VerificationRequests: verificationRequests,
|
||||
Pagination: &paginationClone,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeleteVerificationRequest to delete verification request from database
|
||||
func (p *provider) DeleteVerificationRequest(verificationRequest models.VerificationRequest) error {
|
||||
query := fmt.Sprintf("DELETE FROM %s WHERE id = '%s'", KeySpace+"."+models.Collections.VerificationRequest, verificationRequest.ID)
|
||||
err := p.db.Query(query).Exec()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
@@ -2,7 +2,6 @@ package mongodb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
@@ -23,7 +22,6 @@ func (p *provider) AddEnv(env models.Env) (models.Env, error) {
|
||||
configCollection := p.db.Collection(models.Collections.Env, options.Collection())
|
||||
_, err := configCollection.InsertOne(nil, env)
|
||||
if err != nil {
|
||||
log.Println("error adding config:", err)
|
||||
return env, err
|
||||
}
|
||||
return env, nil
|
||||
@@ -35,7 +33,6 @@ func (p *provider) UpdateEnv(env models.Env) (models.Env, error) {
|
||||
configCollection := p.db.Collection(models.Collections.Env, options.Collection())
|
||||
_, err := configCollection.UpdateOne(nil, bson.M{"_id": bson.M{"$eq": env.ID}}, bson.M{"$set": env}, options.MergeUpdateOptions())
|
||||
if err != nil {
|
||||
log.Println("error updating config:", err)
|
||||
return env, err
|
||||
}
|
||||
return env, nil
|
||||
|
@@ -4,9 +4,8 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
@@ -19,7 +18,8 @@ type provider struct {
|
||||
|
||||
// NewProvider to initialize mongodb connection
|
||||
func NewProvider() (*provider, error) {
|
||||
mongodbOptions := options.Client().ApplyURI(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseURL))
|
||||
dbURL := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabaseURL
|
||||
mongodbOptions := options.Client().ApplyURI(dbURL)
|
||||
maxWait := time.Duration(5 * time.Second)
|
||||
mongodbOptions.ConnectTimeout = &maxWait
|
||||
mongoClient, err := mongo.NewClient(mongodbOptions)
|
||||
@@ -37,18 +37,19 @@ func NewProvider() (*provider, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mongodb := mongoClient.Database(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseName), options.Database())
|
||||
dbName := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabaseName
|
||||
mongodb := mongoClient.Database(dbName, options.Database())
|
||||
|
||||
mongodb.CreateCollection(ctx, models.Collections.User, options.CreateCollection())
|
||||
userCollection := mongodb.Collection(models.Collections.User, options.Collection())
|
||||
userCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{
|
||||
mongo.IndexModel{
|
||||
{
|
||||
Keys: bson.M{"email": 1},
|
||||
Options: options.Index().SetUnique(true).SetSparse(true),
|
||||
},
|
||||
}, options.CreateIndexes())
|
||||
userCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{
|
||||
mongo.IndexModel{
|
||||
{
|
||||
Keys: bson.M{"phone_number": 1},
|
||||
Options: options.Index().SetUnique(true).SetSparse(true).SetPartialFilterExpression(map[string]interface{}{
|
||||
"phone_number": map[string]string{"$type": "string"},
|
||||
@@ -59,13 +60,13 @@ func NewProvider() (*provider, error) {
|
||||
mongodb.CreateCollection(ctx, models.Collections.VerificationRequest, options.CreateCollection())
|
||||
verificationRequestCollection := mongodb.Collection(models.Collections.VerificationRequest, options.Collection())
|
||||
verificationRequestCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{
|
||||
mongo.IndexModel{
|
||||
{
|
||||
Keys: bson.M{"email": 1, "identifier": 1},
|
||||
Options: options.Index().SetUnique(true).SetSparse(true),
|
||||
},
|
||||
}, options.CreateIndexes())
|
||||
verificationRequestCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{
|
||||
mongo.IndexModel{
|
||||
{
|
||||
Keys: bson.M{"token": 1},
|
||||
Options: options.Index().SetSparse(true),
|
||||
},
|
||||
@@ -74,7 +75,7 @@ func NewProvider() (*provider, error) {
|
||||
mongodb.CreateCollection(ctx, models.Collections.Session, options.CreateCollection())
|
||||
sessionCollection := mongodb.Collection(models.Collections.Session, options.Collection())
|
||||
sessionCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{
|
||||
mongo.IndexModel{
|
||||
{
|
||||
Keys: bson.M{"user_id": 1},
|
||||
Options: options.Index().SetSparse(true),
|
||||
},
|
@@ -1,7 +1,6 @@
|
||||
package mongodb
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
@@ -22,7 +21,6 @@ func (p *provider) AddSession(session models.Session) error {
|
||||
sessionCollection := p.db.Collection(models.Collections.Session, options.Collection())
|
||||
_, err := sessionCollection.InsertOne(nil, session)
|
||||
if err != nil {
|
||||
log.Println(`error saving session`, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -33,7 +31,6 @@ func (p *provider) DeleteSession(userId string) error {
|
||||
sessionCollection := p.db.Collection(models.Collections.Session, options.Collection())
|
||||
_, err := sessionCollection.DeleteMany(nil, bson.M{"user_id": userId}, options.Delete())
|
||||
if err != nil {
|
||||
log.Println("error deleting session:", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@@ -1,14 +1,12 @@
|
||||
package mongodb
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"github.com/google/uuid"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
@@ -21,7 +19,11 @@ func (p *provider) AddUser(user models.User) (models.User, error) {
|
||||
}
|
||||
|
||||
if user.Roles == "" {
|
||||
user.Roles = strings.Join(envstore.EnvStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyDefaultRoles), ",")
|
||||
defaultRoles, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyDefaultRoles)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
user.Roles = defaultRoles
|
||||
}
|
||||
user.CreatedAt = time.Now().Unix()
|
||||
user.UpdatedAt = time.Now().Unix()
|
||||
@@ -29,7 +31,6 @@ func (p *provider) AddUser(user models.User) (models.User, error) {
|
||||
userCollection := p.db.Collection(models.Collections.User, options.Collection())
|
||||
_, err := userCollection.InsertOne(nil, user)
|
||||
if err != nil {
|
||||
log.Println("error adding user:", err)
|
||||
return user, err
|
||||
}
|
||||
|
||||
@@ -42,7 +43,6 @@ func (p *provider) UpdateUser(user models.User) (models.User, error) {
|
||||
userCollection := p.db.Collection(models.Collections.User, options.Collection())
|
||||
_, err := userCollection.UpdateOne(nil, bson.M{"_id": bson.M{"$eq": user.ID}}, bson.M{"$set": user}, options.MergeUpdateOptions())
|
||||
if err != nil {
|
||||
log.Println("error updating user:", err)
|
||||
return user, err
|
||||
}
|
||||
return user, nil
|
||||
@@ -53,7 +53,6 @@ func (p *provider) DeleteUser(user models.User) error {
|
||||
userCollection := p.db.Collection(models.Collections.User, options.Collection())
|
||||
_, err := userCollection.DeleteOne(nil, bson.M{"_id": user.ID}, options.Delete())
|
||||
if err != nil {
|
||||
log.Println("error deleting user:", err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -69,12 +68,10 @@ func (p *provider) ListUsers(pagination model.Pagination) (*model.Users, error)
|
||||
opts.SetSort(bson.M{"created_at": -1})
|
||||
|
||||
paginationClone := pagination
|
||||
// TODO add pagination total
|
||||
|
||||
userCollection := p.db.Collection(models.Collections.User, options.Collection())
|
||||
count, err := userCollection.CountDocuments(nil, bson.M{}, options.Count())
|
||||
if err != nil {
|
||||
log.Println("error getting total users:", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -82,7 +79,6 @@ func (p *provider) ListUsers(pagination model.Pagination) (*model.Users, error)
|
||||
|
||||
cursor, err := userCollection.Find(nil, bson.M{}, opts)
|
||||
if err != nil {
|
||||
log.Println("error getting users:", err)
|
||||
return nil, err
|
||||
}
|
||||
defer cursor.Close(nil)
|
||||
|
@@ -1,7 +1,6 @@
|
||||
package mongodb
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
@@ -22,7 +21,6 @@ func (p *provider) AddVerificationRequest(verificationRequest models.Verificatio
|
||||
verificationRequestCollection := p.db.Collection(models.Collections.VerificationRequest, options.Collection())
|
||||
_, err := verificationRequestCollection.InsertOne(nil, verificationRequest)
|
||||
if err != nil {
|
||||
log.Println("error saving verification record:", err)
|
||||
return verificationRequest, err
|
||||
}
|
||||
}
|
||||
@@ -73,7 +71,6 @@ func (p *provider) ListVerificationRequests(pagination model.Pagination) (*model
|
||||
|
||||
cursor, err := verificationRequestCollection.Find(nil, bson.M{}, opts)
|
||||
if err != nil {
|
||||
log.Println("error getting verification requests:", err)
|
||||
return nil, err
|
||||
}
|
||||
defer cursor.Close(nil)
|
||||
@@ -98,7 +95,6 @@ func (p *provider) DeleteVerificationRequest(verificationRequest models.Verifica
|
||||
verificationRequestCollection := p.db.Collection(models.Collections.VerificationRequest, options.Collection())
|
||||
_, err := verificationRequestCollection.DeleteOne(nil, bson.M{"_id": verificationRequest.ID}, options.Delete())
|
||||
if err != nil {
|
||||
log.Println("error deleting verification request::", err)
|
||||
return err
|
||||
}
|
||||
|
||||
|
32
server/db/providers/provider_template/env.go
Normal file
32
server/db/providers/provider_template/env.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package provider_template
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// 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.CreatedAt = time.Now().Unix()
|
||||
env.UpdatedAt = time.Now().Unix()
|
||||
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
|
||||
}
|
20
server/db/providers/provider_template/provider.go
Normal file
20
server/db/providers/provider_template/provider.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package provider_template
|
||||
|
||||
import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// TODO change following provider to new db provider
|
||||
type provider struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewProvider returns a new SQL provider
|
||||
// TODO change following provider to new db provider
|
||||
func NewProvider() (*provider, error) {
|
||||
var sqlDB *gorm.DB
|
||||
|
||||
return &provider{
|
||||
db: sqlDB,
|
||||
}, nil
|
||||
}
|
24
server/db/providers/provider_template/session.go
Normal file
24
server/db/providers/provider_template/session.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package provider_template
|
||||
|
||||
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
|
||||
}
|
61
server/db/providers/provider_template/user.go
Normal file
61
server/db/providers/provider_template/user.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package provider_template
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"github.com/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 == "" {
|
||||
defaultRoles, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyDefaultRoles)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
user.Roles = defaultRoles
|
||||
}
|
||||
|
||||
user.CreatedAt = time.Now().Unix()
|
||||
user.UpdatedAt = time.Now().Unix()
|
||||
|
||||
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(pagination model.Pagination) (*model.Users, error) {
|
||||
return nil, 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
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
package provider_template
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"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(pagination model.Pagination) (*model.VerificationRequests, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// DeleteVerificationRequest to delete verification request from database
|
||||
func (p *provider) DeleteVerificationRequest(verificationRequest models.VerificationRequest) error {
|
||||
return nil
|
||||
}
|
@@ -1,7 +1,6 @@
|
||||
package sql
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
@@ -20,7 +19,6 @@ func (p *provider) AddEnv(env models.Env) (models.Env, error) {
|
||||
|
||||
result := p.db.Create(&env)
|
||||
if result.Error != nil {
|
||||
log.Println("error adding config:", result.Error)
|
||||
return env, result.Error
|
||||
}
|
||||
return env, nil
|
||||
@@ -32,7 +30,6 @@ func (p *provider) UpdateEnv(env models.Env) (models.Env, error) {
|
||||
result := p.db.Save(&env)
|
||||
|
||||
if result.Error != nil {
|
||||
log.Println("error updating config:", result.Error)
|
||||
return env, result.Error
|
||||
}
|
||||
return env, nil
|
||||
|
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/driver/sqlite"
|
||||
@@ -41,15 +41,19 @@ func NewProvider() (*provider, error) {
|
||||
TablePrefix: models.Prefix,
|
||||
},
|
||||
}
|
||||
switch envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) {
|
||||
case constants.DbTypePostgres, constants.DbTypeYugabyte:
|
||||
sqlDB, err = gorm.Open(postgres.Open(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseURL)), ormConfig)
|
||||
|
||||
dbType := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabaseType
|
||||
dbURL := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabaseURL
|
||||
|
||||
switch dbType {
|
||||
case constants.DbTypePostgres, constants.DbTypeYugabyte, constants.DbTypeCockroachDB:
|
||||
sqlDB, err = gorm.Open(postgres.Open(dbURL), ormConfig)
|
||||
case constants.DbTypeSqlite:
|
||||
sqlDB, err = gorm.Open(sqlite.Open(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseURL)), ormConfig)
|
||||
sqlDB, err = gorm.Open(sqlite.Open(dbURL), ormConfig)
|
||||
case constants.DbTypeMysql, constants.DbTypeMariaDB:
|
||||
sqlDB, err = gorm.Open(mysql.Open(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseURL)), ormConfig)
|
||||
sqlDB, err = gorm.Open(mysql.Open(dbURL), ormConfig)
|
||||
case constants.DbTypeSqlserver:
|
||||
sqlDB, err = gorm.Open(sqlserver.Open(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseURL)), ormConfig)
|
||||
sqlDB, err = gorm.Open(sqlserver.Open(dbURL), ormConfig)
|
||||
}
|
||||
|
||||
if err != nil {
|
@@ -1,7 +1,6 @@
|
||||
package sql
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
@@ -23,7 +22,6 @@ func (p *provider) AddSession(session models.Session) error {
|
||||
DoNothing: true,
|
||||
}).Create(&session)
|
||||
if res.Error != nil {
|
||||
log.Println(`error saving session`, res.Error)
|
||||
return res.Error
|
||||
}
|
||||
return nil
|
||||
@@ -34,7 +32,6 @@ func (p *provider) DeleteSession(userId string) error {
|
||||
result := p.db.Where("user_id = ?", userId).Delete(&models.Session{})
|
||||
|
||||
if result.Error != nil {
|
||||
log.Println(`error deleting session:`, result.Error)
|
||||
return result.Error
|
||||
}
|
||||
return nil
|
||||
|
@@ -1,14 +1,12 @@
|
||||
package sql
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
@@ -20,7 +18,11 @@ func (p *provider) AddUser(user models.User) (models.User, error) {
|
||||
}
|
||||
|
||||
if user.Roles == "" {
|
||||
user.Roles = strings.Join(envstore.EnvStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyDefaultRoles), ",")
|
||||
defaultRoles, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyDefaultRoles)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
user.Roles = defaultRoles
|
||||
}
|
||||
|
||||
user.CreatedAt = time.Now().Unix()
|
||||
@@ -33,7 +35,6 @@ func (p *provider) AddUser(user models.User) (models.User, error) {
|
||||
}).Create(&user)
|
||||
|
||||
if result.Error != nil {
|
||||
log.Println("error adding user:", result.Error)
|
||||
return user, result.Error
|
||||
}
|
||||
|
||||
@@ -47,7 +48,6 @@ func (p *provider) UpdateUser(user models.User) (models.User, error) {
|
||||
result := p.db.Save(&user)
|
||||
|
||||
if result.Error != nil {
|
||||
log.Println("error updating user:", result.Error)
|
||||
return user, result.Error
|
||||
}
|
||||
|
||||
@@ -59,7 +59,6 @@ func (p *provider) DeleteUser(user models.User) error {
|
||||
result := p.db.Delete(&user)
|
||||
|
||||
if result.Error != nil {
|
||||
log.Println(`error deleting user:`, result.Error)
|
||||
return result.Error
|
||||
}
|
||||
|
||||
@@ -71,7 +70,6 @@ func (p *provider) ListUsers(pagination model.Pagination) (*model.Users, error)
|
||||
var users []models.User
|
||||
result := p.db.Limit(int(pagination.Limit)).Offset(int(pagination.Offset)).Order("created_at DESC").Find(&users)
|
||||
if result.Error != nil {
|
||||
log.Println("error getting users:", result.Error)
|
||||
return nil, result.Error
|
||||
}
|
||||
|
||||
@@ -99,7 +97,6 @@ func (p *provider) ListUsers(pagination model.Pagination) (*model.Users, error)
|
||||
func (p *provider) GetUserByEmail(email string) (models.User, error) {
|
||||
var user models.User
|
||||
result := p.db.Where("email = ?", email).First(&user)
|
||||
|
||||
if result.Error != nil {
|
||||
return user, result.Error
|
||||
}
|
||||
@@ -112,7 +109,6 @@ func (p *provider) GetUserByID(id string) (models.User, error) {
|
||||
var user models.User
|
||||
|
||||
result := p.db.Where("id = ?", id).First(&user)
|
||||
|
||||
if result.Error != nil {
|
||||
return user, result.Error
|
||||
}
|
||||
|
@@ -1,7 +1,6 @@
|
||||
package sql
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
@@ -21,11 +20,10 @@ func (p *provider) AddVerificationRequest(verificationRequest models.Verificatio
|
||||
verificationRequest.UpdatedAt = time.Now().Unix()
|
||||
result := p.db.Clauses(clause.OnConflict{
|
||||
Columns: []clause.Column{{Name: "email"}, {Name: "identifier"}},
|
||||
DoUpdates: clause.AssignmentColumns([]string{"token", "expires_at"}),
|
||||
DoUpdates: clause.AssignmentColumns([]string{"token", "expires_at", "nonce", "redirect_uri"}),
|
||||
}).Create(&verificationRequest)
|
||||
|
||||
if result.Error != nil {
|
||||
log.Println(`error saving verification request record`, result.Error)
|
||||
return verificationRequest, result.Error
|
||||
}
|
||||
|
||||
@@ -38,7 +36,6 @@ func (p *provider) GetVerificationRequestByToken(token string) (models.Verificat
|
||||
result := p.db.Where("token = ?", token).First(&verificationRequest)
|
||||
|
||||
if result.Error != nil {
|
||||
log.Println(`error getting verification request:`, result.Error)
|
||||
return verificationRequest, result.Error
|
||||
}
|
||||
|
||||
@@ -52,7 +49,6 @@ func (p *provider) GetVerificationRequestByEmail(email string, identifier string
|
||||
result := p.db.Where("email = ? AND identifier = ?", email, identifier).First(&verificationRequest)
|
||||
|
||||
if result.Error != nil {
|
||||
log.Println(`error getting verification token:`, result.Error)
|
||||
return verificationRequest, result.Error
|
||||
}
|
||||
|
||||
@@ -65,7 +61,6 @@ func (p *provider) ListVerificationRequests(pagination model.Pagination) (*model
|
||||
|
||||
result := p.db.Limit(int(pagination.Limit)).Offset(int(pagination.Offset)).Order("created_at DESC").Find(&verificationRequests)
|
||||
if result.Error != nil {
|
||||
log.Println("error getting verification requests:", result.Error)
|
||||
return nil, result.Error
|
||||
}
|
||||
|
||||
@@ -94,7 +89,6 @@ func (p *provider) DeleteVerificationRequest(verificationRequest models.Verifica
|
||||
result := p.db.Delete(&verificationRequest)
|
||||
|
||||
if result.Error != nil {
|
||||
log.Println(`error deleting verification request:`, result.Error)
|
||||
return result.Error
|
||||
}
|
||||
|
||||
|
@@ -4,13 +4,14 @@ import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"strconv"
|
||||
"text/template"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
log "github.com/sirupsen/logrus"
|
||||
gomail "gopkg.in/mail.v2"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
)
|
||||
|
||||
// addEmailTemplate is used to add html template in email body
|
||||
@@ -32,21 +33,61 @@ func addEmailTemplate(a string, b map[string]interface{}, templateName string) s
|
||||
// SendMail function to send mail
|
||||
func SendMail(to []string, Subject, bodyMessage string) error {
|
||||
// dont trigger email sending in case of test
|
||||
if envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyEnv) == "test" {
|
||||
envKey, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyEnv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if envKey == constants.TestEnv {
|
||||
return nil
|
||||
}
|
||||
m := gomail.NewMessage()
|
||||
m.SetHeader("From", envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeySenderEmail))
|
||||
senderEmail, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeySenderEmail)
|
||||
if err != nil {
|
||||
log.Errorf("Error while getting sender email from env variable: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
smtpPort, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeySmtpPort)
|
||||
if err != nil {
|
||||
log.Errorf("Error while getting smtp port from env variable: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
smtpHost, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeySmtpHost)
|
||||
if err != nil {
|
||||
log.Errorf("Error while getting smtp host from env variable: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
smtpUsername, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeySmtpUsername)
|
||||
if err != nil {
|
||||
log.Errorf("Error while getting smtp username from env variable: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
smtpPassword, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeySmtpPassword)
|
||||
if err != nil {
|
||||
log.Errorf("Error while getting smtp password from env variable: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
isProd, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyIsProd)
|
||||
if err != nil {
|
||||
log.Errorf("Error while getting env variable: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
m.SetHeader("From", senderEmail)
|
||||
m.SetHeader("To", to...)
|
||||
m.SetHeader("Subject", Subject)
|
||||
m.SetBody("text/html", bodyMessage)
|
||||
port, _ := strconv.Atoi(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeySmtpPort))
|
||||
d := gomail.NewDialer(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeySmtpHost), port, envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeySmtpUsername), envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeySmtpPassword))
|
||||
if envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyEnv) == "development" {
|
||||
port, _ := strconv.Atoi(smtpPort)
|
||||
d := gomail.NewDialer(smtpHost, port, smtpUsername, smtpPassword)
|
||||
if !isProd {
|
||||
d.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
}
|
||||
if err := d.DialAndSend(m); err != nil {
|
||||
log.Printf("smtp error: %s", err)
|
||||
log.Debug("SMTP Failed: ", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@@ -2,14 +2,19 @@ package email
|
||||
|
||||
import (
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
)
|
||||
|
||||
// SendForgotPasswordMail to send forgot password email
|
||||
func SendForgotPasswordMail(toEmail, token, hostname string) error {
|
||||
resetPasswordUrl := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyResetPasswordURL)
|
||||
resetPasswordUrl, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyResetPasswordURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resetPasswordUrl == "" {
|
||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyResetPasswordURL, hostname+"/app/reset-password")
|
||||
if err := memorystore.Provider.UpdateEnvVariable(constants.EnvKeyResetPasswordURL, hostname+"/app/reset-password"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// The receiver needs to be in slice as the receive supports multiple receiver
|
||||
@@ -103,8 +108,14 @@ func SendForgotPasswordMail(toEmail, token, hostname string) error {
|
||||
`
|
||||
|
||||
data := make(map[string]interface{}, 3)
|
||||
data["org_logo"] = envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyOrganizationLogo)
|
||||
data["org_name"] = envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyOrganizationName)
|
||||
data["org_logo"], err = memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyOrganizationLogo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data["org_name"], err = memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyOrganizationName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data["verification_url"] = resetPasswordUrl + "?token=" + token
|
||||
message = addEmailTemplate(message, data, "reset_password_email.tmpl")
|
||||
|
||||
|
120
server/email/invite_email.go
Normal file
120
server/email/invite_email.go
Normal file
@@ -0,0 +1,120 @@
|
||||
package email
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
)
|
||||
|
||||
// InviteEmail to send invite email
|
||||
func InviteEmail(toEmail, token, verificationURL, redirectURI 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)
|
||||
var err error
|
||||
data["org_logo"], err = memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyOrganizationLogo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data["org_name"], err = memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyOrganizationName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data["verification_url"] = verificationURL + "?token=" + token + "&redirect_uri=" + redirectURI
|
||||
message = addEmailTemplate(message, data, "invite_email.tmpl")
|
||||
// bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message)
|
||||
|
||||
err = SendMail(Receiver, Subject, message)
|
||||
if err != nil {
|
||||
log.Warn("error sending email: ", err)
|
||||
}
|
||||
return err
|
||||
}
|
@@ -1,8 +1,10 @@
|
||||
package email
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
)
|
||||
|
||||
// SendVerificationMail to send verification email
|
||||
@@ -97,11 +99,22 @@ func SendVerificationMail(toEmail, token, hostname string) error {
|
||||
</html>
|
||||
`
|
||||
data := make(map[string]interface{}, 3)
|
||||
data["org_logo"] = envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyOrganizationLogo)
|
||||
data["org_name"] = envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyOrganizationName)
|
||||
var err error
|
||||
data["org_logo"], err = memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyOrganizationLogo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data["org_name"], err = memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyOrganizationName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data["verification_url"] = hostname + "/verify_email?token=" + token
|
||||
message = addEmailTemplate(message, data, "verify_email.tmpl")
|
||||
// bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message)
|
||||
|
||||
return SendMail(Receiver, Subject, message)
|
||||
err = SendMail(Receiver, Subject, message)
|
||||
if err != nil {
|
||||
log.Warn("error sending email: ", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
674
server/env/env.go
vendored
674
server/env/env.go
vendored
@@ -2,183 +2,246 @@ package env
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/crypto"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"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
|
||||
func InitAllEnv() error {
|
||||
envData, err := GetEnvData()
|
||||
if err != nil {
|
||||
log.Println("No env data found in db, using local clone of env data")
|
||||
log.Info("No env data found in db, using local clone of env data")
|
||||
// get clone of current store
|
||||
envData = envstore.EnvStoreObj.GetEnvStoreClone()
|
||||
envData, err = memorystore.Provider.GetEnvStore()
|
||||
if err != nil {
|
||||
log.Debug("Error while getting env data from memorystore: ", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
clientID := envData.StringEnv[constants.EnvKeyClientID]
|
||||
// unique client id for each instance
|
||||
if clientID == "" {
|
||||
cid, ok := envData[constants.EnvKeyClientID]
|
||||
clientID := ""
|
||||
if !ok || cid == "" {
|
||||
clientID = uuid.New().String()
|
||||
envData.StringEnv[constants.EnvKeyClientID] = clientID
|
||||
envData[constants.EnvKeyClientID] = clientID
|
||||
} else {
|
||||
clientID = cid.(string)
|
||||
}
|
||||
|
||||
clientSecret := envData.StringEnv[constants.EnvKeyClientSecret]
|
||||
// unique client id for each instance
|
||||
if clientSecret == "" {
|
||||
clientSecret = uuid.New().String()
|
||||
envData.StringEnv[constants.EnvKeyClientSecret] = clientSecret
|
||||
// unique client secret for each instance
|
||||
if val, ok := envData[constants.EnvKeyClientSecret]; !ok || val != "" {
|
||||
envData[constants.EnvKeyClientSecret] = uuid.New().String()
|
||||
}
|
||||
|
||||
if envData.StringEnv[constants.EnvKeyEnv] == "" {
|
||||
envData.StringEnv[constants.EnvKeyEnv] = os.Getenv(constants.EnvKeyEnv)
|
||||
if envData.StringEnv[constants.EnvKeyEnv] == "" {
|
||||
envData.StringEnv[constants.EnvKeyEnv] = "production"
|
||||
// os string envs
|
||||
osEnv := os.Getenv(constants.EnvKeyEnv)
|
||||
osAppURL := os.Getenv(constants.EnvKeyAppURL)
|
||||
osAuthorizerURL := os.Getenv(constants.EnvKeyAuthorizerURL)
|
||||
osPort := os.Getenv(constants.EnvKeyPort)
|
||||
osAccessTokenExpiryTime := os.Getenv(constants.EnvKeyAccessTokenExpiryTime)
|
||||
osAdminSecret := os.Getenv(constants.EnvKeyAdminSecret)
|
||||
osSmtpHost := os.Getenv(constants.EnvKeySmtpHost)
|
||||
osSmtpPort := os.Getenv(constants.EnvKeySmtpPort)
|
||||
osSmtpUsername := os.Getenv(constants.EnvKeySmtpUsername)
|
||||
osSmtpPassword := os.Getenv(constants.EnvKeySmtpPassword)
|
||||
osSenderEmail := os.Getenv(constants.EnvKeySenderEmail)
|
||||
osJwtType := os.Getenv(constants.EnvKeyJwtType)
|
||||
osJwtSecret := os.Getenv(constants.EnvKeyJwtSecret)
|
||||
osJwtPrivateKey := os.Getenv(constants.EnvKeyJwtPrivateKey)
|
||||
osJwtPublicKey := os.Getenv(constants.EnvKeyJwtPublicKey)
|
||||
osJwtRoleClaim := os.Getenv(constants.EnvKeyJwtRoleClaim)
|
||||
osCustomAccessTokenScript := os.Getenv(constants.EnvKeyCustomAccessTokenScript)
|
||||
osGoogleClientID := os.Getenv(constants.EnvKeyGoogleClientID)
|
||||
osGoogleClientSecret := os.Getenv(constants.EnvKeyGoogleClientSecret)
|
||||
osGithubClientID := os.Getenv(constants.EnvKeyGithubClientID)
|
||||
osGithubClientSecret := os.Getenv(constants.EnvKeyGithubClientSecret)
|
||||
osFacebookClientID := os.Getenv(constants.EnvKeyFacebookClientID)
|
||||
osFacebookClientSecret := os.Getenv(constants.EnvKeyFacebookClientSecret)
|
||||
osLinkedInClientID := os.Getenv(constants.EnvKeyLinkedInClientID)
|
||||
osLinkedInClientSecret := os.Getenv(constants.EnvKeyLinkedInClientSecret)
|
||||
osAppleClientID := os.Getenv(constants.EnvKeyAppleClientID)
|
||||
osAppleClientSecret := os.Getenv(constants.EnvKeyAppleClientSecret)
|
||||
osResetPasswordURL := os.Getenv(constants.EnvKeyResetPasswordURL)
|
||||
osOrganizationName := os.Getenv(constants.EnvKeyOrganizationName)
|
||||
osOrganizationLogo := os.Getenv(constants.EnvKeyOrganizationLogo)
|
||||
|
||||
// os bool vars
|
||||
osDisableBasicAuthentication := os.Getenv(constants.EnvKeyDisableBasicAuthentication)
|
||||
osDisableEmailVerification := os.Getenv(constants.EnvKeyDisableEmailVerification)
|
||||
osDisableMagicLinkLogin := os.Getenv(constants.EnvKeyDisableMagicLinkLogin)
|
||||
osDisableLoginPage := os.Getenv(constants.EnvKeyDisableLoginPage)
|
||||
osDisableSignUp := os.Getenv(constants.EnvKeyDisableSignUp)
|
||||
osDisableRedisForEnv := os.Getenv(constants.EnvKeyDisableRedisForEnv)
|
||||
|
||||
// os slice vars
|
||||
osAllowedOrigins := os.Getenv(constants.EnvKeyAllowedOrigins)
|
||||
osRoles := os.Getenv(constants.EnvKeyRoles)
|
||||
osDefaultRoles := os.Getenv(constants.EnvKeyDefaultRoles)
|
||||
osProtectedRoles := os.Getenv(constants.EnvKeyProtectedRoles)
|
||||
|
||||
ienv, ok := envData[constants.EnvKeyEnv]
|
||||
if !ok || ienv == "" {
|
||||
envData[constants.EnvKeyEnv] = osEnv
|
||||
if envData[constants.EnvKeyEnv] == "" {
|
||||
envData[constants.EnvKeyEnv] = "production"
|
||||
}
|
||||
|
||||
if envData.StringEnv[constants.EnvKeyEnv] == "production" {
|
||||
envData.BoolEnv[constants.EnvKeyIsProd] = true
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
if envData[constants.EnvKeyEnv] == "production" {
|
||||
envData[constants.EnvKeyIsProd] = true
|
||||
} else {
|
||||
envData.BoolEnv[constants.EnvKeyIsProd] = false
|
||||
envData[constants.EnvKeyIsProd] = false
|
||||
}
|
||||
}
|
||||
|
||||
if envData.StringEnv[constants.EnvKeyAppURL] == "" {
|
||||
envData.StringEnv[constants.EnvKeyAppURL] = os.Getenv(constants.EnvKeyAppURL)
|
||||
}
|
||||
|
||||
if envData.StringEnv[constants.EnvKeyPort] == "" {
|
||||
envData.StringEnv[constants.EnvKeyPort] = os.Getenv(constants.EnvKeyPort)
|
||||
if envData.StringEnv[constants.EnvKeyPort] == "" {
|
||||
envData.StringEnv[constants.EnvKeyPort] = "8080"
|
||||
}
|
||||
}
|
||||
|
||||
if envData.StringEnv[constants.EnvKeyAdminSecret] == "" {
|
||||
envData.StringEnv[constants.EnvKeyAdminSecret] = os.Getenv(constants.EnvKeyAdminSecret)
|
||||
}
|
||||
|
||||
if envData.StringEnv[constants.EnvKeySmtpHost] == "" {
|
||||
envData.StringEnv[constants.EnvKeySmtpHost] = os.Getenv(constants.EnvKeySmtpHost)
|
||||
}
|
||||
|
||||
if envData.StringEnv[constants.EnvKeySmtpPort] == "" {
|
||||
envData.StringEnv[constants.EnvKeySmtpPort] = os.Getenv(constants.EnvKeySmtpPort)
|
||||
}
|
||||
|
||||
if envData.StringEnv[constants.EnvKeySmtpUsername] == "" {
|
||||
envData.StringEnv[constants.EnvKeySmtpUsername] = os.Getenv(constants.EnvKeySmtpUsername)
|
||||
}
|
||||
|
||||
if envData.StringEnv[constants.EnvKeySmtpPassword] == "" {
|
||||
envData.StringEnv[constants.EnvKeySmtpPassword] = os.Getenv(constants.EnvKeySmtpPassword)
|
||||
}
|
||||
|
||||
if envData.StringEnv[constants.EnvKeySenderEmail] == "" {
|
||||
envData.StringEnv[constants.EnvKeySenderEmail] = os.Getenv(constants.EnvKeySenderEmail)
|
||||
}
|
||||
|
||||
algo := envData.StringEnv[constants.EnvKeyJwtType]
|
||||
if algo == "" {
|
||||
envData.StringEnv[constants.EnvKeyJwtType] = os.Getenv(constants.EnvKeyJwtType)
|
||||
if envData.StringEnv[constants.EnvKeyJwtType] == "" {
|
||||
envData.StringEnv[constants.EnvKeyJwtType] = "RS256"
|
||||
algo = envData.StringEnv[constants.EnvKeyJwtType]
|
||||
if osEnv != "" && osEnv != envData[constants.EnvKeyEnv] {
|
||||
envData[constants.EnvKeyEnv] = osEnv
|
||||
if envData[constants.EnvKeyEnv] == "production" {
|
||||
envData[constants.EnvKeyIsProd] = true
|
||||
} else {
|
||||
algo = envData.StringEnv[constants.EnvKeyJwtType]
|
||||
if !crypto.IsHMACA(algo) && !crypto.IsRSA(algo) && !crypto.IsECDSA(algo) {
|
||||
return errors.New("invalid JWT_TYPE")
|
||||
}
|
||||
envData[constants.EnvKeyIsProd] = false
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := envData[constants.EnvKeyAppURL]; !ok || val == "" {
|
||||
envData[constants.EnvKeyAppURL] = osAppURL
|
||||
}
|
||||
if osAppURL != "" && envData[constants.EnvKeyAppURL] != osAppURL {
|
||||
envData[constants.EnvKeyAppURL] = osAppURL
|
||||
}
|
||||
|
||||
if val, ok := envData[constants.EnvKeyAuthorizerURL]; !ok || val == "" {
|
||||
envData[constants.EnvKeyAuthorizerURL] = osAuthorizerURL
|
||||
}
|
||||
if osAuthorizerURL != "" && envData[constants.EnvKeyAuthorizerURL] != osAuthorizerURL {
|
||||
envData[constants.EnvKeyAuthorizerURL] = osAuthorizerURL
|
||||
}
|
||||
|
||||
if val, ok := envData[constants.EnvKeyPort]; !ok || val == "" {
|
||||
envData[constants.EnvKeyPort] = osPort
|
||||
if envData[constants.EnvKeyPort] == "" {
|
||||
envData[constants.EnvKeyPort] = "8080"
|
||||
}
|
||||
}
|
||||
if osPort != "" && envData[constants.EnvKeyPort] != osPort {
|
||||
envData[constants.EnvKeyPort] = osPort
|
||||
}
|
||||
|
||||
if val, ok := envData[constants.EnvKeyAccessTokenExpiryTime]; !ok || val == "" {
|
||||
envData[constants.EnvKeyAccessTokenExpiryTime] = osAccessTokenExpiryTime
|
||||
if envData[constants.EnvKeyAccessTokenExpiryTime] == "" {
|
||||
envData[constants.EnvKeyAccessTokenExpiryTime] = "30m"
|
||||
}
|
||||
}
|
||||
if osAccessTokenExpiryTime != "" && envData[constants.EnvKeyAccessTokenExpiryTime] != osAccessTokenExpiryTime {
|
||||
envData[constants.EnvKeyAccessTokenExpiryTime] = osAccessTokenExpiryTime
|
||||
}
|
||||
|
||||
if val, ok := envData[constants.EnvKeyAdminSecret]; !ok || val == "" {
|
||||
envData[constants.EnvKeyAdminSecret] = osAdminSecret
|
||||
}
|
||||
if osAdminSecret != "" && envData[constants.EnvKeyAdminSecret] != osAdminSecret {
|
||||
envData[constants.EnvKeyAdminSecret] = osAdminSecret
|
||||
}
|
||||
|
||||
if val, ok := envData[constants.EnvKeySmtpHost]; !ok || val == "" {
|
||||
envData[constants.EnvKeySmtpHost] = osSmtpHost
|
||||
}
|
||||
if osSmtpHost != "" && envData[constants.EnvKeySmtpHost] != osSmtpHost {
|
||||
envData[constants.EnvKeySmtpHost] = osSmtpHost
|
||||
}
|
||||
|
||||
if val, ok := envData[constants.EnvKeySmtpPort]; !ok || val == "" {
|
||||
envData[constants.EnvKeySmtpPort] = osSmtpPort
|
||||
}
|
||||
if osSmtpPort != "" && envData[constants.EnvKeySmtpPort] != osSmtpPort {
|
||||
envData[constants.EnvKeySmtpPort] = osSmtpPort
|
||||
}
|
||||
|
||||
if val, ok := envData[constants.EnvKeySmtpUsername]; !ok || val == "" {
|
||||
envData[constants.EnvKeySmtpUsername] = osSmtpUsername
|
||||
}
|
||||
if osSmtpUsername != "" && envData[constants.EnvKeySmtpUsername] != osSmtpUsername {
|
||||
envData[constants.EnvKeySmtpUsername] = osSmtpUsername
|
||||
}
|
||||
|
||||
if val, ok := envData[constants.EnvKeySmtpPassword]; !ok || val == "" {
|
||||
envData[constants.EnvKeySmtpPassword] = osSmtpPassword
|
||||
}
|
||||
if osSmtpPassword != "" && envData[constants.EnvKeySmtpPassword] != osSmtpPassword {
|
||||
envData[constants.EnvKeySmtpPassword] = osSmtpPassword
|
||||
}
|
||||
|
||||
if val, ok := envData[constants.EnvKeySenderEmail]; !ok || val == "" {
|
||||
envData[constants.EnvKeySenderEmail] = osSenderEmail
|
||||
}
|
||||
if osSenderEmail != "" && envData[constants.EnvKeySenderEmail] != osSenderEmail {
|
||||
envData[constants.EnvKeySenderEmail] = osSenderEmail
|
||||
}
|
||||
|
||||
algoVal, ok := envData[constants.EnvKeyJwtType]
|
||||
algo := ""
|
||||
if !ok || algoVal == "" {
|
||||
envData[constants.EnvKeyJwtType] = osJwtType
|
||||
if envData[constants.EnvKeyJwtType] == "" {
|
||||
envData[constants.EnvKeyJwtType] = "RS256"
|
||||
algo = envData[constants.EnvKeyJwtType].(string)
|
||||
}
|
||||
} else {
|
||||
algo = algoVal.(string)
|
||||
if !crypto.IsHMACA(algo) && !crypto.IsRSA(algo) && !crypto.IsECDSA(algo) {
|
||||
log.Debug("Invalid JWT Algorithm")
|
||||
return errors.New("invalid JWT_TYPE")
|
||||
}
|
||||
}
|
||||
if osJwtType != "" && osJwtType != algo {
|
||||
if !crypto.IsHMACA(osJwtType) && !crypto.IsRSA(osJwtType) && !crypto.IsECDSA(osJwtType) {
|
||||
log.Debug("Invalid JWT Algorithm")
|
||||
return errors.New("invalid JWT_TYPE")
|
||||
}
|
||||
algo = osJwtType
|
||||
envData[constants.EnvKeyJwtType] = osJwtType
|
||||
}
|
||||
|
||||
if crypto.IsHMACA(algo) {
|
||||
if envData.StringEnv[constants.EnvKeyJwtSecret] == "" {
|
||||
envData.StringEnv[constants.EnvKeyJwtSecret] = os.Getenv(constants.EnvKeyJwtSecret)
|
||||
if envData.StringEnv[constants.EnvKeyJwtSecret] == "" {
|
||||
envData.StringEnv[constants.EnvKeyJwtSecret], _, err = crypto.NewHMACKey(algo, clientID)
|
||||
if val, ok := envData[constants.EnvKeyJwtSecret]; !ok || val == "" {
|
||||
envData[constants.EnvKeyJwtSecret] = osJwtSecret
|
||||
if envData[constants.EnvKeyJwtSecret] == "" {
|
||||
envData[constants.EnvKeyJwtSecret], _, err = crypto.NewHMACKey(algo, clientID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if osJwtSecret != "" && envData[constants.EnvKeyJwtSecret] != osJwtSecret {
|
||||
envData[constants.EnvKeyJwtSecret] = osJwtSecret
|
||||
}
|
||||
}
|
||||
|
||||
if crypto.IsRSA(algo) || crypto.IsECDSA(algo) {
|
||||
privateKey, publicKey := "", ""
|
||||
|
||||
if envData.StringEnv[constants.EnvKeyJwtPrivateKey] == "" {
|
||||
privateKey = os.Getenv(constants.EnvKeyJwtPrivateKey)
|
||||
if val, ok := envData[constants.EnvKeyJwtPrivateKey]; !ok || val == "" {
|
||||
privateKey = osJwtPrivateKey
|
||||
}
|
||||
if osJwtPrivateKey != "" && privateKey != osJwtPrivateKey {
|
||||
privateKey = osJwtPrivateKey
|
||||
}
|
||||
|
||||
if envData.StringEnv[constants.EnvKeyJwtPublicKey] == "" {
|
||||
publicKey = os.Getenv(constants.EnvKeyJwtPublicKey)
|
||||
if val, ok := envData[constants.EnvKeyJwtPublicKey]; !ok || val == "" {
|
||||
publicKey = osJwtPublicKey
|
||||
}
|
||||
if osJwtPublicKey != "" && publicKey != osJwtPublicKey {
|
||||
publicKey = osJwtPublicKey
|
||||
}
|
||||
|
||||
// if algo is RSA / ECDSA, then we need to have both private and public key
|
||||
@@ -221,157 +284,260 @@ func InitAllEnv() error {
|
||||
}
|
||||
}
|
||||
|
||||
envData.StringEnv[constants.EnvKeyJwtPrivateKey] = privateKey
|
||||
envData.StringEnv[constants.EnvKeyJwtPublicKey] = publicKey
|
||||
envData[constants.EnvKeyJwtPrivateKey] = privateKey
|
||||
envData[constants.EnvKeyJwtPublicKey] = publicKey
|
||||
|
||||
}
|
||||
|
||||
if envData.StringEnv[constants.EnvKeyJwtRoleClaim] == "" {
|
||||
envData.StringEnv[constants.EnvKeyJwtRoleClaim] = os.Getenv(constants.EnvKeyJwtRoleClaim)
|
||||
if val, ok := envData[constants.EnvKeyJwtRoleClaim]; !ok || val == "" {
|
||||
envData[constants.EnvKeyJwtRoleClaim] = osJwtRoleClaim
|
||||
|
||||
if envData.StringEnv[constants.EnvKeyJwtRoleClaim] == "" {
|
||||
envData.StringEnv[constants.EnvKeyJwtRoleClaim] = "role"
|
||||
if envData[constants.EnvKeyJwtRoleClaim] == "" {
|
||||
envData[constants.EnvKeyJwtRoleClaim] = "role"
|
||||
}
|
||||
}
|
||||
if osJwtRoleClaim != "" && envData[constants.EnvKeyJwtRoleClaim] != osJwtRoleClaim {
|
||||
envData[constants.EnvKeyJwtRoleClaim] = osJwtRoleClaim
|
||||
}
|
||||
|
||||
if val, ok := envData[constants.EnvKeyCustomAccessTokenScript]; !ok || val == "" {
|
||||
envData[constants.EnvKeyCustomAccessTokenScript] = osCustomAccessTokenScript
|
||||
}
|
||||
if osCustomAccessTokenScript != "" && envData[constants.EnvKeyCustomAccessTokenScript] != osCustomAccessTokenScript {
|
||||
envData[constants.EnvKeyCustomAccessTokenScript] = osCustomAccessTokenScript
|
||||
}
|
||||
|
||||
if val, ok := envData[constants.EnvKeyGoogleClientID]; !ok || val == "" {
|
||||
envData[constants.EnvKeyGoogleClientID] = osGoogleClientID
|
||||
}
|
||||
if osGoogleClientID != "" && envData[constants.EnvKeyGoogleClientID] != osGoogleClientID {
|
||||
envData[constants.EnvKeyGoogleClientID] = osGoogleClientID
|
||||
}
|
||||
|
||||
if val, ok := envData[constants.EnvKeyGoogleClientSecret]; !ok || val == "" {
|
||||
envData[constants.EnvKeyGoogleClientSecret] = osGoogleClientSecret
|
||||
}
|
||||
if osGoogleClientSecret != "" && envData[constants.EnvKeyGoogleClientSecret] != osGoogleClientSecret {
|
||||
envData[constants.EnvKeyGoogleClientSecret] = osGoogleClientSecret
|
||||
}
|
||||
|
||||
if val, ok := envData[constants.EnvKeyGithubClientID]; !ok || val == "" {
|
||||
envData[constants.EnvKeyGithubClientID] = osGithubClientID
|
||||
}
|
||||
if osGithubClientID != "" && envData[constants.EnvKeyGithubClientID] != osGithubClientID {
|
||||
envData[constants.EnvKeyGithubClientID] = osGithubClientID
|
||||
}
|
||||
|
||||
if val, ok := envData[constants.EnvKeyGithubClientSecret]; !ok || val == "" {
|
||||
envData[constants.EnvKeyGithubClientSecret] = osGithubClientSecret
|
||||
}
|
||||
if osGithubClientSecret != "" && envData[constants.EnvKeyGithubClientSecret] != osGithubClientSecret {
|
||||
envData[constants.EnvKeyGithubClientSecret] = osGithubClientSecret
|
||||
}
|
||||
|
||||
if val, ok := envData[constants.EnvKeyFacebookClientID]; !ok || val == "" {
|
||||
envData[constants.EnvKeyFacebookClientID] = osFacebookClientID
|
||||
}
|
||||
if osFacebookClientID != "" && envData[constants.EnvKeyFacebookClientID] != osFacebookClientID {
|
||||
envData[constants.EnvKeyFacebookClientID] = osFacebookClientID
|
||||
}
|
||||
|
||||
if val, ok := envData[constants.EnvKeyFacebookClientSecret]; !ok || val == "" {
|
||||
envData[constants.EnvKeyFacebookClientSecret] = osFacebookClientSecret
|
||||
}
|
||||
if osFacebookClientSecret != "" && envData[constants.EnvKeyFacebookClientSecret] != osFacebookClientSecret {
|
||||
envData[constants.EnvKeyFacebookClientSecret] = osFacebookClientSecret
|
||||
}
|
||||
|
||||
if val, ok := envData[constants.EnvKeyLinkedInClientID]; !ok || val == "" {
|
||||
envData[constants.EnvKeyLinkedInClientID] = osLinkedInClientID
|
||||
}
|
||||
if osFacebookClientID != "" && envData[constants.EnvKeyLinkedInClientID] != osFacebookClientID {
|
||||
envData[constants.EnvKeyLinkedInClientID] = osLinkedInClientID
|
||||
}
|
||||
|
||||
if val, ok := envData[constants.EnvKeyLinkedInClientSecret]; !ok || val == "" {
|
||||
envData[constants.EnvKeyLinkedInClientSecret] = osLinkedInClientSecret
|
||||
}
|
||||
if osFacebookClientSecret != "" && envData[constants.EnvKeyLinkedInClientSecret] != osFacebookClientSecret {
|
||||
envData[constants.EnvKeyLinkedInClientSecret] = osLinkedInClientSecret
|
||||
}
|
||||
|
||||
if val, ok := envData[constants.EnvKeyAppleClientID]; !ok || val == "" {
|
||||
envData[constants.EnvKeyAppleClientID] = osAppleClientID
|
||||
}
|
||||
if osFacebookClientID != "" && envData[constants.EnvKeyAppleClientID] != osFacebookClientID {
|
||||
envData[constants.EnvKeyAppleClientID] = osAppleClientID
|
||||
}
|
||||
|
||||
if val, ok := envData[constants.EnvKeyAppleClientSecret]; !ok || val == "" {
|
||||
envData[constants.EnvKeyAppleClientSecret] = osAppleClientSecret
|
||||
}
|
||||
if osFacebookClientSecret != "" && envData[constants.EnvKeyAppleClientSecret] != osFacebookClientSecret {
|
||||
envData[constants.EnvKeyAppleClientSecret] = osAppleClientSecret
|
||||
}
|
||||
|
||||
if val, ok := envData[constants.EnvKeyResetPasswordURL]; !ok || val == "" {
|
||||
envData[constants.EnvKeyResetPasswordURL] = strings.TrimPrefix(osResetPasswordURL, "/")
|
||||
}
|
||||
if osResetPasswordURL != "" && envData[constants.EnvKeyResetPasswordURL] != osResetPasswordURL {
|
||||
envData[constants.EnvKeyResetPasswordURL] = osResetPasswordURL
|
||||
}
|
||||
|
||||
if val, ok := envData[constants.EnvKeyOrganizationName]; !ok || val == "" {
|
||||
envData[constants.EnvKeyOrganizationName] = osOrganizationName
|
||||
}
|
||||
if osOrganizationName != "" && envData[constants.EnvKeyOrganizationName] != osOrganizationName {
|
||||
envData[constants.EnvKeyOrganizationName] = osOrganizationName
|
||||
}
|
||||
|
||||
if val, ok := envData[constants.EnvKeyOrganizationLogo]; !ok || val == "" {
|
||||
envData[constants.EnvKeyOrganizationLogo] = osOrganizationLogo
|
||||
}
|
||||
if osOrganizationLogo != "" && envData[constants.EnvKeyOrganizationLogo] != osOrganizationLogo {
|
||||
envData[constants.EnvKeyOrganizationLogo] = osOrganizationLogo
|
||||
}
|
||||
|
||||
if _, ok := envData[constants.EnvKeyDisableBasicAuthentication]; !ok {
|
||||
envData[constants.EnvKeyDisableBasicAuthentication] = osDisableBasicAuthentication == "true"
|
||||
}
|
||||
if osDisableBasicAuthentication != "" {
|
||||
boolValue, err := strconv.ParseBool(osDisableBasicAuthentication)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if boolValue != envData[constants.EnvKeyDisableBasicAuthentication].(bool) {
|
||||
envData[constants.EnvKeyDisableBasicAuthentication] = boolValue
|
||||
}
|
||||
}
|
||||
|
||||
if envData.StringEnv[constants.EnvKeyCustomAccessTokenScript] == "" {
|
||||
envData.StringEnv[constants.EnvKeyCustomAccessTokenScript] = os.Getenv(constants.EnvKeyCustomAccessTokenScript)
|
||||
if _, ok := envData[constants.EnvKeyDisableEmailVerification]; !ok {
|
||||
envData[constants.EnvKeyDisableEmailVerification] = osDisableEmailVerification == "true"
|
||||
}
|
||||
|
||||
if envData.StringEnv[constants.EnvKeyRedisURL] == "" {
|
||||
envData.StringEnv[constants.EnvKeyRedisURL] = os.Getenv(constants.EnvKeyRedisURL)
|
||||
}
|
||||
|
||||
if envData.StringEnv[constants.EnvKeyCookieName] == "" {
|
||||
envData.StringEnv[constants.EnvKeyCookieName] = os.Getenv(constants.EnvKeyCookieName)
|
||||
if envData.StringEnv[constants.EnvKeyCookieName] == "" {
|
||||
envData.StringEnv[constants.EnvKeyCookieName] = "authorizer"
|
||||
if osDisableEmailVerification != "" {
|
||||
boolValue, err := strconv.ParseBool(osDisableEmailVerification)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if boolValue != envData[constants.EnvKeyDisableEmailVerification].(bool) {
|
||||
envData[constants.EnvKeyDisableEmailVerification] = boolValue
|
||||
}
|
||||
}
|
||||
|
||||
if envData.StringEnv[constants.EnvKeyGoogleClientID] == "" {
|
||||
envData.StringEnv[constants.EnvKeyGoogleClientID] = os.Getenv(constants.EnvKeyGoogleClientID)
|
||||
if _, ok := envData[constants.EnvKeyDisableMagicLinkLogin]; !ok {
|
||||
envData[constants.EnvKeyDisableMagicLinkLogin] = osDisableMagicLinkLogin == "true"
|
||||
}
|
||||
if osDisableMagicLinkLogin != "" {
|
||||
boolValue, err := strconv.ParseBool(osDisableMagicLinkLogin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if boolValue != envData[constants.EnvKeyDisableMagicLinkLogin].(bool) {
|
||||
envData[constants.EnvKeyDisableMagicLinkLogin] = boolValue
|
||||
}
|
||||
}
|
||||
|
||||
if envData.StringEnv[constants.EnvKeyGoogleClientSecret] == "" {
|
||||
envData.StringEnv[constants.EnvKeyGoogleClientSecret] = os.Getenv(constants.EnvKeyGoogleClientSecret)
|
||||
if _, ok := envData[constants.EnvKeyDisableLoginPage]; !ok {
|
||||
envData[constants.EnvKeyDisableLoginPage] = osDisableLoginPage == "true"
|
||||
}
|
||||
if osDisableLoginPage != "" {
|
||||
boolValue, err := strconv.ParseBool(osDisableLoginPage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if boolValue != envData[constants.EnvKeyDisableLoginPage].(bool) {
|
||||
envData[constants.EnvKeyDisableLoginPage] = boolValue
|
||||
}
|
||||
}
|
||||
|
||||
if envData.StringEnv[constants.EnvKeyGithubClientID] == "" {
|
||||
envData.StringEnv[constants.EnvKeyGithubClientID] = os.Getenv(constants.EnvKeyGithubClientID)
|
||||
if _, ok := envData[constants.EnvKeyDisableSignUp]; !ok {
|
||||
envData[constants.EnvKeyDisableSignUp] = osDisableSignUp == "true"
|
||||
}
|
||||
if osDisableSignUp != "" {
|
||||
boolValue, err := strconv.ParseBool(osDisableSignUp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if boolValue != envData[constants.EnvKeyDisableSignUp].(bool) {
|
||||
envData[constants.EnvKeyDisableSignUp] = boolValue
|
||||
}
|
||||
}
|
||||
|
||||
if envData.StringEnv[constants.EnvKeyGithubClientSecret] == "" {
|
||||
envData.StringEnv[constants.EnvKeyGithubClientSecret] = os.Getenv(constants.EnvKeyGithubClientSecret)
|
||||
if _, ok := envData[constants.EnvKeyDisableRedisForEnv]; !ok {
|
||||
envData[constants.EnvKeyDisableRedisForEnv] = osDisableRedisForEnv == "true"
|
||||
}
|
||||
|
||||
if envData.StringEnv[constants.EnvKeyFacebookClientID] == "" {
|
||||
envData.StringEnv[constants.EnvKeyFacebookClientID] = os.Getenv(constants.EnvKeyFacebookClientID)
|
||||
if osDisableRedisForEnv != "" {
|
||||
boolValue, err := strconv.ParseBool(osDisableRedisForEnv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if boolValue != envData[constants.EnvKeyDisableRedisForEnv].(bool) {
|
||||
envData[constants.EnvKeyDisableRedisForEnv] = boolValue
|
||||
}
|
||||
}
|
||||
|
||||
if envData.StringEnv[constants.EnvKeyFacebookClientSecret] == "" {
|
||||
envData.StringEnv[constants.EnvKeyFacebookClientSecret] = os.Getenv(constants.EnvKeyFacebookClientSecret)
|
||||
}
|
||||
|
||||
if envData.StringEnv[constants.EnvKeyResetPasswordURL] == "" {
|
||||
envData.StringEnv[constants.EnvKeyResetPasswordURL] = strings.TrimPrefix(os.Getenv(constants.EnvKeyResetPasswordURL), "/")
|
||||
}
|
||||
|
||||
envData.BoolEnv[constants.EnvKeyDisableBasicAuthentication] = os.Getenv(constants.EnvKeyDisableBasicAuthentication) == "true"
|
||||
envData.BoolEnv[constants.EnvKeyDisableEmailVerification] = os.Getenv(constants.EnvKeyDisableEmailVerification) == "true"
|
||||
envData.BoolEnv[constants.EnvKeyDisableMagicLinkLogin] = os.Getenv(constants.EnvKeyDisableMagicLinkLogin) == "true"
|
||||
envData.BoolEnv[constants.EnvKeyDisableLoginPage] = os.Getenv(constants.EnvKeyDisableLoginPage) == "true"
|
||||
|
||||
// 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] == "" {
|
||||
envData.BoolEnv[constants.EnvKeyDisableEmailVerification] = true
|
||||
envData.BoolEnv[constants.EnvKeyDisableMagicLinkLogin] = true
|
||||
if envData[constants.EnvKeySmtpHost] == "" || envData[constants.EnvKeySmtpUsername] == "" || envData[constants.EnvKeySmtpPassword] == "" || envData[constants.EnvKeySenderEmail] == "" && envData[constants.EnvKeySmtpPort] == "" {
|
||||
envData[constants.EnvKeyDisableEmailVerification] = true
|
||||
envData[constants.EnvKeyDisableMagicLinkLogin] = true
|
||||
}
|
||||
|
||||
if envData.BoolEnv[constants.EnvKeyDisableEmailVerification] {
|
||||
envData.BoolEnv[constants.EnvKeyDisableMagicLinkLogin] = true
|
||||
if envData[constants.EnvKeyDisableEmailVerification].(bool) {
|
||||
envData[constants.EnvKeyDisableMagicLinkLogin] = true
|
||||
}
|
||||
|
||||
allowedOriginsSplit := strings.Split(os.Getenv(constants.EnvKeyAllowedOrigins), ",")
|
||||
allowedOrigins := []string{}
|
||||
hasWildCard := false
|
||||
if val, ok := envData[constants.EnvKeyAllowedOrigins]; !ok || val == "" {
|
||||
envData[constants.EnvKeyAllowedOrigins] = osAllowedOrigins
|
||||
if envData[constants.EnvKeyAllowedOrigins] == "" {
|
||||
envData[constants.EnvKeyAllowedOrigins] = "*"
|
||||
}
|
||||
}
|
||||
if osAllowedOrigins != "" && envData[constants.EnvKeyAllowedOrigins] != osAllowedOrigins {
|
||||
envData[constants.EnvKeyAllowedOrigins] = osAllowedOrigins
|
||||
}
|
||||
|
||||
for _, val := range allowedOriginsSplit {
|
||||
trimVal := strings.TrimSpace(val)
|
||||
if trimVal != "" {
|
||||
if trimVal != "*" {
|
||||
host, port := utils.GetHostParts(trimVal)
|
||||
allowedOrigins = append(allowedOrigins, host+":"+port)
|
||||
} else {
|
||||
hasWildCard = true
|
||||
allowedOrigins = append(allowedOrigins, trimVal)
|
||||
break
|
||||
}
|
||||
if val, ok := envData[constants.EnvKeyRoles]; !ok || val == "" {
|
||||
envData[constants.EnvKeyRoles] = osRoles
|
||||
if envData[constants.EnvKeyRoles] == "" {
|
||||
envData[constants.EnvKeyRoles] = "user"
|
||||
}
|
||||
}
|
||||
if osRoles != "" && envData[constants.EnvKeyRoles] != osRoles {
|
||||
envData[constants.EnvKeyRoles] = osRoles
|
||||
}
|
||||
roles := strings.Split(envData[constants.EnvKeyRoles].(string), ",")
|
||||
|
||||
if val, ok := envData[constants.EnvKeyDefaultRoles]; !ok || val == "" {
|
||||
envData[constants.EnvKeyDefaultRoles] = osDefaultRoles
|
||||
if envData[constants.EnvKeyDefaultRoles] == "" {
|
||||
envData[constants.EnvKeyDefaultRoles] = "user"
|
||||
}
|
||||
}
|
||||
if osDefaultRoles != "" && envData[constants.EnvKeyDefaultRoles] != osDefaultRoles {
|
||||
envData[constants.EnvKeyDefaultRoles] = osDefaultRoles
|
||||
}
|
||||
defaultRoles := strings.Split(envData[constants.EnvKeyDefaultRoles].(string), ",")
|
||||
if len(defaultRoles) == 0 {
|
||||
defaultRoles = []string{roles[0]}
|
||||
}
|
||||
|
||||
for _, role := range defaultRoles {
|
||||
if !utils.StringSliceContains(roles, role) {
|
||||
return fmt.Errorf("Default role %s is not defined in roles", role)
|
||||
}
|
||||
}
|
||||
|
||||
if len(allowedOrigins) > 1 && hasWildCard {
|
||||
allowedOrigins = []string{"*"}
|
||||
if val, ok := envData[constants.EnvKeyProtectedRoles]; !ok || val == "" {
|
||||
envData[constants.EnvKeyProtectedRoles] = osProtectedRoles
|
||||
}
|
||||
if osProtectedRoles != "" && envData[constants.EnvKeyProtectedRoles] != osProtectedRoles {
|
||||
envData[constants.EnvKeyProtectedRoles] = osProtectedRoles
|
||||
}
|
||||
|
||||
if len(allowedOrigins) == 0 {
|
||||
allowedOrigins = []string{"*"}
|
||||
err = memorystore.Provider.UpdateEnvStore(envData)
|
||||
if err != nil {
|
||||
log.Debug("Error while updating env store: ", err)
|
||||
return err
|
||||
}
|
||||
|
||||
envData.SliceEnv[constants.EnvKeyAllowedOrigins] = allowedOrigins
|
||||
|
||||
rolesEnv := strings.TrimSpace(os.Getenv(constants.EnvKeyRoles))
|
||||
rolesSplit := strings.Split(rolesEnv, ",")
|
||||
roles := []string{}
|
||||
if len(rolesEnv) == 0 {
|
||||
roles = []string{"user"}
|
||||
}
|
||||
|
||||
defaultRolesEnv := strings.TrimSpace(os.Getenv(constants.EnvKeyDefaultRoles))
|
||||
defaultRoleSplit := strings.Split(defaultRolesEnv, ",")
|
||||
defaultRoles := []string{}
|
||||
|
||||
if len(defaultRolesEnv) == 0 {
|
||||
defaultRoles = []string{"user"}
|
||||
}
|
||||
|
||||
protectedRolesEnv := strings.TrimSpace(os.Getenv(constants.EnvKeyProtectedRoles))
|
||||
protectedRolesSplit := strings.Split(protectedRolesEnv, ",")
|
||||
protectedRoles := []string{}
|
||||
|
||||
if len(protectedRolesEnv) > 0 {
|
||||
for _, val := range protectedRolesSplit {
|
||||
trimVal := strings.TrimSpace(val)
|
||||
protectedRoles = append(protectedRoles, trimVal)
|
||||
}
|
||||
}
|
||||
|
||||
for _, val := range rolesSplit {
|
||||
trimVal := strings.TrimSpace(val)
|
||||
if trimVal != "" {
|
||||
roles = append(roles, trimVal)
|
||||
if utils.StringSliceContains(defaultRoleSplit, trimVal) {
|
||||
defaultRoles = append(defaultRoles, trimVal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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`)
|
||||
}
|
||||
|
||||
envData.SliceEnv[constants.EnvKeyRoles] = roles
|
||||
envData.SliceEnv[constants.EnvKeyDefaultRoles] = defaultRoles
|
||||
envData.SliceEnv[constants.EnvKeyProtectedRoles] = protectedRoles
|
||||
|
||||
if os.Getenv(constants.EnvKeyOrganizationName) != "" {
|
||||
envData.StringEnv[constants.EnvKeyOrganizationName] = os.Getenv(constants.EnvKeyOrganizationName)
|
||||
}
|
||||
|
||||
if os.Getenv(constants.EnvKeyOrganizationLogo) != "" {
|
||||
envData.StringEnv[constants.EnvKeyOrganizationLogo] = os.Getenv(constants.EnvKeyOrganizationLogo)
|
||||
}
|
||||
|
||||
envstore.EnvStoreObj.UpdateEnvStore(envData)
|
||||
return nil
|
||||
}
|
||||
|
173
server/env/persist_env.go
vendored
173
server/env/persist_env.go
vendored
@@ -2,53 +2,107 @@ package env
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/crypto"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
)
|
||||
|
||||
func fixBackwardCompatibility(data map[string]interface{}) (bool, map[string]interface{}) {
|
||||
result := data
|
||||
// check if env data is stored in older format
|
||||
hasOlderFormat := false
|
||||
if _, ok := result["bool_env"]; ok {
|
||||
for key, value := range result["bool_env"].(map[string]interface{}) {
|
||||
result[key] = value
|
||||
}
|
||||
hasOlderFormat = true
|
||||
delete(result, "bool_env")
|
||||
}
|
||||
|
||||
if _, ok := result["string_env"]; ok {
|
||||
for key, value := range result["string_env"].(map[string]interface{}) {
|
||||
result[key] = value
|
||||
}
|
||||
hasOlderFormat = true
|
||||
delete(result, "string_env")
|
||||
}
|
||||
|
||||
if _, ok := result["slice_env"]; ok {
|
||||
for key, value := range result["slice_env"].(map[string]interface{}) {
|
||||
typeOfValue := reflect.TypeOf(value)
|
||||
if strings.Contains(typeOfValue.String(), "[]string") {
|
||||
result[key] = strings.Join(value.([]string), ",")
|
||||
}
|
||||
if strings.Contains(typeOfValue.String(), "[]interface") {
|
||||
result[key] = strings.Join(utils.ConvertInterfaceToStringSlice(value), ",")
|
||||
}
|
||||
}
|
||||
hasOlderFormat = true
|
||||
delete(result, "slice_env")
|
||||
}
|
||||
|
||||
return hasOlderFormat, result
|
||||
}
|
||||
|
||||
// GetEnvData returns the env data from database
|
||||
func GetEnvData() (envstore.Store, error) {
|
||||
var result envstore.Store
|
||||
func GetEnvData() (map[string]interface{}, error) {
|
||||
var result map[string]interface{}
|
||||
env, err := db.Provider.GetEnv()
|
||||
// config not found in db
|
||||
if err != nil {
|
||||
log.Debug("Error while getting env data from db: ", err)
|
||||
return result, err
|
||||
}
|
||||
|
||||
encryptionKey := env.Hash
|
||||
decryptedEncryptionKey, err := crypto.DecryptB64(encryptionKey)
|
||||
if err != nil {
|
||||
log.Debug("Error while decrypting encryption key: ", err)
|
||||
return result, err
|
||||
}
|
||||
|
||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyEncryptionKey, decryptedEncryptionKey)
|
||||
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyEncryptionKey, decryptedEncryptionKey)
|
||||
|
||||
b64DecryptedConfig, err := crypto.DecryptB64(env.EnvData)
|
||||
if err != nil {
|
||||
log.Debug("Error while decrypting env data from B64: ", err)
|
||||
return result, err
|
||||
}
|
||||
|
||||
decryptedConfigs, err := crypto.DecryptAESEnv([]byte(b64DecryptedConfig))
|
||||
if err != nil {
|
||||
log.Debug("Error while decrypting env data from AES: ", err)
|
||||
return result, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(decryptedConfigs, &result)
|
||||
if err != nil {
|
||||
log.Debug("Error while unmarshalling env data: ", err)
|
||||
return result, err
|
||||
}
|
||||
|
||||
hasOlderFormat, result := fixBackwardCompatibility(result)
|
||||
|
||||
if hasOlderFormat {
|
||||
err = memorystore.Provider.UpdateEnvStore(result)
|
||||
if err != nil {
|
||||
log.Debug("Error while updating env store: ", err)
|
||||
return result, err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
@@ -59,11 +113,22 @@ func PersistEnv() error {
|
||||
if err != nil {
|
||||
// AES encryption needs 32 bit key only, so we chop off last 4 characters from 36 bit uuid
|
||||
hash := uuid.New().String()[:36-4]
|
||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyEncryptionKey, hash)
|
||||
err := memorystore.Provider.UpdateEnvVariable(constants.EnvKeyEncryptionKey, hash)
|
||||
if err != nil {
|
||||
log.Debug("Error while updating encryption env variable: ", err)
|
||||
return err
|
||||
}
|
||||
encodedHash := crypto.EncryptB64(hash)
|
||||
|
||||
encryptedConfig, err := crypto.EncryptEnvData(envstore.EnvStoreObj.GetEnvStoreClone())
|
||||
res, err := memorystore.Provider.GetEnvStore()
|
||||
if err != nil {
|
||||
log.Debug("Error while getting env store: ", err)
|
||||
return err
|
||||
}
|
||||
|
||||
encryptedConfig, err := crypto.EncryptEnvData(res)
|
||||
if err != nil {
|
||||
log.Debug("Error while encrypting env data: ", err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -74,6 +139,7 @@ func PersistEnv() error {
|
||||
|
||||
env, err = db.Provider.AddEnv(env)
|
||||
if err != nil {
|
||||
log.Debug("Error while persisting env data to db: ", err)
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
@@ -82,107 +148,112 @@ func PersistEnv() error {
|
||||
encryptionKey := env.Hash
|
||||
decryptedEncryptionKey, err := crypto.DecryptB64(encryptionKey)
|
||||
if err != nil {
|
||||
log.Debug("Error while decrypting encryption key: ", err)
|
||||
return err
|
||||
}
|
||||
|
||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyEncryptionKey, decryptedEncryptionKey)
|
||||
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyEncryptionKey, decryptedEncryptionKey)
|
||||
|
||||
b64DecryptedConfig, err := crypto.DecryptB64(env.EnvData)
|
||||
if err != nil {
|
||||
log.Debug("Error while decrypting env data from B64: ", err)
|
||||
return err
|
||||
}
|
||||
|
||||
decryptedConfigs, err := crypto.DecryptAESEnv([]byte(b64DecryptedConfig))
|
||||
if err != nil {
|
||||
log.Debug("Error while decrypting env data from AES: ", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// temp store variable
|
||||
var storeData envstore.Store
|
||||
storeData := map[string]interface{}{}
|
||||
|
||||
err = json.Unmarshal(decryptedConfigs, &storeData)
|
||||
if err != nil {
|
||||
log.Debug("Error while unmarshalling env data: ", err)
|
||||
return err
|
||||
}
|
||||
|
||||
hasOlderFormat, result := fixBackwardCompatibility(storeData)
|
||||
if hasOlderFormat {
|
||||
err = memorystore.Provider.UpdateEnvStore(result)
|
||||
if err != nil {
|
||||
log.Debug("Error while updating env store: ", err)
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// if env is changed via env file or OS env
|
||||
// give that higher preference and update db, but we don't recommend it
|
||||
|
||||
hasChanged := false
|
||||
|
||||
for key, value := range storeData.StringEnv {
|
||||
for key, value := range storeData {
|
||||
// don't override unexposed envs
|
||||
// check only for derivative keys
|
||||
// No need to check for ENCRYPTION_KEY which special key we use for encrypting config data
|
||||
// as we have removed it from json
|
||||
if key != constants.EnvKeyEncryptionKey {
|
||||
// check only for derivative keys
|
||||
// No need to check for ENCRYPTION_KEY which special key we use for encrypting config data
|
||||
// as we have removed it from json
|
||||
envValue := strings.TrimSpace(os.Getenv(key))
|
||||
|
||||
// env is not empty
|
||||
if envValue != "" {
|
||||
if value != envValue {
|
||||
storeData.StringEnv[key] = envValue
|
||||
hasChanged = true
|
||||
switch key {
|
||||
case constants.EnvKeyIsProd, constants.EnvKeyDisableBasicAuthentication, constants.EnvKeyDisableEmailVerification, constants.EnvKeyDisableLoginPage, constants.EnvKeyDisableMagicLinkLogin, constants.EnvKeyDisableSignUp, constants.EnvKeyDisableRedisForEnv:
|
||||
if envValueBool, err := strconv.ParseBool(envValue); err == nil {
|
||||
if value.(bool) != envValueBool {
|
||||
storeData[key] = envValueBool
|
||||
hasChanged = true
|
||||
}
|
||||
}
|
||||
default:
|
||||
if value != nil && value.(string) != envValue {
|
||||
storeData[key] = envValue
|
||||
hasChanged = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for key, value := range storeData.BoolEnv {
|
||||
envValue := strings.TrimSpace(os.Getenv(key))
|
||||
// env is not empty
|
||||
if envValue != "" {
|
||||
envValueBool, _ := strconv.ParseBool(envValue)
|
||||
if value != envValueBool {
|
||||
storeData.BoolEnv[key] = envValueBool
|
||||
hasChanged = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for key, value := range storeData.SliceEnv {
|
||||
envValue := strings.TrimSpace(os.Getenv(key))
|
||||
// env is not empty
|
||||
if envValue != "" {
|
||||
envStringArr := strings.Split(envValue, ",")
|
||||
if !utils.IsStringArrayEqual(value, envStringArr) {
|
||||
storeData.SliceEnv[key] = envStringArr
|
||||
hasChanged = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handle derivative cases like disabling email verification & magic login
|
||||
// in case SMTP is off but env is set to true
|
||||
if storeData.StringEnv[constants.EnvKeySmtpHost] == "" || storeData.StringEnv[constants.EnvKeySmtpUsername] == "" || storeData.StringEnv[constants.EnvKeySmtpPassword] == "" || storeData.StringEnv[constants.EnvKeySenderEmail] == "" && storeData.StringEnv[constants.EnvKeySmtpPort] == "" {
|
||||
if !storeData.BoolEnv[constants.EnvKeyDisableEmailVerification] {
|
||||
storeData.BoolEnv[constants.EnvKeyDisableEmailVerification] = true
|
||||
if storeData[constants.EnvKeySmtpHost] == "" || storeData[constants.EnvKeySmtpUsername] == "" || storeData[constants.EnvKeySmtpPassword] == "" || storeData[constants.EnvKeySenderEmail] == "" && storeData[constants.EnvKeySmtpPort] == "" {
|
||||
if !storeData[constants.EnvKeyDisableEmailVerification].(bool) {
|
||||
storeData[constants.EnvKeyDisableEmailVerification] = true
|
||||
hasChanged = true
|
||||
}
|
||||
|
||||
if !storeData.BoolEnv[constants.EnvKeyDisableMagicLinkLogin] {
|
||||
storeData.BoolEnv[constants.EnvKeyDisableMagicLinkLogin] = true
|
||||
if !storeData[constants.EnvKeyDisableMagicLinkLogin].(bool) {
|
||||
storeData[constants.EnvKeyDisableMagicLinkLogin] = true
|
||||
hasChanged = true
|
||||
}
|
||||
}
|
||||
envstore.EnvStoreObj.UpdateEnvStore(storeData)
|
||||
|
||||
err = memorystore.Provider.UpdateEnvStore(storeData)
|
||||
if err != nil {
|
||||
log.Debug("Error while updating env store: ", err)
|
||||
return err
|
||||
}
|
||||
|
||||
jwk, err := crypto.GenerateJWKBasedOnEnv()
|
||||
if err != nil {
|
||||
log.Debug("Error while generating JWK: ", err)
|
||||
return err
|
||||
}
|
||||
// updating jwk
|
||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyJWK, jwk)
|
||||
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyJWK, jwk)
|
||||
|
||||
if hasChanged {
|
||||
encryptedConfig, err := crypto.EncryptEnvData(storeData)
|
||||
if err != nil {
|
||||
log.Debug("Error while encrypting env data: ", err)
|
||||
return err
|
||||
}
|
||||
|
||||
env.EnvData = encryptedConfig
|
||||
_, err = db.Provider.UpdateEnv(env)
|
||||
if err != nil {
|
||||
log.Println("error updating config:", err)
|
||||
log.Debug("Failed to Update Config: ", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@@ -1,119 +0,0 @@
|
||||
package envstore
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
)
|
||||
|
||||
var (
|
||||
// ARG_DB_URL is the cli arg variable for the database url
|
||||
ARG_DB_URL *string
|
||||
// ARG_DB_TYPE is the cli arg variable for the database type
|
||||
ARG_DB_TYPE *string
|
||||
// ARG_ENV_FILE is the cli arg variable for the env file
|
||||
ARG_ENV_FILE *string
|
||||
)
|
||||
|
||||
// Store data structure
|
||||
type Store struct {
|
||||
StringEnv map[string]string `json:"string_env"`
|
||||
BoolEnv map[string]bool `json:"bool_env"`
|
||||
SliceEnv map[string][]string `json:"slice_env"`
|
||||
}
|
||||
|
||||
// EnvStore struct
|
||||
type EnvStore struct {
|
||||
mutex sync.Mutex
|
||||
store *Store
|
||||
}
|
||||
|
||||
var defaultStore = &EnvStore{
|
||||
store: &Store{
|
||||
StringEnv: map[string]string{
|
||||
constants.EnvKeyAdminCookieName: "authorizer-admin",
|
||||
constants.EnvKeyJwtRoleClaim: "role",
|
||||
constants.EnvKeyOrganizationName: "Authorizer",
|
||||
constants.EnvKeyOrganizationLogo: "https://www.authorizer.dev/images/logo.png",
|
||||
},
|
||||
BoolEnv: map[string]bool{
|
||||
constants.EnvKeyDisableBasicAuthentication: false,
|
||||
constants.EnvKeyDisableMagicLinkLogin: false,
|
||||
constants.EnvKeyDisableEmailVerification: false,
|
||||
constants.EnvKeyDisableLoginPage: false,
|
||||
},
|
||||
SliceEnv: map[string][]string{},
|
||||
},
|
||||
}
|
||||
|
||||
// EnvStoreObj.GetBoolStoreEnvVariable global variable for EnvStore
|
||||
var EnvStoreObj = defaultStore
|
||||
|
||||
// UpdateEnvStore to update the whole env store object
|
||||
func (e *EnvStore) UpdateEnvStore(store Store) {
|
||||
e.mutex.Lock()
|
||||
defer e.mutex.Unlock()
|
||||
// just override the keys + new keys
|
||||
|
||||
for key, value := range store.StringEnv {
|
||||
e.store.StringEnv[key] = value
|
||||
}
|
||||
|
||||
for key, value := range store.BoolEnv {
|
||||
e.store.BoolEnv[key] = value
|
||||
}
|
||||
|
||||
for key, value := range store.SliceEnv {
|
||||
e.store.SliceEnv[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateEnvVariable to update the particular env variable
|
||||
func (e *EnvStore) UpdateEnvVariable(storeIdentifier, key string, value interface{}) {
|
||||
e.mutex.Lock()
|
||||
defer e.mutex.Unlock()
|
||||
switch storeIdentifier {
|
||||
case constants.StringStoreIdentifier:
|
||||
e.store.StringEnv[key] = value.(string)
|
||||
case constants.BoolStoreIdentifier:
|
||||
e.store.BoolEnv[key] = value.(bool)
|
||||
case constants.SliceStoreIdentifier:
|
||||
e.store.SliceEnv[key] = value.([]string)
|
||||
}
|
||||
}
|
||||
|
||||
// GetStringStoreEnvVariable to get the env variable from string store object
|
||||
func (e *EnvStore) GetStringStoreEnvVariable(key string) string {
|
||||
// e.mutex.Lock()
|
||||
// defer e.mutex.Unlock()
|
||||
return e.store.StringEnv[key]
|
||||
}
|
||||
|
||||
// GetBoolStoreEnvVariable to get the env variable from bool store object
|
||||
func (e *EnvStore) GetBoolStoreEnvVariable(key string) bool {
|
||||
// e.mutex.Lock()
|
||||
// defer e.mutex.Unlock()
|
||||
return e.store.BoolEnv[key]
|
||||
}
|
||||
|
||||
// GetSliceStoreEnvVariable to get the env variable from slice store object
|
||||
func (e *EnvStore) GetSliceStoreEnvVariable(key string) []string {
|
||||
// e.mutex.Lock()
|
||||
// defer e.mutex.Unlock()
|
||||
return e.store.SliceEnv[key]
|
||||
}
|
||||
|
||||
// GetEnvStoreClone to get clone of current env store object
|
||||
func (e *EnvStore) GetEnvStoreClone() Store {
|
||||
e.mutex.Lock()
|
||||
defer e.mutex.Unlock()
|
||||
|
||||
result := *e.store
|
||||
return result
|
||||
}
|
||||
|
||||
func (e *EnvStore) ResetStore() {
|
||||
e.mutex.Lock()
|
||||
defer e.mutex.Unlock()
|
||||
e.store = defaultStore.store
|
||||
}
|
@@ -9,6 +9,7 @@ require (
|
||||
github.com/gin-gonic/gin v1.7.2
|
||||
github.com/go-playground/validator/v10 v10.8.0 // indirect
|
||||
github.com/go-redis/redis/v8 v8.11.0
|
||||
github.com/gocql/gocql v1.0.0
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/uuid v1.3.0
|
||||
@@ -19,6 +20,7 @@ require (
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||
github.com/robertkrimen/otto v0.0.0-20211024170158-b87d35c0b86f
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/ugorji/go v1.2.6 // indirect
|
||||
github.com/vektah/gqlparser/v2 v2.2.0
|
||||
|
@@ -48,6 +48,10 @@ github.com/arangodb/go-velocypack v0.0.0-20200318135517-5af53c29c67e h1:Xg+hGrY2
|
||||
github.com/arangodb/go-velocypack v0.0.0-20200318135517-5af53c29c67e/go.mod h1:mq7Shfa/CaixoDxiyAAc5jZ6CVBAyPaNQCGS7mkj4Ho=
|
||||
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
|
||||
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
|
||||
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY=
|
||||
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
@@ -106,6 +110,8 @@ github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfC
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gocql/gocql v1.0.0 h1:UnbTERpP72VZ/viKE1Q1gPtmLvyTZTvuAstvSRydw/c=
|
||||
github.com/gocql/gocql v1.0.0/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
@@ -140,8 +146,9 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
@@ -175,6 +182,8 @@ github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB
|
||||
github.com/gorilla/mux v1.6.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
@@ -322,6 +331,8 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV
|
||||
github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
@@ -677,6 +688,8 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/mail.v2 v2.3.1 h1:WYFn/oANrAGP2C0dcV6/pbkPzv8yGzqTjPmTeO7qoXk=
|
||||
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=
|
||||
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user