Compare commits

..

1 Commits

Author SHA1 Message Date
Lakhan Samani
25cb9a831b feat: add cursor for pagination 2022-09-26 23:50:24 +05:30
295 changed files with 9809 additions and 28576 deletions

View File

@ -2,4 +2,3 @@ ENV=production
DATABASE_URL=data.db DATABASE_URL=data.db
DATABASE_TYPE=sqlite DATABASE_TYPE=sqlite
CUSTOM_ACCESS_TOKEN_SCRIPT="function(user,tokenPayload){var data = tokenPayload;data.extra = {'x-extra-id': user.id};return data;}" CUSTOM_ACCESS_TOKEN_SCRIPT="function(user,tokenPayload){var data = tokenPayload;data.extra = {'x-extra-id': user.id};return data;}"
DISABLE_PLAYGROUND=true

View File

@ -7,9 +7,3 @@ SMTP_PORT=2525
SMTP_USERNAME=test SMTP_USERNAME=test
SMTP_PASSWORD=test SMTP_PASSWORD=test
SENDER_EMAIL="info@authorizer.dev" SENDER_EMAIL="info@authorizer.dev"
TWILIO_API_KEY=test
TWILIO_API_SECRET=test
TWILIO_ACCOUNT_SID=ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
TWILIO_SENDER=909921212112
SENDER_NAME="Authorizer"
AWS_REGION=ap-south-1

View File

@ -1,36 +0,0 @@
name: "deploy"
on: [push]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Cloning repo
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Get Repo Name
id: repo_name
run: echo "::set-output name=repo::$(echo ${GITHUB_REPOSITORY##*/})"
- name: Get Branch Name
id: branch_name
run: echo "::set-output name=branch::$(echo ${GITHUB_REF##*/})"
- name: Push branch 'discours-dev' to staging
if: steps.branch_name.outputs.branch == 'discours-dev'
uses: dokku/github-action@master
with:
branch: "main"
git_remote_url: "ssh://dokku@staging.discours.io:22/authorizer"
ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Push branch 'discours' to v2.discours.io
if: steps.branch_name.outputs.branch == 'discours'
uses: dokku/github-action@master
with:
branch: "main"
git_remote_url: "ssh://dokku@v2.discours.io:22/authorizer"
ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }}
git_push_flags: '--force'

View File

@ -45,30 +45,12 @@ Please ask as many questions as you need, either directly in the issue or on [Di
1. Fork the [authorizer](https://github.com/authorizerdev/authorizer) repository (**Skip this step if you have access to repo**) 1. Fork the [authorizer](https://github.com/authorizerdev/authorizer) repository (**Skip this step if you have access to repo**)
2. Clone repo: `git clone https://github.com/authorizerdev/authorizer.git` or use the forked url from step 1 2. 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` 3. Change directory to authorizer: `cd authorizer`
4. Create Env file `cp .env.sample .env`. Check all the supported env [here](https://docs.authorizer.dev/core/env/) 5. 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 Dashboard `make build-dashboard`
6. Build App `make build-app` 7. Build App `make build-app`
7. Build Server `make clean && make` 8. Build Server `make clean && make`
> Note: if you don't have [`make`](https://www.ibm.com/docs/en/aix/7.2?topic=concepts-make-command), you can `cd` into `server` dir and build using the `go build` command. In that case you will have to build `dashboard` & `app` manually using `npm run build` on both dirs. > 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. In that case you will have to build `dashboard` & `app` manually using `npm run build` on both dirs.
8. Run binary `./build/server` 9. Run binary `./build/server`
### Updating GraphQL schema
- Modify `server/graph/schema.graphqls` file
- Run `make generate-graphql` this will update the models and required methods
- If a new mutation or query is added
- Write the implementation for the new resolver in `server/resolvers/NEW_RESOLVER.GO`
- Update `server/graph/schema.resolvers.go` with the new resolver method
### Adding support for new database
- Run `make generate-db-template dbname=NEW_DB_NAME`
eg `make generate-db-template dbname=dynamodb`
This command will generate a folder in server/db/providers/ with name specified in the above command.
One will have to implement methods present in that folder.
> Note: Connection for database and schema changes are written in `server/db/providers/DB_NAME/provider.go` > `NewProvider` method is called for any given db based on the env variables present.
### Testing ### Testing

3
.github/FUNDING.yml vendored
View File

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

View File

@ -19,27 +19,19 @@ on:
jobs: jobs:
releases: releases:
name: Release Authorizer name: Release Authorizer Binary
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
- uses: actions/setup-node@v2 - uses: actions/setup-node@v2
with: with:
node-version: '16' node-version: '16'
- # Add support for more platforms with QEMU (optional)
# https://github.com/docker/setup-qemu-action
name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
with:
platforms: linux/amd64,linux/arm64
- uses: actions/setup-go@v2 - uses: actions/setup-go@v2
with: with:
go-version: '^1.19.1' go-version: '^1.17.3'
- name: Install dependencies - name: Install dependencies
run: | run: |
sudo apt-get install build-essential wget zip libc6-dev-arm64-cross && \ sudo apt-get install build-essential wget zip gcc-mingw-w64 && \
echo "/usr/bin/x86_64-w64-mingw32-gcc" >> GITHUB_PATH && \ echo "/usr/bin/x86_64-w64-mingw32-gcc" >> GITHUB_PATH && \
wget --no-check-certificate --progress=dot:mega https://github.com/wangyoucao577/assets-uploader/releases/download/v0.3.0/github-assets-uploader-v0.3.0-linux-amd64.tar.gz -O github-assets-uploader.tar.gz && \ wget --no-check-certificate --progress=dot:mega https://github.com/wangyoucao577/assets-uploader/releases/download/v0.3.0/github-assets-uploader-v0.3.0-linux-amd64.tar.gz -O github-assets-uploader.tar.gz && \
tar -zxf github-assets-uploader.tar.gz && \ tar -zxf github-assets-uploader.tar.gz && \
@ -52,28 +44,25 @@ jobs:
run: whereis go run: whereis go
- name: Print Go Version - name: Print Go Version
run: go version run: go version
- name: Install gox
run: go install github.com/mitchellh/gox@latest
- name: Set VERSION env - name: Set VERSION env
run: echo VERSION=$(basename ${GITHUB_REF}) >> ${GITHUB_ENV} run: echo VERSION=$(basename ${GITHUB_REF}) >> ${GITHUB_ENV}
- name: Copy .env file - name: Copy .env file
run: mv .env.sample .env run: mv .env.sample .env
- name: Build package - name: Package files for windows
run: | run: |
make clean && \ make clean && \
make build && \ CGO_ENABLED=1 GOOS=windows CC=/usr/bin/x86_64-w64-mingw32-gcc make && \
mkdir -p authorizer-${VERSION}-darwin-arm64/build authorizer-${VERSION}-darwin-arm64/app authorizer-${VERSION}-darwin-arm64/dashboard && cp build/darwin/arm64/server authorizer-${VERSION}-darwin-arm64/build/ && cp .env authorizer-${VERSION}-darwin-arm64/.env && cp -rf app/build authorizer-${VERSION}-darwin-arm64/app/build && cp -rf templates authorizer-${VERSION}-darwin-arm64/ && cp -rf dashboard/build authorizer-${VERSION}-darwin-arm64/dashboard/build && tar cvfz authorizer-${VERSION}-darwin-arm64.tar.gz authorizer-${VERSION}-darwin-arm64 && \ mv build/server build/server.exe && \
mkdir -p authorizer-${VERSION}-darwin-amd64/build authorizer-${VERSION}-darwin-amd64/app authorizer-${VERSION}-darwin-amd64/dashboard && cp build/darwin/amd64/server authorizer-${VERSION}-darwin-amd64/build/ && cp .env authorizer-${VERSION}-darwin-amd64/.env && cp -rf app/build authorizer-${VERSION}-darwin-amd64/app/build && cp -rf templates authorizer-${VERSION}-darwin-amd64/ && cp -rf dashboard/build authorizer-${VERSION}-darwin-amd64/dashboard/build && tar cvfz authorizer-${VERSION}-darwin-amd64.tar.gz authorizer-${VERSION}-darwin-amd64 && \ zip -vr authorizer-${VERSION}-windows-amd64.zip .env app/build build templates dashboard/build
mkdir -p authorizer-${VERSION}-linux-amd64/build authorizer-${VERSION}-linux-amd64/app authorizer-${VERSION}-linux-amd64/dashboard && cp build/linux/amd64/server authorizer-${VERSION}-linux-amd64/build/ && cp .env authorizer-${VERSION}-linux-amd64/.env && cp -rf app/build authorizer-${VERSION}-linux-amd64/app/build && cp -rf templates authorizer-${VERSION}-linux-amd64/ && cp -rf dashboard/build authorizer-${VERSION}-linux-amd64/dashboard/build && tar cvfz authorizer-${VERSION}-linux-amd64.tar.gz authorizer-${VERSION}-linux-amd64 && \ - name: Package files for linux
mkdir -p authorizer-${VERSION}-linux-arm64/build authorizer-${VERSION}-linux-arm64/app authorizer-${VERSION}-linux-arm64/dashboard && cp build/linux/arm64/server authorizer-${VERSION}-linux-arm64/build/ && cp .env authorizer-${VERSION}-linux-arm64/.env && cp -rf app/build authorizer-${VERSION}-linux-arm64/app/build && cp -rf templates authorizer-${VERSION}-linux-arm64/ && cp -rf dashboard/build authorizer-${VERSION}-linux-arm64/dashboard/build && tar cvfz authorizer-${VERSION}-linux-arm64.tar.gz authorizer-${VERSION}-linux-arm64 && \ run: |
mkdir -p authorizer-${VERSION}-windows-amd64/build authorizer-${VERSION}-windows-amd64/app authorizer-${VERSION}-windows-amd64/dashboard && cp build/windows/amd64/server.exe authorizer-${VERSION}-windows-amd64/build/ && cp .env authorizer-${VERSION}-windows-amd64/.env && cp -rf app/build authorizer-${VERSION}-windows-amd64/app/build && cp -rf templates authorizer-${VERSION}-windows-amd64/ && cp -rf dashboard/build authorizer-${VERSION}-windows-amd64/dashboard/build && zip -vr authorizer-${VERSION}-windows-amd64.zip authorizer-${VERSION}-windows-amd64 make clean && \
CGO_ENABLED=1 make && \
tar cvfz authorizer-${VERSION}-linux-amd64.tar.gz .env app/build build templates dashboard/build
- name: Upload assets - name: Upload assets
run: | run: |
github-assets-uploader -f authorizer-${VERSION}-darwin-arm64.tar.gz -mediatype application/gzip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION} github-assets-uploader -f authorizer-${VERSION}-windows-amd64.zip -mediatype application/zip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION} && \
github-assets-uploader -f authorizer-${VERSION}-darwin-amd64.tar.gz -mediatype application/gzip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
github-assets-uploader -f authorizer-${VERSION}-linux-amd64.tar.gz -mediatype application/gzip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION} github-assets-uploader -f authorizer-${VERSION}-linux-amd64.tar.gz -mediatype application/gzip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
github-assets-uploader -f authorizer-${VERSION}-linux-arm64.tar.gz -mediatype application/gzip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
github-assets-uploader -f authorizer-${VERSION}-windows-amd64.zip -mediatype application/zip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
- name: Log in to Docker Hub - name: Log in to Docker Hub
uses: docker/login-action@v1 uses: docker/login-action@v1
with: with:
@ -85,11 +74,6 @@ jobs:
uses: docker/metadata-action@v3 uses: docker/metadata-action@v3
with: with:
images: lakhansamani/authorizer images: lakhansamani/authorizer
tags: |
type=schedule
type=ref,event=branch
type=ref,event=tag
type=ref,event=pr
- name: Build and push Docker image - name: Build and push Docker image
uses: docker/build-push-action@v2 uses: docker/build-push-action@v2
@ -98,6 +82,5 @@ jobs:
push: true push: true
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64,linux/arm64
build-args: | build-args: |
VERSION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }} VERSION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }}

4
.gitignore vendored
View File

@ -16,7 +16,3 @@ test.db
.yalc .yalc
yalc.lock yalc.lock
certs/ certs/
*-shm
*-wal
.idea
*.iml

View File

@ -1,4 +1,4 @@
FROM golang:1.21.3-alpine3.18 AS go-builder FROM golang:1.17-alpine as go-builder
WORKDIR /authorizer WORKDIR /authorizer
COPY server server COPY server server
COPY Makefile . COPY Makefile .
@ -11,7 +11,7 @@ RUN apk add build-base &&\
make clean && make && \ make clean && make && \
chmod 777 build/server chmod 777 build/server
FROM node:20-alpine3.18 AS node-builder FROM node:17-alpine3.12 as node-builder
WORKDIR /authorizer WORKDIR /authorizer
COPY app app COPY app app
COPY dashboard dashboard COPY dashboard dashboard
@ -20,7 +20,7 @@ RUN apk add build-base &&\
make build-app && \ make build-app && \
make build-dashboard make build-dashboard
FROM alpine:3.18 FROM alpine:latest
RUN adduser -D -h /authorizer -u 1000 -k /dev/null authorizer RUN adduser -D -h /authorizer -u 1000 -k /dev/null authorizer
WORKDIR /authorizer WORKDIR /authorizer
RUN mkdir app dashboard RUN mkdir app dashboard

View File

@ -3,12 +3,6 @@ VERSION := $(or $(VERSION),$(DEFAULT_VERSION))
cmd: cmd:
cd server && go build -ldflags "-w -X main.VERSION=$(VERSION)" -o '../build/server' cd server && go build -ldflags "-w -X main.VERSION=$(VERSION)" -o '../build/server'
build:
cd server && gox \
-osarch="linux/amd64 linux/arm64 darwin/arm64 darwin/amd64 windows/amd64" \
-ldflags "-w -X main.VERSION=$(VERSION)" \
-output="../build/{{.OS}}/{{.Arch}}/server" \
./...
build-app: build-app:
cd app && npm i && npm run build cd app && npm i && npm run build
build-dashboard: build-dashboard:
@ -16,7 +10,7 @@ build-dashboard:
clean: clean:
rm -rf build rm -rf build
test: test:
rm -rf server/test/test.db server/test/test.db-shm server/test/test.db-wal && rm -rf test.db test.db-shm test.db-wal && cd server && go clean --testcache && TEST_DBS="sqlite" go test -p 1 -v ./test rm -rf server/test/test.db && rm -rf test.db && cd server && go clean --testcache && TEST_DBS="sqlite" go test -p 1 -v ./test
test-mongodb: test-mongodb:
docker run -d --name authorizer_mongodb_db -p 27017:27017 mongo:4.4.15 docker run -d --name authorizer_mongodb_db -p 27017:27017 mongo:4.4.15
cd server && go clean --testcache && TEST_DBS="mongodb" go test -p 1 -v ./test cd server && go clean --testcache && TEST_DBS="mongodb" go test -p 1 -v ./test
@ -26,34 +20,18 @@ test-scylladb:
cd server && go clean --testcache && TEST_DBS="scylladb" go test -p 1 -v ./test cd server && go clean --testcache && TEST_DBS="scylladb" go test -p 1 -v ./test
docker rm -vf authorizer_scylla_db docker rm -vf authorizer_scylla_db
test-arangodb: test-arangodb:
docker run -d --name authorizer_arangodb -p 8529:8529 -e ARANGO_NO_AUTH=1 arangodb/arangodb:3.10.3 docker run -d --name authorizer_arangodb -p 8529:8529 -e ARANGO_NO_AUTH=1 arangodb/arangodb:3.8.4
cd server && go clean --testcache && TEST_DBS="arangodb" go test -p 1 -v ./test cd server && go clean --testcache && TEST_DBS="arangodb" go test -p 1 -v ./test
docker rm -vf authorizer_arangodb docker rm -vf authorizer_arangodb
test-dynamodb:
docker run -d --name dynamodb-local-test -p 8000:8000 amazon/dynamodb-local:latest
cd server && go clean --testcache && TEST_DBS="dynamodb" go test -p 1 -v ./test
docker rm -vf dynamodb-local-test
test-couchbase:
docker run -d --name couchbase-local-test -p 8091-8097:8091-8097 -p 11210:11210 -p 11207:11207 -p 18091-18095:18091-18095 -p 18096:18096 -p 18097:18097 couchbase:latest
sh scripts/couchbase-test.sh
cd server && go clean --testcache && TEST_DBS="couchbase" go test -p 1 -v ./test
docker rm -vf couchbase-local-test
test-all-db: test-all-db:
rm -rf server/test/test.db server/test/test.db-shm server/test/test.db-wal && rm -rf test.db test.db-shm test.db-wal rm -rf server/test/test.db && rm -rf test.db
docker run -d --name authorizer_scylla_db -p 9042:9042 scylladb/scylla docker run -d --name authorizer_scylla_db -p 9042:9042 scylladb/scylla
docker run -d --name authorizer_mongodb_db -p 27017:27017 mongo:4.4.15 docker run -d --name authorizer_mongodb_db -p 27017:27017 mongo:4.4.15
docker run -d --name authorizer_arangodb -p 8529:8529 -e ARANGO_NO_AUTH=1 arangodb/arangodb:3.10.3 docker run -d --name authorizer_arangodb -p 8529:8529 -e ARANGO_NO_AUTH=1 arangodb/arangodb:3.8.4
docker run -d --name dynamodb-local-test -p 8000:8000 amazon/dynamodb-local:latest cd server && go clean --testcache && TEST_DBS="sqlite,mongodb,arangodb,scylladb" go test -p 1 -v ./test
docker run -d --name couchbase-local-test -p 8091-8097:8091-8097 -p 11210:11210 -p 11207:11207 -p 18091-18095:18091-18095 -p 18096:18096 -p 18097:18097 couchbase:latest
sh scripts/couchbase-test.sh
cd server && go clean --testcache && TEST_DBS="sqlite,mongodb,arangodb,scylladb,dynamodb,couchbase" go test -p 1 -v ./test
docker rm -vf authorizer_scylla_db docker rm -vf authorizer_scylla_db
docker rm -vf authorizer_mongodb_db docker rm -vf authorizer_mongodb_db
docker rm -vf authorizer_arangodb docker rm -vf authorizer_arangodb
docker rm -vf dynamodb-local-test generate:
docker rm -vf couchbase-local-test cd server && go get github.com/99designs/gqlgen/cmd@v0.14.0 && go run github.com/99designs/gqlgen generate
generate-graphql:
cd server && go run github.com/99designs/gqlgen generate && go mod tidy
generate-db-template:
cp -rf server/db/providers/provider_template server/db/providers/${dbname}
find server/db/providers/${dbname} -type f -exec sed -i -e 's/provider_template/${dbname}/g' {} \;

View File

@ -68,8 +68,6 @@ Deploy production ready Authorizer instance using one click deployment options a
| Railway.app | <a href="https://railway.app/new/template/nwXp1C?referralCode=FEF4uT"><img src="https://railway.app/button.svg" style="height: 44px" alt="Deploy on Railway"></a> | [docs](https://docs.authorizer.dev/deployment/railway) | | Railway.app | <a href="https://railway.app/new/template/nwXp1C?referralCode=FEF4uT"><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) | | 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 | [![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/authorizerdev/authorizer-render) | [docs](https://docs.authorizer.dev/deployment/render) | | Render | [![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/authorizerdev/authorizer-render) | [docs](https://docs.authorizer.dev/deployment/render) |
| Koyeb | <a target="_blank" href="https://app.koyeb.com/deploy?name=authorizer&type=docker&image=docker.io/lakhansamani/authorizer&env[PORT]=8000&env[DATABASE_TYPE]=postgres&env[DATABASE_URL]=CHANGE_ME&ports=8000;http;/"><img alt="Deploy to Koyeb" src="https://www.koyeb.com/static/images/deploy/button.svg" /></a> | [docs](https://docs.authorizer.dev/deployment/koyeb) |
| RepoCloud | <a href="https://repocloud.io/details/?app_id=174"><img src="https://d16t0pc4846x52.cloudfront.net/deploy.png" alt="Deploy on RepoCloud"></a> | [docs](https://repocloud.io/details/?app_id=174) |
### Deploy Authorizer Using Source Code ### Deploy Authorizer Using Source Code

View File

@ -1,6 +0,0 @@
{
"tabWidth": 2,
"singleQuote": true,
"trailingComma": "all",
"useTabs": true
}

971
app/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -5,14 +5,13 @@
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"build": "rm -rf build && NODE_ENV=production node ./esbuild.config.js", "build": "rm -rf build && NODE_ENV=production node ./esbuild.config.js",
"start": "NODE_ENV=development node ./esbuild.config.js", "start": "NODE_ENV=development node ./esbuild.config.js"
"format": "prettier --write 'src/**/*.(ts|tsx|js|jsx)'"
}, },
"keywords": [], "keywords": [],
"author": "Lakhan Samani", "author": "Lakhan Samani",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@authorizerdev/authorizer-react": "^1.3.2", "@authorizerdev/authorizer-react": "^1.1.0",
"@types/react": "^17.0.15", "@types/react": "^17.0.15",
"@types/react-dom": "^17.0.9", "@types/react-dom": "^17.0.9",
"esbuild": "^0.12.17", "esbuild": "^0.12.17",
@ -20,12 +19,11 @@
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-is": "^17.0.2", "react-is": "^17.0.2",
"react-router-dom": "^5.2.0", "react-router-dom": "^5.2.0",
"styled-components": "^5.3.0", "typescript": "^4.3.5",
"typescript": "^4.3.5" "styled-components": "^5.3.0"
}, },
"devDependencies": { "devDependencies": {
"@types/react-router-dom": "^5.1.8", "@types/react-router-dom": "^5.1.8",
"@types/styled-components": "^5.1.11", "@types/styled-components": "^5.1.11"
"prettier": "2.7.1"
} }
} }

View File

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

View File

@ -27,12 +27,13 @@ export default function App() {
if (redirectURL) { if (redirectURL) {
urlProps.redirectURL = redirectURL; urlProps.redirectURL = redirectURL;
} else { } else {
urlProps.redirectURL = window.location.href; urlProps.redirectURL = window.location.origin + '/app';
} }
const globalState: Record<string, string> = { const globalState: Record<string, string> = {
...window['__authorizer__'], ...window['__authorizer__'],
...urlProps, ...urlProps,
}; };
return ( return (
<div <div
style={{ style={{
@ -53,7 +54,7 @@ export default function App() {
<img <img
src={`${globalState.organizationLogo}`} src={`${globalState.organizationLogo}`}
alt="logo" alt="logo"
style={{ height: 60, objectFit: 'cover' }} style={{ height: 60, width: 60, objectFit: 'cover' }}
/> />
<h1>{globalState.organizationName}</h1> <h1>{globalState.organizationName}</h1>
</div> </div>

View File

@ -32,14 +32,12 @@ export default function Root({
const { token, loading, config } = useAuthorizer(); const { token, loading, config } = useAuthorizer();
const searchParams = new URLSearchParams( const searchParams = new URLSearchParams(
hasWindow() ? window.location.search : ``, hasWindow() ? window.location.search : ``
); );
const state = searchParams.get('state') || createRandomString(); const state = searchParams.get('state') || createRandomString();
const scope = searchParams.get('scope') const scope = searchParams.get('scope')
? searchParams.get('scope')?.toString().split(' ') ? searchParams.get('scope')?.toString().split(' ')
: ['openid', 'profile', 'email']; : ['openid', 'profile', 'email'];
const code = searchParams.get('code') || '';
const nonce = searchParams.get('nonce') || '';
const urlProps: Record<string, any> = { const urlProps: Record<string, any> = {
state, state,
@ -59,22 +57,10 @@ export default function Root({
useEffect(() => { useEffect(() => {
if (token) { if (token) {
let redirectURL = 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}`; let params = `access_token=${token.access_token}&id_token=${token.id_token}&expires_in=${token.expires_in}&state=${globalState.state}`;
// Note: If OIDC breaks in the future, use the above params
let params = `state=${globalState.state}`;
if (code !== '') {
params += `&code=${code}`;
}
if (nonce !== '') {
params += `&nonce=${nonce}`;
}
if (token.refresh_token) { if (token.refresh_token) {
params += `&refresh_token=${token.refresh_token}`; params += `&refresh_token=${token.refresh_token}`;
} }
const url = new URL(redirectURL); const url = new URL(redirectURL);
if (redirectURL.includes('?')) { if (redirectURL.includes('?')) {
redirectURL = `${redirectURL}&${params}`; redirectURL = `${redirectURL}&${params}`;
@ -88,7 +74,7 @@ export default function Root({
} }
} }
return () => {}; return () => {};
}, [token, config]); }, [token]);
if (loading) { if (loading) {
return <h1>Loading...</h1>; return <h1>Loading...</h1>;
@ -114,7 +100,7 @@ export default function Root({
<Route path="/app" exact> <Route path="/app" exact>
<Login urlProps={urlProps} /> <Login urlProps={urlProps} />
</Route> </Route>
<Route path="/app/signup"> <Route path="/app/signup" exact>
<SignUp urlProps={urlProps} /> <SignUp urlProps={urlProps} />
</Route> </Route>
<Route path="/app/reset-password"> <Route path="/app/reset-password">

View File

@ -32,25 +32,20 @@ const FooterContent = styled.div`
export default function Login({ urlProps }: { urlProps: Record<string, any> }) { export default function Login({ urlProps }: { urlProps: Record<string, any> }) {
const { config } = useAuthorizer(); const { config } = useAuthorizer();
const [view, setView] = useState<VIEW_TYPES>(VIEW_TYPES.LOGIN); const [view, setView] = useState<VIEW_TYPES>(VIEW_TYPES.LOGIN);
const isBasicAuth = config.is_basic_authentication_enabled;
return ( return (
<Fragment> <Fragment>
{view === VIEW_TYPES.LOGIN && ( {view === VIEW_TYPES.LOGIN && (
<Fragment> <Fragment>
<h1 style={{ textAlign: 'center' }}>Login</h1> <h1 style={{ textAlign: 'center' }}>Login</h1>
<AuthorizerSocialLogin urlProps={urlProps} />
<br /> <br />
{(config.is_basic_authentication_enabled || <AuthorizerSocialLogin urlProps={urlProps} />
config.is_mobile_basic_authentication_enabled) && {config.is_basic_authentication_enabled &&
!config.is_magic_link_login_enabled && ( !config.is_magic_link_login_enabled && (
<AuthorizerBasicAuthLogin urlProps={urlProps} /> <AuthorizerBasicAuthLogin urlProps={urlProps} />
)} )}
{config.is_magic_link_login_enabled && ( {config.is_magic_link_login_enabled && (
<AuthorizerMagicLinkLogin urlProps={urlProps} /> <AuthorizerMagicLinkLogin urlProps={urlProps} />
)} )}
{(config.is_basic_authentication_enabled ||
config.is_mobile_basic_authentication_enabled) &&
!config.is_magic_link_login_enabled && (
<Footer> <Footer>
<Link <Link
to="#" to="#"
@ -60,21 +55,12 @@ export default function Login({ urlProps }: { urlProps: Record<string, any> }) {
Forgot Password? Forgot Password?
</Link> </Link>
</Footer> </Footer>
)}
</Fragment> </Fragment>
)} )}
{view === VIEW_TYPES.FORGOT_PASSWORD && ( {view === VIEW_TYPES.FORGOT_PASSWORD && (
<Fragment> <Fragment>
<h1 style={{ textAlign: 'center' }}>Forgot Password</h1> <h1 style={{ textAlign: 'center' }}>Forgot Password</h1>
<AuthorizerForgotPassword <AuthorizerForgotPassword urlProps={urlProps} />
urlProps={{
...urlProps,
redirect_uri: `${window.location.origin}/app/reset-password`,
}}
onPasswordReset={() => {
setView(VIEW_TYPES.LOGIN);
}}
/>
<Footer> <Footer>
<Link <Link
to="#" to="#"
@ -90,7 +76,7 @@ export default function Login({ urlProps }: { urlProps: Record<string, any> }) {
!config.is_magic_link_login_enabled && !config.is_magic_link_login_enabled &&
config.is_sign_up_enabled && ( config.is_sign_up_enabled && (
<FooterContent> <FooterContent>
Don't have an account? &nbsp; <Link to="/app/signup"> Sign Up</Link> Don't have an account? <Link to="/app/signup"> Sign Up</Link>
</FooterContent> </FooterContent>
)} )}
</Fragment> </Fragment>

View File

@ -1,5 +1,5 @@
import React, { Fragment } from 'react'; import React, { Fragment } from 'react';
import { AuthorizerSignup, AuthorizerSocialLogin } from '@authorizerdev/authorizer-react'; import { AuthorizerSignup } from '@authorizerdev/authorizer-react';
import styled from 'styled-components'; import styled from 'styled-components';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
@ -19,7 +19,6 @@ export default function SignUp({
<Fragment> <Fragment>
<h1 style={{ textAlign: 'center' }}>Sign Up</h1> <h1 style={{ textAlign: 'center' }}>Sign Up</h1>
<br /> <br />
<AuthorizerSocialLogin urlProps={urlProps} />
<AuthorizerSignup urlProps={urlProps} /> <AuthorizerSignup urlProps={urlProps} />
<FooterContent> <FooterContent>
Already have an account? <Link to="/app"> Login</Link> Already have an account? <Link to="/app"> Login</Link>

View File

@ -8,7 +8,7 @@ export const createRandomString = () => {
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_~.'; '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_~.';
let random = ''; let random = '';
const randomValues = Array.from( const randomValues = Array.from(
getCrypto().getRandomValues(new Uint8Array(43)), getCrypto().getRandomValues(new Uint8Array(43))
); );
randomValues.forEach((v) => (random += charset[v % charset.length])); randomValues.forEach((v) => (random += charset[v % charset.length]));
return random; return random;

View File

@ -1,619 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@authorizerdev/authorizer-js@^2.0.3":
version "2.0.3"
resolved "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-2.0.3.tgz"
integrity sha512-uencwr3Ea8mwfxVKDFf2ITRCRSmzvua+O2voRuiWQORtRQTgZQjkN3M+IEkEj+WP9M1iFIl+NDgzECsp8ptC/A==
dependencies:
cross-fetch "^3.1.5"
"@authorizerdev/authorizer-react@^1.3.2":
version "1.3.2"
resolved "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.3.2.tgz"
integrity sha512-3kMAygHBCa8Fc9Oo0lz1k88r+Pd6kx1PSn3NMYLwxQXy2jRt4xWn7iuGn+SDGFs3DzofaN71I61gRwQ+6dO1rw==
dependencies:
"@authorizerdev/authorizer-js" "^2.0.3"
validator "^13.11.0"
"@babel/code-frame@^7.22.13":
version "7.22.13"
resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz"
integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==
dependencies:
"@babel/highlight" "^7.22.13"
chalk "^2.4.2"
"@babel/generator@^7.23.0":
version "7.23.0"
resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz"
integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==
dependencies:
"@babel/types" "^7.23.0"
"@jridgewell/gen-mapping" "^0.3.2"
"@jridgewell/trace-mapping" "^0.3.17"
jsesc "^2.5.1"
"@babel/helper-annotate-as-pure@^7.16.0":
version "7.16.7"
resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz"
integrity sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==
dependencies:
"@babel/types" "^7.16.7"
"@babel/helper-environment-visitor@^7.22.20":
version "7.22.20"
resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz"
integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==
"@babel/helper-function-name@^7.23.0":
version "7.23.0"
resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz"
integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==
dependencies:
"@babel/template" "^7.22.15"
"@babel/types" "^7.23.0"
"@babel/helper-hoist-variables@^7.22.5":
version "7.22.5"
resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz"
integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==
dependencies:
"@babel/types" "^7.22.5"
"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.16.0":
version "7.16.7"
resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz"
integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==
dependencies:
"@babel/types" "^7.16.7"
"@babel/helper-split-export-declaration@^7.22.6":
version "7.22.6"
resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz"
integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==
dependencies:
"@babel/types" "^7.22.5"
"@babel/helper-string-parser@^7.22.5":
version "7.22.5"
resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz"
integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==
"@babel/helper-validator-identifier@^7.22.20":
version "7.22.20"
resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz"
integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==
"@babel/highlight@^7.22.13":
version "7.22.20"
resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz"
integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==
dependencies:
"@babel/helper-validator-identifier" "^7.22.20"
chalk "^2.4.2"
js-tokens "^4.0.0"
"@babel/parser@^7.22.15", "@babel/parser@^7.23.0":
version "7.23.0"
resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz"
integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==
"@babel/runtime@^7.1.2", "@babel/runtime@^7.12.1":
version "7.14.8"
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz"
integrity sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/template@^7.22.15":
version "7.22.15"
resolved "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz"
integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==
dependencies:
"@babel/code-frame" "^7.22.13"
"@babel/parser" "^7.22.15"
"@babel/types" "^7.22.15"
"@babel/traverse@^7.4.5":
version "7.23.2"
resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz"
integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==
dependencies:
"@babel/code-frame" "^7.22.13"
"@babel/generator" "^7.23.0"
"@babel/helper-environment-visitor" "^7.22.20"
"@babel/helper-function-name" "^7.23.0"
"@babel/helper-hoist-variables" "^7.22.5"
"@babel/helper-split-export-declaration" "^7.22.6"
"@babel/parser" "^7.23.0"
"@babel/types" "^7.23.0"
debug "^4.1.0"
globals "^11.1.0"
"@babel/types@^7.16.7", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0":
version "7.23.0"
resolved "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz"
integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==
dependencies:
"@babel/helper-string-parser" "^7.22.5"
"@babel/helper-validator-identifier" "^7.22.20"
to-fast-properties "^2.0.0"
"@emotion/is-prop-valid@^0.8.8":
version "0.8.8"
resolved "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz"
integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==
dependencies:
"@emotion/memoize" "0.7.4"
"@emotion/memoize@0.7.4":
version "0.7.4"
resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz"
integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==
"@emotion/stylis@^0.8.4":
version "0.8.5"
resolved "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz"
integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==
"@emotion/unitless@^0.7.4":
version "0.7.5"
resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz"
integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==
"@jridgewell/gen-mapping@^0.3.2":
version "0.3.3"
resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz"
integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==
dependencies:
"@jridgewell/set-array" "^1.0.1"
"@jridgewell/sourcemap-codec" "^1.4.10"
"@jridgewell/trace-mapping" "^0.3.9"
"@jridgewell/resolve-uri@^3.1.0":
version "3.1.1"
resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz"
integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==
"@jridgewell/set-array@^1.0.1":
version "1.1.2"
resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz"
integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14":
version "1.4.15"
resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz"
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9":
version "0.3.20"
resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz"
integrity sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==
dependencies:
"@jridgewell/resolve-uri" "^3.1.0"
"@jridgewell/sourcemap-codec" "^1.4.14"
"@types/history@*":
version "4.7.9"
resolved "https://registry.npmjs.org/@types/history/-/history-4.7.9.tgz"
integrity sha512-MUc6zSmU3tEVnkQ78q0peeEjKWPUADMlC/t++2bI8WnAG2tvYRPIgHG8lWkXwqc8MsUF6Z2MOf+Mh5sazOmhiQ==
"@types/hoist-non-react-statics@*":
version "3.3.1"
resolved "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz"
integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
dependencies:
"@types/react" "*"
hoist-non-react-statics "^3.3.0"
"@types/prop-types@*":
version "15.7.4"
resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz"
integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==
"@types/react-dom@^17.0.9":
version "17.0.9"
resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.9.tgz"
integrity sha512-wIvGxLfgpVDSAMH5utdL9Ngm5Owu0VsGmldro3ORLXV8CShrL8awVj06NuEXFQ5xyaYfdca7Sgbk/50Ri1GdPg==
dependencies:
"@types/react" "*"
"@types/react-router-dom@^5.1.8":
version "5.1.8"
resolved "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.8.tgz"
integrity sha512-03xHyncBzG0PmDmf8pf3rehtjY0NpUj7TIN46FrT5n1ZWHPZvXz32gUyNboJ+xsL8cpg8bQVLcllptcQHvocrw==
dependencies:
"@types/history" "*"
"@types/react" "*"
"@types/react-router" "*"
"@types/react-router@*":
version "5.1.16"
resolved "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.16.tgz"
integrity sha512-8d7nR/fNSqlTFGHti0R3F9WwIertOaaA1UEB8/jr5l5mDMOs4CidEgvvYMw4ivqrBK+vtVLxyTj2P+Pr/dtgzg==
dependencies:
"@types/history" "*"
"@types/react" "*"
"@types/react@*", "@types/react@^17.0.15":
version "17.0.15"
resolved "https://registry.npmjs.org/@types/react/-/react-17.0.15.tgz"
integrity sha512-uTKHDK9STXFHLaKv6IMnwp52fm0hwU+N89w/p9grdUqcFA6WuqDyPhaWopbNyE1k/VhgzmHl8pu1L4wITtmlLw==
dependencies:
"@types/prop-types" "*"
"@types/scheduler" "*"
csstype "^3.0.2"
"@types/scheduler@*":
version "0.16.2"
resolved "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz"
integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==
"@types/styled-components@^5.1.11":
version "5.1.25"
resolved "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.25.tgz"
integrity sha512-fgwl+0Pa8pdkwXRoVPP9JbqF0Ivo9llnmsm+7TCI330kbPIFd9qv1Lrhr37shf4tnxCOSu+/IgqM7uJXLWZZNQ==
dependencies:
"@types/hoist-non-react-statics" "*"
"@types/react" "*"
csstype "^3.0.2"
ansi-styles@^3.2.1:
version "3.2.1"
resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz"
integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
dependencies:
color-convert "^1.9.0"
"babel-plugin-styled-components@>= 1.12.0":
version "2.0.2"
resolved "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.0.2.tgz"
integrity sha512-7eG5NE8rChnNTDxa6LQfynwgHTVOYYaHJbUYSlOhk8QBXIQiMBKq4gyfHBBKPrxUcVBXVJL61ihduCpCQbuNbw==
dependencies:
"@babel/helper-annotate-as-pure" "^7.16.0"
"@babel/helper-module-imports" "^7.16.0"
babel-plugin-syntax-jsx "^6.18.0"
lodash "^4.17.11"
babel-plugin-syntax-jsx@^6.18.0:
version "6.18.0"
resolved "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz"
integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=
camelize@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz"
integrity sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=
chalk@^2.4.2:
version "2.4.2"
resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
dependencies:
ansi-styles "^3.2.1"
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
color-convert@^1.9.0:
version "1.9.3"
resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz"
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
dependencies:
color-name "1.1.3"
color-name@1.1.3:
version "1.1.3"
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz"
integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
cross-fetch@^3.1.5:
version "3.1.8"
resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz"
integrity sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==
dependencies:
node-fetch "^2.6.12"
css-color-keywords@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz"
integrity sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU=
css-to-react-native@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.0.0.tgz"
integrity sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ==
dependencies:
camelize "^1.0.0"
css-color-keywords "^1.0.0"
postcss-value-parser "^4.0.2"
csstype@^3.0.2:
version "3.0.8"
resolved "https://registry.npmjs.org/csstype/-/csstype-3.0.8.tgz"
integrity sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==
debug@^4.1.0:
version "4.3.3"
resolved "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz"
integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==
dependencies:
ms "2.1.2"
esbuild@^0.12.17:
version "0.12.17"
resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.12.17.tgz"
integrity sha512-GshKJyVYUnlSXIZj/NheC2O0Kblh42CS7P1wJyTbbIHevTG4jYMS9NNw8EOd8dDWD0dzydYHS01MpZoUcQXB4g==
escape-string-regexp@^1.0.5:
version "1.0.5"
resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz"
integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
globals@^11.1.0:
version "11.12.0"
resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz"
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
has-flag@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz"
integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
history@^4.9.0:
version "4.10.1"
resolved "https://registry.npmjs.org/history/-/history-4.10.1.tgz"
integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==
dependencies:
"@babel/runtime" "^7.1.2"
loose-envify "^1.2.0"
resolve-pathname "^3.0.0"
tiny-invariant "^1.0.2"
tiny-warning "^1.0.0"
value-equal "^1.0.1"
hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0:
version "3.3.2"
resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz"
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
dependencies:
react-is "^16.7.0"
isarray@0.0.1:
version "0.0.1"
resolved "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
jsesc@^2.5.1:
version "2.5.2"
resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz"
integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
lodash@^4.17.11:
version "4.17.21"
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
version "1.4.0"
resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
dependencies:
js-tokens "^3.0.0 || ^4.0.0"
mini-create-react-context@^0.4.0:
version "0.4.1"
resolved "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz"
integrity sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==
dependencies:
"@babel/runtime" "^7.12.1"
tiny-warning "^1.0.3"
ms@2.1.2:
version "2.1.2"
resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
node-fetch@^2.6.12:
version "2.7.0"
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz"
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
dependencies:
whatwg-url "^5.0.0"
object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
path-to-regexp@^1.7.0:
version "1.8.0"
resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz"
integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==
dependencies:
isarray "0.0.1"
postcss-value-parser@^4.0.2:
version "4.2.0"
resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz"
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
prettier@2.7.1:
version "2.7.1"
resolved "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz"
integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==
prop-types@^15.0.0, prop-types@^15.6.2:
version "15.7.2"
resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
dependencies:
loose-envify "^1.4.0"
object-assign "^4.1.1"
react-is "^16.8.1"
react-dom@^17.0.2, "react-dom@>= 16.8.0":
version "17.0.2"
resolved "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz"
integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
scheduler "^0.20.2"
react-is@^16.6.0:
version "16.13.1"
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-is@^16.7.0:
version "16.13.1"
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-is@^16.8.1:
version "16.13.1"
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-is@^17.0.2, "react-is@>= 16.8.0":
version "17.0.2"
resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
react-router-dom@^5.2.0:
version "5.2.0"
resolved "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz"
integrity sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==
dependencies:
"@babel/runtime" "^7.1.2"
history "^4.9.0"
loose-envify "^1.3.1"
prop-types "^15.6.2"
react-router "5.2.0"
tiny-invariant "^1.0.2"
tiny-warning "^1.0.0"
react-router@5.2.0:
version "5.2.0"
resolved "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz"
integrity sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==
dependencies:
"@babel/runtime" "^7.1.2"
history "^4.9.0"
hoist-non-react-statics "^3.1.0"
loose-envify "^1.3.1"
mini-create-react-context "^0.4.0"
path-to-regexp "^1.7.0"
prop-types "^15.6.2"
react-is "^16.6.0"
tiny-invariant "^1.0.2"
tiny-warning "^1.0.0"
"react@^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0", react@^17.0.2, "react@>= 16.8.0", react@>=15, react@>=16, react@17.0.2:
version "17.0.2"
resolved "https://registry.npmjs.org/react/-/react-17.0.2.tgz"
integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
regenerator-runtime@^0.13.4:
version "0.13.9"
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz"
integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
resolve-pathname@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz"
integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==
scheduler@^0.20.2:
version "0.20.2"
resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz"
integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
shallowequal@^1.1.0:
version "1.1.0"
resolved "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz"
integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==
styled-components@^5.3.0, "styled-components@>= 2":
version "5.3.3"
resolved "https://registry.npmjs.org/styled-components/-/styled-components-5.3.3.tgz"
integrity sha512-++4iHwBM7ZN+x6DtPPWkCI4vdtwumQ+inA/DdAsqYd4SVgUKJie5vXyzotA00ttcFdQkCng7zc6grwlfIfw+lw==
dependencies:
"@babel/helper-module-imports" "^7.0.0"
"@babel/traverse" "^7.4.5"
"@emotion/is-prop-valid" "^0.8.8"
"@emotion/stylis" "^0.8.4"
"@emotion/unitless" "^0.7.4"
babel-plugin-styled-components ">= 1.12.0"
css-to-react-native "^3.0.0"
hoist-non-react-statics "^3.0.0"
shallowequal "^1.1.0"
supports-color "^5.5.0"
supports-color@^5.3.0, supports-color@^5.5.0:
version "5.5.0"
resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz"
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
dependencies:
has-flag "^3.0.0"
tiny-invariant@^1.0.2:
version "1.1.0"
resolved "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz"
integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==
tiny-warning@^1.0.0, tiny-warning@^1.0.3:
version "1.0.3"
resolved "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz"
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
to-fast-properties@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz"
integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
tr46@~0.0.3:
version "0.0.3"
resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz"
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
typescript@^4.3.5:
version "4.3.5"
resolved "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz"
integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==
validator@^13.11.0:
version "13.11.0"
resolved "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz"
integrity sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==
value-equal@^1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz"
integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz"
integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
whatwg-url@^5.0.0:
version "5.0.0"
resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz"
integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
dependencies:
tr46 "~0.0.3"
webidl-conversions "^3.0.0"

View File

@ -1,6 +0,0 @@
{
"tabWidth": 2,
"singleQuote": true,
"trailingComma": "all",
"useTabs": true
}

File diff suppressed because it is too large Load Diff

View File

@ -5,8 +5,7 @@
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"build": "rm -rf build && NODE_ENV=production node ./esbuild.config.js", "build": "rm -rf build && NODE_ENV=production node ./esbuild.config.js",
"start": "NODE_ENV=development node ./esbuild.config.js", "start": "NODE_ENV=development node ./esbuild.config.js"
"format": "prettier --write --use-tabs 'src/**/*.(ts|tsx|js|jsx)'"
}, },
"keywords": [], "keywords": [],
"author": "Lakhan Samani", "author": "Lakhan Samani",
@ -36,7 +35,6 @@
"urql": "^2.0.6" "urql": "^2.0.6"
}, },
"devDependencies": { "devDependencies": {
"@types/react-email-editor": "^1.1.7", "@types/react-email-editor": "^1.1.7"
"prettier": "2.7.1"
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
import React, { useState } from 'react'; import React from 'react';
import { import {
Button, Button,
Center, Center,
@ -20,14 +20,13 @@ import { useClient } from 'urql';
import { FaSave } from 'react-icons/fa'; import { FaSave } from 'react-icons/fa';
import InputField from './InputField'; import InputField from './InputField';
import { import {
ArrayInputType,
DateInputType, DateInputType,
MultiSelectInputType,
SelectInputType, SelectInputType,
TextInputType, TextInputType,
} from '../constants'; } from '../constants';
import { getObjectDiff } from '../utils'; import { getObjectDiff } from '../utils';
import { UpdateUser } from '../graphql/mutation'; import { UpdateUser } from '../graphql/mutation';
import { GetAvailableRolesQuery } from '../graphql/queries';
const GenderTypes = { const GenderTypes = {
Undisclosed: null, Undisclosed: null,
@ -58,9 +57,8 @@ const EditUserModal = ({
}) => { }) => {
const client = useClient(); const client = useClient();
const toast = useToast(); const toast = useToast();
const [availableRoles, setAvailableRoles] = useState<string[]>([]);
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
const [userData, setUserData] = useState<userDataTypes>({ const [userData, setUserData] = React.useState<userDataTypes>({
id: '', id: '',
email: '', email: '',
given_name: '', given_name: '',
@ -75,17 +73,7 @@ const EditUserModal = ({
}); });
React.useEffect(() => { React.useEffect(() => {
setUserData(user); setUserData(user);
fetchAvailableRoles();
}, []); }, []);
const fetchAvailableRoles = async () => {
const res = await client.query(GetAvailableRolesQuery).toPromise();
if (res.data?._env?.ROLES && res.data?._env?.PROTECTED_ROLES) {
setAvailableRoles([
...res.data._env.ROLES,
...res.data._env.PROTECTED_ROLES,
]);
}
};
const saveHandler = async () => { const saveHandler = async () => {
const diff = getObjectDiff(user, userData); const diff = getObjectDiff(user, userData);
const updatedUserData = diff.reduce( const updatedUserData = diff.reduce(
@ -94,7 +82,7 @@ const EditUserModal = ({
// @ts-ignore // @ts-ignore
[property]: userData[property], [property]: userData[property],
}), }),
{}, {}
); );
const res = await client const res = await client
.mutation(UpdateUser, { params: { ...updatedUserData, id: userData.id } }) .mutation(UpdateUser, { params: { ...updatedUserData, id: userData.id } })
@ -104,14 +92,14 @@ const EditUserModal = ({
title: 'User data update failed', title: 'User data update failed',
isClosable: true, isClosable: true,
status: 'error', status: 'error',
position: 'top-right', position: 'bottom-right',
}); });
} else if (res.data?._update_user?.id) { } else if (res.data?._update_user?.id) {
toast({ toast({
title: 'User data update successful', title: 'User data update successful',
isClosable: true, isClosable: true,
status: 'success', status: 'success',
position: 'top-right', position: 'bottom-right',
}); });
} }
onClose(); onClose();
@ -233,8 +221,7 @@ const EditUserModal = ({
<InputField <InputField
variables={userData} variables={userData}
setVariables={setUserData} setVariables={setUserData}
availableRoles={availableRoles} inputType={ArrayInputType.USER_ROLES}
inputType={MultiSelectInputType.USER_ROLES}
/> />
</Center> </Center>
</Flex> </Flex>

View File

@ -1,28 +1,28 @@
import React from 'react'; import React from "react";
import { Flex, Stack, Text, useMediaQuery } from '@chakra-ui/react'; import { Flex, Stack, Text, useMediaQuery } from "@chakra-ui/react";
import InputField from '../../components/InputField'; import InputField from "../../components/InputField";
import { TextInputType, TextAreaInputType } from '../../constants'; import { TextInputType, TextAreaInputType } from "../../constants";
const AccessToken = ({ variables, setVariables }: any) => { const AccessToken = ({ variables, setVariables }: any) => {
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)'); const [isNotSmallerScreen] = useMediaQuery("(min-width:600px)");
return ( return (
<div> <div>
{' '} {" "}
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}> <Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
Access Token Access Token
</Text> </Text>
<Stack spacing={6} padding="2% 0%"> <Stack spacing={6} padding="2% 0%">
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}> <Flex direction={isNotSmallerScreen ? "row" : "column"}>
<Flex <Flex
w={isNotSmallerScreen ? '30%' : '50%'} w={isNotSmallerScreen ? "30%" : "50%"}
justifyContent="start" justifyContent="start"
alignItems="center" alignItems="center"
> >
<Text fontSize="sm">Access Token Expiry Time:</Text> <Text fontSize="sm">Access Token Expiry Time:</Text>
</Flex> </Flex>
<Flex <Flex
w={isNotSmallerScreen ? '70%' : '100%'} w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? '0' : '3'} mt={isNotSmallerScreen ? "0" : "3"}
> >
<InputField <InputField
borderRadius={5} borderRadius={5}
@ -33,9 +33,9 @@ const AccessToken = ({ variables, setVariables }: any) => {
/> />
</Flex> </Flex>
</Flex> </Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}> <Flex direction={isNotSmallerScreen ? "row" : "column"}>
<Flex <Flex
w={isNotSmallerScreen ? '30%' : '60%'} w={isNotSmallerScreen ? "30%" : "60%"}
justifyContent="start" justifyContent="start"
direction="column" direction="column"
> >
@ -45,8 +45,8 @@ const AccessToken = ({ variables, setVariables }: any) => {
</Text> </Text>
</Flex> </Flex>
<Flex <Flex
w={isNotSmallerScreen ? '70%' : '100%'} w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? '0' : '3'} mt={isNotSmallerScreen ? "0" : "3"}
> >
<InputField <InputField
variables={variables} variables={variables}

View File

@ -1,24 +1,24 @@
import React from 'react'; import React from "react";
import { Flex, Stack, Center, Text, useMediaQuery } from '@chakra-ui/react'; import { Flex, Stack, Center, Text, useMediaQuery } from "@chakra-ui/react";
import InputField from '../../components/InputField'; import InputField from "../../components/InputField";
import { ArrayInputType } from '../../constants'; import { ArrayInputType} from "../../constants";
const DomainWhiteListing = ({ variables, setVariables }: any) => { const DomainWhiteListing = ({ variables, setVariables }: any) => {
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)'); const [isNotSmallerScreen] = useMediaQuery("(min-width:600px)");
return ( return (
<div> <div>
{' '} {" "}
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}> <Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
Domain White Listing Domain White Listing
</Text> </Text>
<Stack spacing={6} padding="2% 0%"> <Stack spacing={6} padding="2% 0%">
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}> <Flex direction={isNotSmallerScreen ? "row" : "column"}>
<Flex w="30%" justifyContent="start" alignItems="center"> <Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Allowed Origins:</Text> <Text fontSize="sm">Allowed Origins:</Text>
</Flex> </Flex>
<Center <Center
w={isNotSmallerScreen ? '70%' : '100%'} w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? '0' : '3'} mt={isNotSmallerScreen ? "0" : "3"}
> >
<InputField <InputField
variables={variables} variables={variables}

View File

@ -1,28 +1,28 @@
import React from 'react'; import React from "react";
import { Flex, Stack, Center, Text, useMediaQuery } from '@chakra-ui/react'; import { Flex, Stack, Center, Text, useMediaQuery } from "@chakra-ui/react";
import InputField from '../../components/InputField'; import InputField from "../../components/InputField";
import { TextInputType, HiddenInputType } from '../../constants'; import { TextInputType, HiddenInputType} from "../../constants";
const EmailConfigurations = ({ const EmailConfigurations = ({
variables, variables,
setVariables, setVariables,
fieldVisibility, fieldVisibility,
setFieldVisibility, setFieldVisibility,
}: any) => { }: any) => {
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)'); const [isNotSmallerScreen] = useMediaQuery("(min-width:600px)");
return ( return (
<div> <div>
{' '} {" "}
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}> <Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
Email Configurations Email Configurations
</Text> </Text>
<Stack spacing={6} padding="2% 0%"> <Stack spacing={6} padding="2% 0%">
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}> <Flex direction={isNotSmallerScreen ? "row" : "column"}>
<Flex w="30%" justifyContent="start" alignItems="center"> <Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">SMTP Host:</Text> <Text fontSize="sm">SMTP Host:</Text>
</Flex> </Flex>
<Center <Center
w={isNotSmallerScreen ? '70%' : '100%'} w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? '0' : '3'} mt={isNotSmallerScreen ? "0" : "3"}
> >
<InputField <InputField
borderRadius={5} borderRadius={5}
@ -32,13 +32,13 @@ const EmailConfigurations = ({
/> />
</Center> </Center>
</Flex> </Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}> <Flex direction={isNotSmallerScreen ? "row" : "column"}>
<Flex w="30%" justifyContent="start" alignItems="center"> <Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">SMTP Port:</Text> <Text fontSize="sm">SMTP Port:</Text>
</Flex> </Flex>
<Center <Center
w={isNotSmallerScreen ? '70%' : '100%'} w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? '0' : '3'} mt={isNotSmallerScreen ? "0" : "3"}
> >
<InputField <InputField
borderRadius={5} borderRadius={5}
@ -48,37 +48,17 @@ const EmailConfigurations = ({
/> />
</Center> </Center>
</Flex> </Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}> <Flex direction={isNotSmallerScreen ? "row" : "column"}>
<Flex <Flex
w={isNotSmallerScreen ? '30%' : '40%'} w={isNotSmallerScreen ? "30%" : "40%"}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">SMTP Local Name:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.SMTP_LOCAL_NAME}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex
w={isNotSmallerScreen ? '30%' : '40%'}
justifyContent="start" justifyContent="start"
alignItems="center" alignItems="center"
> >
<Text fontSize="sm">SMTP Username:</Text> <Text fontSize="sm">SMTP Username:</Text>
</Flex> </Flex>
<Center <Center
w={isNotSmallerScreen ? '70%' : '100%'} w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? '0' : '3'} mt={isNotSmallerScreen ? "0" : "3"}
> >
<InputField <InputField
borderRadius={5} borderRadius={5}
@ -88,17 +68,17 @@ const EmailConfigurations = ({
/> />
</Center> </Center>
</Flex> </Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}> <Flex direction={isNotSmallerScreen ? "row" : "column"}>
<Flex <Flex
w={isNotSmallerScreen ? '30%' : '40%'} w={isNotSmallerScreen ? "30%" : "40%"}
justifyContent="start" justifyContent="start"
alignItems="center" alignItems="center"
> >
<Text fontSize="sm">SMTP Password:</Text> <Text fontSize="sm">SMTP Password:</Text>
</Flex> </Flex>
<Center <Center
w={isNotSmallerScreen ? '70%' : '100%'} w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? '0' : '3'} mt={isNotSmallerScreen ? "0" : "3"}
> >
<InputField <InputField
borderRadius={5} borderRadius={5}
@ -110,13 +90,13 @@ const EmailConfigurations = ({
/> />
</Center> </Center>
</Flex> </Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}> <Flex direction={isNotSmallerScreen ? "row" : "column"}>
<Flex w="30%" justifyContent="start" alignItems="center"> <Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">From Email:</Text> <Text fontSize="sm">From Email:</Text>
</Flex> </Flex>
<Center <Center
w={isNotSmallerScreen ? '70%' : '100%'} w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? '0' : '3'} mt={isNotSmallerScreen ? "0" : "3"}
> >
<InputField <InputField
borderRadius={5} borderRadius={5}
@ -126,22 +106,6 @@ const EmailConfigurations = ({
/> />
</Center> </Center>
</Flex> </Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Sender Name:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.SENDER_NAME}
/>
</Center>
</Flex>
</Stack> </Stack>
</div> </div>
); );

View File

@ -8,154 +8,105 @@ const Features = ({ variables, setVariables }: any) => {
<div> <div>
{' '} {' '}
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}> <Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
Features Disable Features
</Text> </Text>
<Stack spacing={6}> <Stack spacing={6}>
<Flex> <Flex>
<Flex w="100%" justifyContent="start" alignItems="center"> <Flex w="100%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Login Page:</Text> <Text fontSize="sm">Disable Login Page:</Text>
</Flex> </Flex>
<Flex justifyContent="start"> <Flex justifyContent="start">
<InputField <InputField
variables={variables} variables={variables}
setVariables={setVariables} setVariables={setVariables}
inputType={SwitchInputType.DISABLE_LOGIN_PAGE} inputType={SwitchInputType.DISABLE_LOGIN_PAGE}
hasReversedValue
/> />
</Flex> </Flex>
</Flex> </Flex>
<Flex> <Flex>
<Flex w="100%" justifyContent="start" alignItems="center"> <Flex w="100%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Email Verification:</Text> <Text fontSize="sm">Disable Email Verification:</Text>
</Flex> </Flex>
<Flex justifyContent="start"> <Flex justifyContent="start">
<InputField <InputField
variables={variables} variables={variables}
setVariables={setVariables} setVariables={setVariables}
inputType={SwitchInputType.DISABLE_EMAIL_VERIFICATION} inputType={SwitchInputType.DISABLE_EMAIL_VERIFICATION}
hasReversedValue
/> />
</Flex> </Flex>
</Flex> </Flex>
<Flex> <Flex>
<Flex w="100%" justifyContent="start" alignItems="center"> <Flex w="100%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Magic Login Link:</Text> <Text fontSize="sm">Disable Magic Login Link:</Text>
</Flex> </Flex>
<Flex justifyContent="start"> <Flex justifyContent="start">
<InputField <InputField
variables={variables} variables={variables}
setVariables={setVariables} setVariables={setVariables}
inputType={SwitchInputType.DISABLE_MAGIC_LINK_LOGIN} inputType={SwitchInputType.DISABLE_MAGIC_LINK_LOGIN}
hasReversedValue
/> />
</Flex> </Flex>
</Flex> </Flex>
<Flex> <Flex>
<Flex w="100%" justifyContent="start" alignItems="center"> <Flex w="100%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Email Basic Authentication:</Text> <Text fontSize="sm">Disable Basic Authentication:</Text>
</Flex> </Flex>
<Flex justifyContent="start"> <Flex justifyContent="start">
<InputField <InputField
variables={variables} variables={variables}
setVariables={setVariables} setVariables={setVariables}
inputType={SwitchInputType.DISABLE_BASIC_AUTHENTICATION} inputType={SwitchInputType.DISABLE_BASIC_AUTHENTICATION}
hasReversedValue
/> />
</Flex> </Flex>
</Flex> </Flex>
<Flex> <Flex>
<Flex w="100%" justifyContent="start" alignItems="center"> <Flex w="100%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Mobile Basic Authentication:</Text> <Text fontSize="sm">Disable Sign Up:</Text>
</Flex>
<Flex justifyContent="start">
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.DISABLE_MOBILE_BASIC_AUTHENTICATION}
hasReversedValue
/>
</Flex>
</Flex>
<Flex>
<Flex w="100%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Sign Up:</Text>
</Flex> </Flex>
<Flex justifyContent="start" mb={3}> <Flex justifyContent="start" mb={3}>
<InputField <InputField
variables={variables} variables={variables}
setVariables={setVariables} setVariables={setVariables}
inputType={SwitchInputType.DISABLE_SIGN_UP} inputType={SwitchInputType.DISABLE_SIGN_UP}
hasReversedValue
/> />
</Flex> </Flex>
</Flex> </Flex>
<Flex> <Flex>
<Flex w="100%" justifyContent="start" alignItems="center"> <Flex w="100%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Strong Password:</Text> <Text fontSize="sm">Disable Strong Password:</Text>
</Flex> </Flex>
<Flex justifyContent="start" mb={3}> <Flex justifyContent="start" mb={3}>
<InputField <InputField
variables={variables} variables={variables}
setVariables={setVariables} setVariables={setVariables}
inputType={SwitchInputType.DISABLE_STRONG_PASSWORD} inputType={SwitchInputType.DISABLE_STRONG_PASSWORD}
hasReversedValue
/> />
</Flex> </Flex>
</Flex> </Flex>
<Flex alignItems="center"> <Flex alignItems="center">
<Flex w="100%" alignItems="baseline" flexDir="column"> <Flex w="100%" alignItems="baseline" flexDir="column">
<Text fontSize="sm">Multi Factor Authentication (MFA):</Text> <Text fontSize="sm">
Disable Multi Factor Authentication (MFA):
</Text>
<Text fontSize="x-small"> <Text fontSize="x-small">
Note: Enabling this will ignore Enforcing MFA shown below and will Note: Enabling this will ignore Enforcing MFA shown below and will
also ignore the user MFA setting. also ignore the user MFA setting.
</Text> </Text>
</Flex> </Flex>
<Flex justifyContent="start" mb={3}> <Flex justifyContent="start" mb={3}>
<InputField <InputField
variables={variables} variables={variables}
setVariables={setVariables} setVariables={setVariables}
inputType={SwitchInputType.DISABLE_MULTI_FACTOR_AUTHENTICATION} inputType={SwitchInputType.DISABLE_MULTI_FACTOR_AUTHENTICATION}
hasReversedValue
/> />
</Flex> </Flex>
</Flex> </Flex>
{!variables.DISABLE_MULTI_FACTOR_AUTHENTICATION && ( </Stack>
<Flex alignItems="center"> <Divider paddingY={5} />
<Flex w="100%" alignItems="baseline" flexDir="column"> <Text fontSize="md" paddingTop={5} fontWeight="bold" mb={5}>
<Text fontSize="sm">Time Based OTP (TOTP):</Text> Enable Features
<Text fontSize="x-small">Note: to enable totp mfa</Text> </Text>
</Flex> <Stack spacing={6}>
<Flex justifyContent="start" mb={3}>
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.DISABLE_TOTP_LOGIN}
hasReversedValue
/>
</Flex>
</Flex>
)}
{!variables.DISABLE_MULTI_FACTOR_AUTHENTICATION && (
<Flex alignItems="center">
<Flex w="100%" alignItems="baseline" flexDir="column">
<Text fontSize="sm">EMAIL OTP:</Text>
<Text fontSize="x-small">Note: to enable email otp mfa</Text>
</Flex>
<Flex justifyContent="start" mb={3}>
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.DISABLE_MAIL_OTP_LOGIN}
hasReversedValue
/>
</Flex>
</Flex>
)}
<Flex alignItems="center"> <Flex alignItems="center">
<Flex w="100%" alignItems="baseline" flexDir="column"> <Flex w="100%" alignItems="baseline" flexDir="column">
<Text fontSize="sm"> <Text fontSize="sm">
@ -174,53 +125,6 @@ const Features = ({ variables, setVariables }: any) => {
/> />
</Flex> </Flex>
</Flex> </Flex>
<Flex>
<Flex w="100%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Playground:</Text>
</Flex>
<Flex justifyContent="start">
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.DISABLE_PLAYGROUND}
hasReversedValue
/>
</Flex>
</Flex>
</Stack>
<Divider paddingY={5} />
<Text fontSize="md" paddingTop={5} fontWeight="bold" mb={5}>
Cookie Security Features
</Text>
<Stack spacing={6}>
<Flex>
<Flex w="100%" alignItems="baseline" flexDir="column">
<Text fontSize="sm">Use Secure App Cookie:</Text>
<Text fontSize="x-small">
Note: If you set this to insecure, it will set{' '}
<code>sameSite</code> property of cookie to <code>lax</code> mode
</Text>
</Flex>
<Flex justifyContent="start">
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.APP_COOKIE_SECURE}
/>
</Flex>
</Flex>
<Flex>
<Flex w="100%" alignItems="baseline" flexDir="column">
<Text fontSize="sm">Use Secure Admin Cookie:</Text>
</Flex>
<Flex justifyContent="start">
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.ADMIN_COOKIE_SECURE}
/>
</Flex>
</Flex>
</Stack> </Stack>
</div> </div>
); );

View File

@ -37,13 +37,13 @@ const JSTConfigurations = ({
JSON.stringify({ JSON.stringify({
type: variables.JWT_TYPE, type: variables.JWT_TYPE,
key: variables.JWT_PUBLIC_KEY || variables.JWT_SECRET, key: variables.JWT_PUBLIC_KEY || variables.JWT_SECRET,
}), })
); );
toast({ toast({
title: `JWT config copied successfully`, title: `JWT config copied successfully`,
isClosable: true, isClosable: true,
status: 'success', status: 'success',
position: 'top-right', position: 'bottom-right',
}); });
} catch (err) { } catch (err) {
console.error({ console.error({
@ -54,13 +54,14 @@ const JSTConfigurations = ({
title: `Failed to copy JWT config`, title: `Failed to copy JWT config`,
isClosable: true, isClosable: true,
status: 'error', status: 'error',
position: 'top-right', position: 'bottom-right',
}); });
} }
}; };
return ( return (
<div> <div>
{' '}
<Flex <Flex
borderRadius={5} borderRadius={5}
width="100%" width="100%"

View File

@ -16,17 +16,8 @@ import {
FaLinkedin, FaLinkedin,
FaApple, FaApple,
FaTwitter, FaTwitter,
FaMicrosoft,
FaTwitch,
FaDiscord,
} from 'react-icons/fa'; } from 'react-icons/fa';
import { import { TextInputType, HiddenInputType } from '../../constants';
TextInputType,
HiddenInputType,
ResponseModes,
ResponseTypes,
SelectInputType,
} from '../../constants';
const OAuthConfig = ({ const OAuthConfig = ({
envVariables, envVariables,
@ -78,42 +69,6 @@ const OAuthConfig = ({
/> />
</Center> </Center>
</Flex> </Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Default Response Type:</Text>
</Flex>
<Flex
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '2'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={SelectInputType.DEFAULT_AUTHORIZE_RESPONSE_TYPE}
value={SelectInputType}
options={ResponseTypes}
/>
</Flex>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Default Response Mode:</Text>
</Flex>
<Flex
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '2'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={SelectInputType.DEFAULT_AUTHORIZE_RESPONSE_MODE}
value={SelectInputType}
options={ResponseModes}
/>
</Flex>
</Flex>
</Stack> </Stack>
<Divider mt={5} mb={2} color="blackAlpha.700" /> <Divider mt={5} mb={2} color="blackAlpha.700" />
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={4}> <Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={4}>
@ -310,44 +265,6 @@ const OAuthConfig = ({
/> />
</Center> </Center>
</Flex> </Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Center
w={isNotSmallerScreen ? '55px' : '35px'}
h="35px"
marginRight="1.5%"
border="1px solid #3b5998"
borderRadius="5px"
>
<FaDiscord style={{ color: '#7289da' }} />
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.DISCORD_CLIENT_ID}
placeholder="Discord 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.DISCORD_CLIENT_SECRET}
placeholder="Discord Client Secret"
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}> <Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Center <Center
w={isNotSmallerScreen ? '55px' : '35px'} w={isNotSmallerScreen ? '55px' : '35px'}
@ -386,136 +303,6 @@ const OAuthConfig = ({
/> />
</Center> </Center>
</Flex> </Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Center
w={isNotSmallerScreen ? '55px' : '35px'}
h="35px"
marginRight="1.5%"
border="1px solid #3b5998"
borderRadius="5px"
>
<FaMicrosoft />
</Center>
<Center
w={isNotSmallerScreen ? '35%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID}
placeholder="Microsoft Active Directory TenantID"
/>
</Center>
<Center
w={isNotSmallerScreen ? '35%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.MICROSOFT_CLIENT_ID}
placeholder="Microsoft Client ID"
/>
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.MICROSOFT_CLIENT_SECRET}
placeholder="Microsoft Client Secret"
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Center
w={isNotSmallerScreen ? '55px' : '35px'}
h="35px"
marginRight="1.5%"
border="1px solid #3b5998"
borderRadius="5px"
>
<FaTwitch />
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.TWITCH_CLIENT_ID}
placeholder="Twitch 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.TWITCH_CLIENT_SECRET}
placeholder="Twitch 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"
>
<img
src="https://authorizer.dev/_next/image?url=%2Fimages%2Froblox.png&w=25&q=25"
alt="Roblox"
/>
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.ROBLOX_CLIENT_ID}
placeholder="Roblox 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.ROBLOX_CLIENT_SECRET}
placeholder="Roblox Client Secret"
/>
</Center>
</Flex>
</Stack> </Stack>
</Box> </Box>
</div> </div>

View File

@ -1,28 +1,28 @@
import React from 'react'; import React from "react";
import { Flex, Stack, Center, Text, useMediaQuery } from '@chakra-ui/react'; import { Flex, Stack, Center, Text, useMediaQuery } from "@chakra-ui/react";
import InputField from '../InputField'; import InputField from "../InputField";
import { TextInputType } from '../../constants'; import { TextInputType } from "../../constants";
const OrganizationInfo = ({ variables, setVariables }: any) => { const OrganizationInfo = ({ variables, setVariables }: any) => {
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)'); const [isNotSmallerScreen] = useMediaQuery("(min-width:600px)");
return ( return (
<div> <div>
{' '} {" "}
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}> <Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
Organization Information Organization Information
</Text> </Text>
<Stack spacing={6} padding="2% 0%"> <Stack spacing={6} padding="2% 0%">
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}> <Flex direction={isNotSmallerScreen ? "row" : "column"}>
<Flex <Flex
w={isNotSmallerScreen ? '30%' : '40%'} w={isNotSmallerScreen ? "30%" : "40%"}
justifyContent="start" justifyContent="start"
alignItems="center" alignItems="center"
> >
<Text fontSize="sm">Organization Name:</Text> <Text fontSize="sm">Organization Name:</Text>
</Flex> </Flex>
<Center <Center
w={isNotSmallerScreen ? '70%' : '100%'} w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? '0' : '3'} mt={isNotSmallerScreen ? "0" : "3"}
> >
<InputField <InputField
borderRadius={5} borderRadius={5}
@ -32,17 +32,17 @@ const OrganizationInfo = ({ variables, setVariables }: any) => {
/> />
</Center> </Center>
</Flex> </Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}> <Flex direction={isNotSmallerScreen ? "row" : "column"}>
<Flex <Flex
w={isNotSmallerScreen ? '30%' : '40%'} w={isNotSmallerScreen ? "30%" : "40%"}
justifyContent="start" justifyContent="start"
alignItems="center" alignItems="center"
> >
<Text fontSize="sm">Organization Logo:</Text> <Text fontSize="sm">Organization Logo:</Text>
</Flex> </Flex>
<Center <Center
w={isNotSmallerScreen ? '70%' : '100%'} w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? '0' : '3'} mt={isNotSmallerScreen ? "0" : "3"}
> >
<InputField <InputField
borderRadius={5} borderRadius={5}

View File

@ -1,4 +1,4 @@
import React from 'react'; import React from "react";
import { import {
Flex, Flex,
Stack, Stack,
@ -8,10 +8,10 @@ import {
InputGroup, InputGroup,
InputRightElement, InputRightElement,
useMediaQuery, useMediaQuery,
} from '@chakra-ui/react'; } from "@chakra-ui/react";
import { FaRegEyeSlash, FaRegEye } from 'react-icons/fa'; import { FaRegEyeSlash, FaRegEye } from "react-icons/fa";
import InputField from '../InputField'; import InputField from "../InputField";
import { HiddenInputType } from '../../constants'; import { HiddenInputType } from "../../constants";
const SecurityAdminSecret = ({ const SecurityAdminSecret = ({
variables, variables,
setVariables, setVariables,
@ -20,10 +20,10 @@ const SecurityAdminSecret = ({
validateAdminSecretHandler, validateAdminSecretHandler,
adminSecret, adminSecret,
}: any) => { }: any) => {
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)'); const [isNotSmallerScreen] = useMediaQuery("(min-width:600px)");
return ( return (
<div> <div>
{' '} {" "}
<Text fontSize="md" paddingTop="2%" fontWeight="bold"> <Text fontSize="md" paddingTop="2%" fontWeight="bold">
Security (Admin Secret) Security (Admin Secret)
</Text> </Text>
@ -35,20 +35,20 @@ const SecurityAdminSecret = ({
borderRadius="5px" borderRadius="5px"
> >
<Flex <Flex
marginTop={isNotSmallerScreen ? '3%' : '5%'} marginTop={isNotSmallerScreen ? "3%" : "5%"}
direction={isNotSmallerScreen ? 'row' : 'column'} direction={isNotSmallerScreen ? "row" : "column"}
> >
<Flex <Flex
mt={3} mt={3}
w={isNotSmallerScreen ? '30%' : '40%'} w={isNotSmallerScreen ? "30%" : "40%"}
justifyContent="start" justifyContent="start"
alignItems="center" alignItems="center"
> >
<Text fontSize="sm">Old Admin Secret:</Text> <Text fontSize="sm">Old Admin Secret:</Text>
</Flex> </Flex>
<Center <Center
w={isNotSmallerScreen ? '70%' : '100%'} w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? '0' : '3'} mt={isNotSmallerScreen ? "0" : "3"}
> >
<InputGroup size="sm"> <InputGroup size="sm">
<Input <Input
@ -59,8 +59,8 @@ const SecurityAdminSecret = ({
onChange={(event: any) => validateAdminSecretHandler(event)} onChange={(event: any) => validateAdminSecretHandler(event)}
type={ type={
!fieldVisibility[HiddenInputType.OLD_ADMIN_SECRET] !fieldVisibility[HiddenInputType.OLD_ADMIN_SECRET]
? 'password' ? "password"
: 'text' : "text"
} }
/> />
<InputRightElement <InputRightElement
@ -104,18 +104,18 @@ const SecurityAdminSecret = ({
</Flex> </Flex>
<Flex <Flex
paddingBottom="3%" paddingBottom="3%"
direction={isNotSmallerScreen ? 'row' : 'column'} direction={isNotSmallerScreen ? "row" : "column"}
> >
<Flex <Flex
w={isNotSmallerScreen ? '30%' : '50%'} w={isNotSmallerScreen ? "30%" : "50%"}
justifyContent="start" justifyContent="start"
alignItems="center" alignItems="center"
> >
<Text fontSize="sm">New Admin Secret:</Text> <Text fontSize="sm">New Admin Secret:</Text>
</Flex> </Flex>
<Center <Center
w={isNotSmallerScreen ? '70%' : '100%'} w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? '0' : '3'} mt={isNotSmallerScreen ? "0" : "3"}
> >
<InputField <InputField
borderRadius={5} borderRadius={5}

View File

@ -73,7 +73,7 @@ const GenerateKeysModal = ({ jwtType, getData }: propTypes) => {
title: 'Error occurred generating jwt keys', title: 'Error occurred generating jwt keys',
isClosable: true, isClosable: true,
status: 'error', status: 'error',
position: 'top-right', position: 'bottom-right',
}); });
closeHandler(); closeHandler();
} else { } else {
@ -107,7 +107,7 @@ const GenerateKeysModal = ({ jwtType, getData }: propTypes) => {
title: 'Error occurred setting jwt keys', title: 'Error occurred setting jwt keys',
isClosable: true, isClosable: true,
status: 'error', status: 'error',
position: 'top-right', position: 'bottom-right',
}); });
return; return;
@ -116,7 +116,7 @@ const GenerateKeysModal = ({ jwtType, getData }: propTypes) => {
title: 'JWT keys updated successfully', title: 'JWT keys updated successfully',
isClosable: true, isClosable: true,
status: 'success', status: 'success',
position: 'top-right', position: 'bottom-right',
}); });
closeHandler(); closeHandler();
}; };
@ -167,7 +167,7 @@ const GenerateKeysModal = ({ jwtType, getData }: propTypes) => {
) : ( ) : (
<> <>
{Object.values(HMACEncryptionType).includes( {Object.values(HMACEncryptionType).includes(
stateVariables.JWT_TYPE, stateVariables.JWT_TYPE
) ? ( ) ? (
<Flex marginTop="8"> <Flex marginTop="8">
<Flex w="23%" justifyContent="start" alignItems="center"> <Flex w="23%" justifyContent="start" alignItems="center">

View File

@ -1,4 +1,4 @@
import React, { useState } from 'react'; import React from 'react';
import { import {
Box, Box,
Flex, Flex,
@ -13,12 +13,6 @@ import {
Textarea, Textarea,
Switch, Switch,
Text, Text,
MenuButton,
MenuList,
MenuItemOption,
MenuOptionGroup,
Button,
Menu,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { import {
FaRegClone, FaRegClone,
@ -26,7 +20,6 @@ import {
FaRegEyeSlash, FaRegEyeSlash,
FaPlus, FaPlus,
FaTimes, FaTimes,
FaAngleDown,
} from 'react-icons/fa'; } from 'react-icons/fa';
import { import {
ArrayInputOperations, ArrayInputOperations,
@ -37,7 +30,6 @@ import {
TextAreaInputType, TextAreaInputType,
SwitchInputType, SwitchInputType,
DateInputType, DateInputType,
MultiSelectInputType,
} from '../constants'; } from '../constants';
import { copyTextToClipboard } from '../utils'; import { copyTextToClipboard } from '../utils';
@ -47,18 +39,13 @@ const InputField = ({
setVariables, setVariables,
fieldVisibility, fieldVisibility,
setFieldVisibility, setFieldVisibility,
availableRoles,
// This prop is added to improve the user experience for the boolean ENV variable having `DISABLE_` prefix, as those values need to be considered in inverted form.
hasReversedValue,
...downshiftProps ...downshiftProps
}: any) => { }: any) => {
const props = { const props = {
size: 'sm', size: 'sm',
...downshiftProps, ...downshiftProps,
}; };
const [availableUserRoles, setAvailableUserRoles] = const [inputFieldVisibility, setInputFieldVisibility] = React.useState<
useState<string[]>(availableRoles);
const [inputFieldVisibility, setInputFieldVisibility] = useState<
Record<string, boolean> Record<string, boolean>
>({ >({
ROLES: false, ROLES: false,
@ -67,7 +54,7 @@ const InputField = ({
ALLOWED_ORIGINS: false, ALLOWED_ORIGINS: false,
roles: false, roles: false,
}); });
const [inputData, setInputData] = useState<Record<string, string>>({ const [inputData, setInputData] = React.useState<Record<string, string>>({
ROLES: '', ROLES: '',
DEFAULT_ROLES: '', DEFAULT_ROLES: '',
PROTECTED_ROLES: '', PROTECTED_ROLES: '',
@ -77,7 +64,7 @@ const InputField = ({
const updateInputHandler = ( const updateInputHandler = (
type: string, type: string,
operation: any, operation: any,
role: string = '', role: string = ''
) => { ) => {
if (operation === ArrayInputOperations.APPEND) { if (operation === ArrayInputOperations.APPEND) {
if (inputData[type] !== '') { if (inputData[type] !== '') {
@ -91,7 +78,7 @@ const InputField = ({
} }
if (operation === ArrayInputOperations.REMOVE) { if (operation === ArrayInputOperations.REMOVE) {
let updatedEnvVars = variables[type].filter( let updatedEnvVars = variables[type].filter(
(item: string) => item !== role, (item: string) => item !== role
); );
setVariables({ setVariables({
...variables, ...variables,
@ -108,7 +95,7 @@ const InputField = ({
onChange={( onChange={(
event: Event & { event: Event & {
target: HTMLInputElement; target: HTMLInputElement;
}, }
) => ) =>
setVariables({ setVariables({
...variables, ...variables,
@ -129,11 +116,11 @@ const InputField = ({
<InputGroup size="sm"> <InputGroup size="sm">
<Input <Input
{...props} {...props}
value={variables[inputType] || ''} value={variables[inputType] ?? ''}
onChange={( onChange={(
event: Event & { event: Event & {
target: HTMLInputElement; target: HTMLInputElement;
}, }
) => ) =>
setVariables({ setVariables({
...variables, ...variables,
@ -220,7 +207,7 @@ const InputField = ({
updateInputHandler( updateInputHandler(
inputType, inputType,
ArrayInputOperations.REMOVE, ArrayInputOperations.REMOVE,
role, role
) )
} }
/> />
@ -234,7 +221,7 @@ const InputField = ({
size="xs" size="xs"
minW="150px" minW="150px"
placeholder="add a new value" placeholder="add a new value"
value={inputData[inputType] || ''} value={inputData[inputType] ?? ''}
onChange={(e: any) => { onChange={(e: any) => {
setInputData({ ...inputData, [inputType]: e.target.value }); setInputData({ ...inputData, [inputType]: e.target.value });
}} }}
@ -291,87 +278,6 @@ const InputField = ({
</Select> </Select>
); );
} }
if (Object.values(MultiSelectInputType).includes(inputType)) {
return (
<Flex w="100%" style={{ position: 'relative' }}>
<Flex
border="1px solid #e2e8f0"
w="100%"
borderRadius="var(--chakra-radii-sm)"
p="1% 0 0 2.5%"
overflowX={variables[inputType].length > 3 ? 'scroll' : 'hidden'}
overflowY="hidden"
justifyContent="space-between"
alignItems="center"
>
<Flex justifyContent="start" alignItems="center" w="100%" wrap="wrap">
{variables[inputType].map((role: string, index: number) => (
<Box key={index} margin="0.5%" role="group">
<Tag
size="sm"
variant="outline"
colorScheme="gray"
minW="fit-content"
>
<TagLabel cursor="default">{role}</TagLabel>
<TagRightIcon
boxSize="12px"
as={FaTimes}
display="none"
cursor="pointer"
_groupHover={{ display: 'block' }}
onClick={() =>
updateInputHandler(
inputType,
ArrayInputOperations.REMOVE,
role,
)
}
/>
</Tag>
</Box>
))}
</Flex>
<Menu matchWidth={true}>
<MenuButton px="10px" py="7.5px">
<FaAngleDown />
</MenuButton>
<MenuList
position="absolute"
top="0"
right="0"
zIndex="10"
maxH="150"
overflowX="scroll"
>
<MenuOptionGroup
title={undefined}
value={variables[inputType]}
type="checkbox"
onChange={(values: string[] | string) => {
setVariables({
...variables,
[inputType]: values,
});
}}
>
{availableUserRoles.map((role) => {
return (
<MenuItemOption
key={`multiselect-menu-${role}`}
value={role}
>
{role}
</MenuItemOption>
);
})}
</MenuOptionGroup>
</MenuList>
</Menu>
</Flex>
</Flex>
);
}
if (Object.values(TextAreaInputType).includes(inputType)) { if (Object.values(TextAreaInputType).includes(inputType)) {
return ( return (
<Textarea <Textarea
@ -382,7 +288,7 @@ const InputField = ({
onChange={( onChange={(
event: Event & { event: Event & {
target: HTMLInputElement; target: HTMLInputElement;
}, }
) => ) =>
setVariables({ setVariables({
...variables, ...variables,
@ -400,9 +306,7 @@ const InputField = ({
</Text> </Text>
<Switch <Switch
size="md" size="md"
isChecked={ isChecked={variables[inputType]}
hasReversedValue ? !variables[inputType] : variables[inputType]
}
onChange={() => { onChange={() => {
setVariables({ setVariables({
...variables, ...variables,

View File

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

View File

@ -218,7 +218,7 @@ export const Sidebar = ({ onClose, ...rest }: SidebarProps) => {
</NavItem>{' '} </NavItem>{' '}
</Text> </Text>
</NavLink> </NavLink>
), )
)} )}
<Link <Link
href="/playground" href="/playground"

View File

@ -29,10 +29,6 @@ import {
Tbody, Tbody,
Td, Td,
Code, Code,
Radio,
RadioGroup,
Stack,
Textarea,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { FaPlus, FaAngleDown, FaAngleUp } from 'react-icons/fa'; import { FaPlus, FaAngleDown, FaAngleUp } from 'react-icons/fa';
import { useClient } from 'urql'; import { useClient } from 'urql';
@ -42,7 +38,6 @@ import {
EmailTemplateInputDataFields, EmailTemplateInputDataFields,
emailTemplateEventNames, emailTemplateEventNames,
emailTemplateVariables, emailTemplateVariables,
EmailTemplateEditors,
} from '../constants'; } from '../constants';
import { capitalizeFirstLetter } from '../utils'; import { capitalizeFirstLetter } from '../utils';
import { AddEmailTemplate, EditEmailTemplate } from '../graphql/mutation'; import { AddEmailTemplate, EditEmailTemplate } from '../graphql/mutation';
@ -71,8 +66,6 @@ interface templateVariableDataTypes {
interface emailTemplateDataType { interface emailTemplateDataType {
[EmailTemplateInputDataFields.EVENT_NAME]: string; [EmailTemplateInputDataFields.EVENT_NAME]: string;
[EmailTemplateInputDataFields.SUBJECT]: string; [EmailTemplateInputDataFields.SUBJECT]: string;
[EmailTemplateInputDataFields.TEMPLATE]: string;
[EmailTemplateInputDataFields.DESIGN]: string;
} }
interface validatorDataType { interface validatorDataType {
@ -82,8 +75,6 @@ interface validatorDataType {
const initTemplateData: emailTemplateDataType = { const initTemplateData: emailTemplateDataType = {
[EmailTemplateInputDataFields.EVENT_NAME]: emailTemplateEventNames.Signup, [EmailTemplateInputDataFields.EVENT_NAME]: emailTemplateEventNames.Signup,
[EmailTemplateInputDataFields.SUBJECT]: '', [EmailTemplateInputDataFields.SUBJECT]: '',
[EmailTemplateInputDataFields.TEMPLATE]: '',
[EmailTemplateInputDataFields.DESIGN]: '',
}; };
const initTemplateValidatorData: validatorDataType = { const initTemplateValidatorData: validatorDataType = {
@ -100,9 +91,6 @@ const UpdateEmailTemplate = ({
const emailEditorRef = useRef(null); const emailEditorRef = useRef(null);
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
const [loading, setLoading] = useState<boolean>(false); const [loading, setLoading] = useState<boolean>(false);
const [editor, setEditor] = useState<string>(
EmailTemplateEditors.PLAIN_HTML_EDITOR,
);
const [templateVariables, setTemplateVariables] = useState< const [templateVariables, setTemplateVariables] = useState<
templateVariableDataTypes[] templateVariableDataTypes[]
>([]); >([]);
@ -119,11 +107,9 @@ const UpdateEmailTemplate = ({
if (selectedTemplate) { if (selectedTemplate) {
const { design } = selectedTemplate; const { design } = selectedTemplate;
try { try {
if (design) {
const designData = JSON.parse(design); const designData = JSON.parse(design);
// @ts-ignore // @ts-ignore
emailEditorRef.current.editor.loadDesign(designData); emailEditorRef.current.editor.loadDesign(designData);
}
} catch (error) { } catch (error) {
console.error(error); console.error(error);
onClose(); onClose();
@ -150,7 +136,24 @@ const UpdateEmailTemplate = ({
); );
}; };
const updateTemplate = async (params: emailTemplateDataType) => { const saveData = async () => {
if (!validateData()) return;
setLoading(true);
// @ts-ignore
return await emailEditorRef.current.editor.exportHtml(async (data) => {
const { design, html } = data;
if (!html || !design) {
setLoading(false);
return;
}
const params = {
[EmailTemplateInputDataFields.EVENT_NAME]:
templateData[EmailTemplateInputDataFields.EVENT_NAME],
[EmailTemplateInputDataFields.SUBJECT]:
templateData[EmailTemplateInputDataFields.SUBJECT],
[EmailTemplateInputDataFields.TEMPLATE]: html.trim(),
[EmailTemplateInputDataFields.DESIGN]: JSON.stringify(design),
};
let res: any = {}; let res: any = {};
if ( if (
view === UpdateModalViews.Edit && view === UpdateModalViews.Edit &&
@ -173,7 +176,7 @@ const UpdateEmailTemplate = ({
title: capitalizeFirstLetter(res.error.message), title: capitalizeFirstLetter(res.error.message),
isClosable: true, isClosable: true,
status: 'error', status: 'error',
position: 'top-right', position: 'bottom-right',
}); });
} else if ( } else if (
res.data?._add_email_template || res.data?._add_email_template ||
@ -182,11 +185,11 @@ const UpdateEmailTemplate = ({
toast({ toast({
title: capitalizeFirstLetter( title: capitalizeFirstLetter(
res.data?._add_email_template?.message || res.data?._add_email_template?.message ||
res.data?._update_email_template?.message, res.data?._update_email_template?.message
), ),
isClosable: true, isClosable: true,
status: 'success', status: 'success',
position: 'top-right', position: 'bottom-right',
}); });
setTemplateData({ setTemplateData({
...initTemplateData, ...initTemplateData,
@ -194,41 +197,9 @@ const UpdateEmailTemplate = ({
setValidator({ ...initTemplateValidatorData }); setValidator({ ...initTemplateValidatorData });
fetchEmailTemplatesData(); fetchEmailTemplatesData();
} }
};
const saveData = async () => {
if (!validateData()) return;
setLoading(true);
let params: emailTemplateDataType = {
[EmailTemplateInputDataFields.EVENT_NAME]:
templateData[EmailTemplateInputDataFields.EVENT_NAME],
[EmailTemplateInputDataFields.SUBJECT]:
templateData[EmailTemplateInputDataFields.SUBJECT],
[EmailTemplateInputDataFields.TEMPLATE]:
templateData[EmailTemplateInputDataFields.TEMPLATE],
[EmailTemplateInputDataFields.DESIGN]: '',
};
if (editor === EmailTemplateEditors.UNLAYER_EDITOR) {
// @ts-ignore
await emailEditorRef.current.editor.exportHtml(async (data) => {
const { design, html } = data;
if (!html || !design) {
setLoading(false);
return;
}
params = {
...params,
[EmailTemplateInputDataFields.TEMPLATE]: html.trim(),
[EmailTemplateInputDataFields.DESIGN]: JSON.stringify(design),
};
await updateTemplate(params);
});
} else {
await updateTemplate(params);
}
view === UpdateModalViews.ADD && onClose(); view === UpdateModalViews.ADD && onClose();
});
}; };
const resetData = () => { const resetData = () => {
if (selectedTemplate) { if (selectedTemplate) {
setTemplateData(selectedTemplate); setTemplateData(selectedTemplate);
@ -236,8 +207,6 @@ const UpdateEmailTemplate = ({
setTemplateData({ ...initTemplateData }); setTemplateData({ ...initTemplateData });
} }
}; };
// set template data if edit modal is open
useEffect(() => { useEffect(() => {
if ( if (
isOpen && isOpen &&
@ -245,15 +214,13 @@ const UpdateEmailTemplate = ({
selectedTemplate && selectedTemplate &&
Object.keys(selectedTemplate || {}).length Object.keys(selectedTemplate || {}).length
) { ) {
const { id, created_at, ...rest } = selectedTemplate; const { id, created_at, template, design, ...rest } = selectedTemplate;
setTemplateData(rest); setTemplateData(rest);
} }
}, [isOpen]); }, [isOpen]);
// set template variables
useEffect(() => { useEffect(() => {
const updatedTemplateVariables = Object.entries( const updatedTemplateVariables = Object.entries(
emailTemplateVariables, emailTemplateVariables
).reduce((acc, [key, val]): any => { ).reduce((acc, [key, val]): any => {
if ( if (
(templateData[EmailTemplateInputDataFields.EVENT_NAME] !== (templateData[EmailTemplateInputDataFields.EVENT_NAME] !==
@ -277,51 +244,6 @@ const UpdateEmailTemplate = ({
setTemplateVariables(updatedTemplateVariables); setTemplateVariables(updatedTemplateVariables);
}, [templateData[EmailTemplateInputDataFields.EVENT_NAME]]); }, [templateData[EmailTemplateInputDataFields.EVENT_NAME]]);
// change editor
useEffect(() => {
if (isOpen && selectedTemplate) {
const { design } = selectedTemplate;
if (design) {
setEditor(EmailTemplateEditors.UNLAYER_EDITOR);
} else {
setEditor(EmailTemplateEditors.PLAIN_HTML_EDITOR);
}
}
}, [isOpen, selectedTemplate]);
// reset fields when editor is changed
useEffect(() => {
if (selectedTemplate?.design) {
if (editor === EmailTemplateEditors.UNLAYER_EDITOR) {
setTemplateData({
...templateData,
[EmailTemplateInputDataFields.TEMPLATE]: selectedTemplate.template,
[EmailTemplateInputDataFields.DESIGN]: selectedTemplate.design,
});
} else {
setTemplateData({
...templateData,
[EmailTemplateInputDataFields.TEMPLATE]: '',
[EmailTemplateInputDataFields.DESIGN]: '',
});
}
} else if (selectedTemplate?.template) {
if (editor === EmailTemplateEditors.UNLAYER_EDITOR) {
setTemplateData({
...templateData,
[EmailTemplateInputDataFields.TEMPLATE]: '',
[EmailTemplateInputDataFields.DESIGN]: '',
});
} else {
setTemplateData({
...templateData,
[EmailTemplateInputDataFields.TEMPLATE]: selectedTemplate?.template,
[EmailTemplateInputDataFields.DESIGN]: '',
});
}
}
}, [editor]);
return ( return (
<> <>
{view === UpdateModalViews.ADD ? ( {view === UpdateModalViews.ADD ? (
@ -445,7 +367,7 @@ const UpdateEmailTemplate = ({
onChange={(e) => onChange={(e) =>
inputChangehandler( inputChangehandler(
EmailTemplateInputDataFields.EVENT_NAME, EmailTemplateInputDataFields.EVENT_NAME,
e.currentTarget.value, e.currentTarget.value
) )
} }
> >
@ -454,7 +376,7 @@ const UpdateEmailTemplate = ({
<option value={value} key={key}> <option value={value} key={key}>
{key} {key}
</option> </option>
), )
)} )}
</Select> </Select>
</Flex> </Flex>
@ -479,7 +401,7 @@ const UpdateEmailTemplate = ({
onChange={(e) => onChange={(e) =>
inputChangehandler( inputChangehandler(
EmailTemplateInputDataFields.SUBJECT, EmailTemplateInputDataFields.SUBJECT,
e.currentTarget.value, e.currentTarget.value
) )
} }
/> />
@ -492,22 +414,7 @@ const UpdateEmailTemplate = ({
alignItems="center" alignItems="center"
marginBottom="2%" marginBottom="2%"
> >
<Flex flex="1">Template Body</Flex> Template Body
<Flex flex="3">
<RadioGroup
onChange={(value) => setEditor(value)}
value={editor}
>
<Stack direction="row" spacing="50px">
<Radio value={EmailTemplateEditors.PLAIN_HTML_EDITOR}>
Plain HTML
</Radio>
<Radio value={EmailTemplateEditors.UNLAYER_EDITOR}>
Unlayer Editor
</Radio>
</Stack>
</RadioGroup>
</Flex>
</Flex> </Flex>
<Flex <Flex
width="100%" width="100%"
@ -516,22 +423,7 @@ const UpdateEmailTemplate = ({
border="1px solid" border="1px solid"
borderColor="gray.200" borderColor="gray.200"
> >
{editor === EmailTemplateEditors.UNLAYER_EDITOR ? (
<EmailEditor ref={emailEditorRef} onReady={onReady} /> <EmailEditor ref={emailEditorRef} onReady={onReady} />
) : (
<Textarea
value={templateData.template}
onChange={(e) => {
setTemplateData({
...templateData,
[EmailTemplateInputDataFields.TEMPLATE]: e.target.value,
});
}}
placeholder="Template HTML"
border="0"
height="500px"
/>
)}
</Flex> </Flex>
</Flex> </Flex>
</ModalBody> </ModalBody>

View File

@ -63,7 +63,6 @@ interface headersValidatorDataType {
interface selecetdWebhookDataTypes { interface selecetdWebhookDataTypes {
[WebhookInputDataFields.ID]: string; [WebhookInputDataFields.ID]: string;
[WebhookInputDataFields.EVENT_NAME]: string; [WebhookInputDataFields.EVENT_NAME]: string;
[WebhookInputDataFields.EVENT_DESCRIPTION]?: string;
[WebhookInputDataFields.ENDPOINT]: string; [WebhookInputDataFields.ENDPOINT]: string;
[WebhookInputDataFields.ENABLED]: boolean; [WebhookInputDataFields.ENABLED]: boolean;
[WebhookInputDataFields.HEADERS]?: Record<string, string>; [WebhookInputDataFields.HEADERS]?: Record<string, string>;
@ -87,7 +86,6 @@ const initHeadersValidatorData: headersValidatorDataType = {
interface webhookDataType { interface webhookDataType {
[WebhookInputDataFields.EVENT_NAME]: string; [WebhookInputDataFields.EVENT_NAME]: string;
[WebhookInputDataFields.EVENT_DESCRIPTION]?: string;
[WebhookInputDataFields.ENDPOINT]: string; [WebhookInputDataFields.ENDPOINT]: string;
[WebhookInputDataFields.ENABLED]: boolean; [WebhookInputDataFields.ENABLED]: boolean;
[WebhookInputDataFields.HEADERS]: headersDataType[]; [WebhookInputDataFields.HEADERS]: headersDataType[];
@ -100,7 +98,6 @@ interface validatorDataType {
const initWebhookData: webhookDataType = { const initWebhookData: webhookDataType = {
[WebhookInputDataFields.EVENT_NAME]: webhookEventNames['User login'], [WebhookInputDataFields.EVENT_NAME]: webhookEventNames['User login'],
[WebhookInputDataFields.EVENT_DESCRIPTION]: '',
[WebhookInputDataFields.ENDPOINT]: '', [WebhookInputDataFields.ENDPOINT]: '',
[WebhookInputDataFields.ENABLED]: true, [WebhookInputDataFields.ENABLED]: true,
[WebhookInputDataFields.HEADERS]: [{ ...initHeadersData }], [WebhookInputDataFields.HEADERS]: [{ ...initHeadersData }],
@ -129,13 +126,13 @@ const UpdateWebhookModal = ({
...initWebhookValidatorData, ...initWebhookValidatorData,
}); });
const [verifiedStatus, setVerifiedStatus] = useState<webhookVerifiedStatus>( const [verifiedStatus, setVerifiedStatus] = useState<webhookVerifiedStatus>(
webhookVerifiedStatus.PENDING, webhookVerifiedStatus.PENDING
); );
const inputChangehandler = ( const inputChangehandler = (
inputType: string, inputType: string,
value: any, value: any,
headerInputType: string = WebhookInputHeaderFields.KEY, headerInputType: string = WebhookInputHeaderFields.KEY,
headerIndex: number = 0, headerIndex: number = 0
) => { ) => {
if ( if (
verifiedStatus !== webhookVerifiedStatus.PENDING && verifiedStatus !== webhookVerifiedStatus.PENDING &&
@ -147,9 +144,6 @@ const UpdateWebhookModal = ({
case WebhookInputDataFields.EVENT_NAME: case WebhookInputDataFields.EVENT_NAME:
setWebhook({ ...webhook, [inputType]: value }); setWebhook({ ...webhook, [inputType]: value });
break; break;
case WebhookInputDataFields.EVENT_DESCRIPTION:
setWebhook({ ...webhook, [inputType]: value });
break;
case WebhookInputDataFields.ENDPOINT: case WebhookInputDataFields.ENDPOINT:
setWebhook({ ...webhook, [inputType]: value }); setWebhook({ ...webhook, [inputType]: value });
setValidator({ setValidator({
@ -244,7 +238,7 @@ const UpdateWebhookModal = ({
validator[WebhookInputDataFields.ENDPOINT] && validator[WebhookInputDataFields.ENDPOINT] &&
!validator[WebhookInputDataFields.HEADERS].some( !validator[WebhookInputDataFields.HEADERS].some(
(headerData: headersValidatorDataType) => (headerData: headersValidatorDataType) =>
!headerData.key || !headerData.value, !headerData.key || !headerData.value
) )
); );
}; };
@ -252,8 +246,6 @@ const UpdateWebhookModal = ({
let params: any = { let params: any = {
[WebhookInputDataFields.EVENT_NAME]: [WebhookInputDataFields.EVENT_NAME]:
webhook[WebhookInputDataFields.EVENT_NAME], webhook[WebhookInputDataFields.EVENT_NAME],
[WebhookInputDataFields.EVENT_DESCRIPTION]:
webhook[WebhookInputDataFields.EVENT_DESCRIPTION],
[WebhookInputDataFields.ENDPOINT]: [WebhookInputDataFields.ENDPOINT]:
webhook[WebhookInputDataFields.ENDPOINT], webhook[WebhookInputDataFields.ENDPOINT],
[WebhookInputDataFields.ENABLED]: webhook[WebhookInputDataFields.ENABLED], [WebhookInputDataFields.ENABLED]: webhook[WebhookInputDataFields.ENABLED],
@ -264,7 +256,7 @@ const UpdateWebhookModal = ({
(acc, data) => { (acc, data) => {
return data.key ? { ...acc, [data.key]: data.value } : acc; return data.key ? { ...acc, [data.key]: data.value } : acc;
}, },
{}, {}
); );
if (Object.keys(headers).length) { if (Object.keys(headers).length) {
params[WebhookInputDataFields.HEADERS] = headers; params[WebhookInputDataFields.HEADERS] = headers;
@ -298,16 +290,16 @@ const UpdateWebhookModal = ({
title: capitalizeFirstLetter(res.error.message), title: capitalizeFirstLetter(res.error.message),
isClosable: true, isClosable: true,
status: 'error', status: 'error',
position: 'top-right', position: 'bottom-right',
}); });
} else if (res.data?._add_webhook || res.data?._update_webhook) { } else if (res.data?._add_webhook || res.data?._update_webhook) {
toast({ toast({
title: capitalizeFirstLetter( title: capitalizeFirstLetter(
res.data?._add_webhook?.message || res.data?._update_webhook?.message, res.data?._add_webhook?.message || res.data?._update_webhook?.message
), ),
isClosable: true, isClosable: true,
status: 'success', status: 'success',
position: 'top-right', position: 'bottom-right',
}); });
setWebhook({ setWebhook({
...initWebhookData, ...initWebhookData,
@ -341,7 +333,7 @@ const UpdateWebhookModal = ({
setValidator({ setValidator({
...validator, ...validator,
[WebhookInputDataFields.HEADERS]: new Array( [WebhookInputDataFields.HEADERS]: new Array(
formattedHeadersData.length, formattedHeadersData.length
) )
.fill({}) .fill({})
.map(() => ({ ...initHeadersValidatorData })), .map(() => ({ ...initHeadersValidatorData })),
@ -410,13 +402,11 @@ const UpdateWebhookModal = ({
<Flex flex="3"> <Flex flex="3">
<Select <Select
size="md" size="md"
value={ value={webhook[WebhookInputDataFields.EVENT_NAME]}
webhook[WebhookInputDataFields.EVENT_NAME].split('-')[0]
}
onChange={(e) => onChange={(e) =>
inputChangehandler( inputChangehandler(
WebhookInputDataFields.EVENT_NAME, WebhookInputDataFields.EVENT_NAME,
e.currentTarget.value, e.currentTarget.value
) )
} }
> >
@ -425,35 +415,11 @@ const UpdateWebhookModal = ({
<option value={value} key={key}> <option value={value} key={key}>
{key} {key}
</option> </option>
), )
)} )}
</Select> </Select>
</Flex> </Flex>
</Flex> </Flex>
<Flex
width="100%"
justifyContent="start"
alignItems="center"
marginBottom="5%"
>
<Flex flex="1">Event Description</Flex>
<Flex flex="3">
<InputGroup size="md">
<Input
pr="4.5rem"
type="text"
placeholder="User event"
value={webhook[WebhookInputDataFields.EVENT_DESCRIPTION]}
onChange={(e) =>
inputChangehandler(
WebhookInputDataFields.EVENT_DESCRIPTION,
e.currentTarget.value,
)
}
/>
</InputGroup>
</Flex>
</Flex>
<Flex <Flex
width="100%" width="100%"
justifyContent="start" justifyContent="start"
@ -472,7 +438,7 @@ const UpdateWebhookModal = ({
onChange={(e) => onChange={(e) =>
inputChangehandler( inputChangehandler(
WebhookInputDataFields.ENDPOINT, WebhookInputDataFields.ENDPOINT,
e.currentTarget.value, e.currentTarget.value
) )
} }
/> />
@ -496,7 +462,7 @@ const UpdateWebhookModal = ({
onChange={() => onChange={() =>
inputChangehandler( inputChangehandler(
WebhookInputDataFields.ENABLED, WebhookInputDataFields.ENABLED,
!webhook[WebhookInputDataFields.ENABLED], !webhook[WebhookInputDataFields.ENABLED]
) )
} }
/> />
@ -551,7 +517,7 @@ const UpdateWebhookModal = ({
WebhookInputDataFields.HEADERS, WebhookInputDataFields.HEADERS,
e.target.value, e.target.value,
WebhookInputHeaderFields.KEY, WebhookInputHeaderFields.KEY,
index, index
) )
} }
width="30%" width="30%"
@ -574,7 +540,7 @@ const UpdateWebhookModal = ({
WebhookInputDataFields.HEADERS, WebhookInputDataFields.HEADERS,
e.target.value, e.target.value,
WebhookInputHeaderFields.VALUE, WebhookInputHeaderFields.VALUE,
index, index
) )
} }
width="65%" width="65%"
@ -594,7 +560,7 @@ const UpdateWebhookModal = ({
</InputRightElement> </InputRightElement>
</InputGroup> </InputGroup>
</Flex> </Flex>
), )
)} )}
</Flex> </Flex>
<Divider marginY={5} /> <Divider marginY={5} />

View File

@ -161,15 +161,15 @@ const ViewWebhookLogsModal = ({
<Td> <Td>
<Text fontSize="sm">{`${logData.id.substring( <Text fontSize="sm">{`${logData.id.substring(
0, 0,
5, 5
)}***${logData.id.substring( )}***${logData.id.substring(
logData.id.length - 5, logData.id.length - 5,
logData.id.length, logData.id.length
)}`}</Text> )}`}</Text>
</Td> </Td>
<Td> <Td>
{dayjs(logData.created_at * 1000).format( {dayjs(logData.created_at * 1000).format(
'MMM DD, YYYY', 'MMM DD, YYYY'
)} )}
</Td> </Td>
<Td> <Td>

View File

@ -9,20 +9,13 @@ export const TextInputType = {
FACEBOOK_CLIENT_ID: 'FACEBOOK_CLIENT_ID', FACEBOOK_CLIENT_ID: 'FACEBOOK_CLIENT_ID',
LINKEDIN_CLIENT_ID: 'LINKEDIN_CLIENT_ID', LINKEDIN_CLIENT_ID: 'LINKEDIN_CLIENT_ID',
APPLE_CLIENT_ID: 'APPLE_CLIENT_ID', APPLE_CLIENT_ID: 'APPLE_CLIENT_ID',
DISCORD_CLIENT_ID: 'DISCORD_CLIENT_ID',
TWITTER_CLIENT_ID: 'TWITTER_CLIENT_ID', TWITTER_CLIENT_ID: 'TWITTER_CLIENT_ID',
MICROSOFT_CLIENT_ID: 'MICROSOFT_CLIENT_ID',
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID: 'MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID',
TWITCH_CLIENT_ID: 'TWITCH_CLIENT_ID',
ROBLOX_CLIENT_ID: 'ROBLOX_CLIENT_ID',
JWT_ROLE_CLAIM: 'JWT_ROLE_CLAIM', JWT_ROLE_CLAIM: 'JWT_ROLE_CLAIM',
REDIS_URL: 'REDIS_URL', REDIS_URL: 'REDIS_URL',
SMTP_HOST: 'SMTP_HOST', SMTP_HOST: 'SMTP_HOST',
SMTP_PORT: 'SMTP_PORT', SMTP_PORT: 'SMTP_PORT',
SMTP_USERNAME: 'SMTP_USERNAME', SMTP_USERNAME: 'SMTP_USERNAME',
SMTP_LOCAL_NAME: 'SMTP_LOCAL_NAME',
SENDER_EMAIL: 'SENDER_EMAIL', SENDER_EMAIL: 'SENDER_EMAIL',
SENDER_NAME: 'SENDER_NAME',
ORGANIZATION_NAME: 'ORGANIZATION_NAME', ORGANIZATION_NAME: 'ORGANIZATION_NAME',
ORGANIZATION_LOGO: 'ORGANIZATION_LOGO', ORGANIZATION_LOGO: 'ORGANIZATION_LOGO',
DATABASE_NAME: 'DATABASE_NAME', DATABASE_NAME: 'DATABASE_NAME',
@ -43,11 +36,7 @@ export const HiddenInputType = {
FACEBOOK_CLIENT_SECRET: 'FACEBOOK_CLIENT_SECRET', FACEBOOK_CLIENT_SECRET: 'FACEBOOK_CLIENT_SECRET',
LINKEDIN_CLIENT_SECRET: 'LINKEDIN_CLIENT_SECRET', LINKEDIN_CLIENT_SECRET: 'LINKEDIN_CLIENT_SECRET',
APPLE_CLIENT_SECRET: 'APPLE_CLIENT_SECRET', APPLE_CLIENT_SECRET: 'APPLE_CLIENT_SECRET',
DISCORD_CLIENT_SECRET: 'DISCORD_CLIENT_SECRET',
TWITTER_CLIENT_SECRET: 'TWITTER_CLIENT_SECRET', TWITTER_CLIENT_SECRET: 'TWITTER_CLIENT_SECRET',
MICROSOFT_CLIENT_SECRET: 'MICROSOFT_CLIENT_SECRET',
TWITCH_CLIENT_SECRET: 'TWITCH_CLIENT_SECRET',
ROBLOX_CLIENT_SECRET: 'ROBLOX_CLIENT_SECRET',
JWT_SECRET: 'JWT_SECRET', JWT_SECRET: 'JWT_SECRET',
SMTP_PASSWORD: 'SMTP_PASSWORD', SMTP_PASSWORD: 'SMTP_PASSWORD',
ADMIN_SECRET: 'ADMIN_SECRET', ADMIN_SECRET: 'ADMIN_SECRET',
@ -59,17 +48,12 @@ export const ArrayInputType = {
DEFAULT_ROLES: 'DEFAULT_ROLES', DEFAULT_ROLES: 'DEFAULT_ROLES',
PROTECTED_ROLES: 'PROTECTED_ROLES', PROTECTED_ROLES: 'PROTECTED_ROLES',
ALLOWED_ORIGINS: 'ALLOWED_ORIGINS', ALLOWED_ORIGINS: 'ALLOWED_ORIGINS',
USER_ROLES: 'roles',
}; };
export const SelectInputType = { export const SelectInputType = {
JWT_TYPE: 'JWT_TYPE', JWT_TYPE: 'JWT_TYPE',
GENDER: 'gender', GENDER: 'gender',
DEFAULT_AUTHORIZE_RESPONSE_TYPE: 'DEFAULT_AUTHORIZE_RESPONSE_TYPE',
DEFAULT_AUTHORIZE_RESPONSE_MODE: 'DEFAULT_AUTHORIZE_RESPONSE_MODE',
};
export const MultiSelectInputType = {
USER_ROLES: 'roles',
}; };
export const TextAreaInputType = { export const TextAreaInputType = {
@ -79,21 +63,15 @@ export const TextAreaInputType = {
}; };
export const SwitchInputType = { export const SwitchInputType = {
APP_COOKIE_SECURE: 'APP_COOKIE_SECURE',
ADMIN_COOKIE_SECURE: 'ADMIN_COOKIE_SECURE',
DISABLE_LOGIN_PAGE: 'DISABLE_LOGIN_PAGE', DISABLE_LOGIN_PAGE: 'DISABLE_LOGIN_PAGE',
DISABLE_MAGIC_LINK_LOGIN: 'DISABLE_MAGIC_LINK_LOGIN', DISABLE_MAGIC_LINK_LOGIN: 'DISABLE_MAGIC_LINK_LOGIN',
DISABLE_EMAIL_VERIFICATION: 'DISABLE_EMAIL_VERIFICATION', DISABLE_EMAIL_VERIFICATION: 'DISABLE_EMAIL_VERIFICATION',
DISABLE_BASIC_AUTHENTICATION: 'DISABLE_BASIC_AUTHENTICATION', DISABLE_BASIC_AUTHENTICATION: 'DISABLE_BASIC_AUTHENTICATION',
DISABLE_MOBILE_BASIC_AUTHENTICATION: 'DISABLE_MOBILE_BASIC_AUTHENTICATION',
DISABLE_SIGN_UP: 'DISABLE_SIGN_UP', DISABLE_SIGN_UP: 'DISABLE_SIGN_UP',
DISABLE_REDIS_FOR_ENV: 'DISABLE_REDIS_FOR_ENV', DISABLE_REDIS_FOR_ENV: 'DISABLE_REDIS_FOR_ENV',
DISABLE_STRONG_PASSWORD: 'DISABLE_STRONG_PASSWORD', DISABLE_STRONG_PASSWORD: 'DISABLE_STRONG_PASSWORD',
DISABLE_MULTI_FACTOR_AUTHENTICATION: 'DISABLE_MULTI_FACTOR_AUTHENTICATION', DISABLE_MULTI_FACTOR_AUTHENTICATION: 'DISABLE_MULTI_FACTOR_AUTHENTICATION',
ENFORCE_MULTI_FACTOR_AUTHENTICATION: 'ENFORCE_MULTI_FACTOR_AUTHENTICATION', ENFORCE_MULTI_FACTOR_AUTHENTICATION: 'ENFORCE_MULTI_FACTOR_AUTHENTICATION',
DISABLE_PLAYGROUND: 'DISABLE_PLAYGROUND',
DISABLE_TOTP_LOGIN: 'DISABLE_TOTP_LOGIN',
DISABLE_MAIL_OTP_LOGIN: 'DISABLE_MAIL_OTP_LOGIN',
}; };
export const DateInputType = { export const DateInputType = {
@ -134,17 +112,8 @@ export interface envVarTypes {
LINKEDIN_CLIENT_SECRET: string; LINKEDIN_CLIENT_SECRET: string;
APPLE_CLIENT_ID: string; APPLE_CLIENT_ID: string;
APPLE_CLIENT_SECRET: string; APPLE_CLIENT_SECRET: string;
DISCORD_CLIENT_ID: string;
DISCORD_CLIENT_SECRET: string;
TWITTER_CLIENT_ID: string; TWITTER_CLIENT_ID: string;
TWITTER_CLIENT_SECRET: string; TWITTER_CLIENT_SECRET: string;
MICROSOFT_CLIENT_ID: string;
MICROSOFT_CLIENT_SECRET: string;
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID: string;
TWITCH_CLIENT_ID: string;
TWITCH_CLIENT_SECRET: string;
ROBLOX_CLIENT_ID: string;
ROBLOX_CLIENT_SECRET: string;
ROLES: [string] | []; ROLES: [string] | [];
DEFAULT_ROLES: [string] | []; DEFAULT_ROLES: [string] | [];
PROTECTED_ROLES: [string] | []; PROTECTED_ROLES: [string] | [];
@ -158,21 +127,16 @@ export interface envVarTypes {
SMTP_PORT: string; SMTP_PORT: string;
SMTP_USERNAME: string; SMTP_USERNAME: string;
SMTP_PASSWORD: string; SMTP_PASSWORD: string;
SMTP_LOCAL_NAME: string;
SENDER_EMAIL: string; SENDER_EMAIL: string;
SENDER_NAME: string;
ALLOWED_ORIGINS: [string] | []; ALLOWED_ORIGINS: [string] | [];
ORGANIZATION_NAME: string; ORGANIZATION_NAME: string;
ORGANIZATION_LOGO: string; ORGANIZATION_LOGO: string;
CUSTOM_ACCESS_TOKEN_SCRIPT: string; CUSTOM_ACCESS_TOKEN_SCRIPT: string;
ADMIN_SECRET: string; ADMIN_SECRET: string;
APP_COOKIE_SECURE: boolean;
ADMIN_COOKIE_SECURE: boolean;
DISABLE_LOGIN_PAGE: boolean; DISABLE_LOGIN_PAGE: boolean;
DISABLE_MAGIC_LINK_LOGIN: boolean; DISABLE_MAGIC_LINK_LOGIN: boolean;
DISABLE_EMAIL_VERIFICATION: boolean; DISABLE_EMAIL_VERIFICATION: boolean;
DISABLE_BASIC_AUTHENTICATION: boolean; DISABLE_BASIC_AUTHENTICATION: boolean;
DISABLE_MOBILE_BASIC_AUTHENTICATION: boolean;
DISABLE_SIGN_UP: boolean; DISABLE_SIGN_UP: boolean;
DISABLE_STRONG_PASSWORD: boolean; DISABLE_STRONG_PASSWORD: boolean;
OLD_ADMIN_SECRET: string; OLD_ADMIN_SECRET: string;
@ -182,11 +146,6 @@ export interface envVarTypes {
ACCESS_TOKEN_EXPIRY_TIME: string; ACCESS_TOKEN_EXPIRY_TIME: string;
DISABLE_MULTI_FACTOR_AUTHENTICATION: boolean; DISABLE_MULTI_FACTOR_AUTHENTICATION: boolean;
ENFORCE_MULTI_FACTOR_AUTHENTICATION: boolean; ENFORCE_MULTI_FACTOR_AUTHENTICATION: boolean;
DEFAULT_AUTHORIZE_RESPONSE_TYPE: string;
DEFAULT_AUTHORIZE_RESPONSE_MODE: string;
DISABLE_PLAYGROUND: boolean;
DISABLE_TOTP_LOGIN: boolean;
DISABLE_MAIL_OTP_LOGIN: boolean;
} }
export const envSubViews = { export const envSubViews = {
@ -205,7 +164,6 @@ export const envSubViews = {
export enum WebhookInputDataFields { export enum WebhookInputDataFields {
ID = 'id', ID = 'id',
EVENT_DESCRIPTION = 'event_description',
EVENT_NAME = 'event_name', EVENT_NAME = 'event_name',
ENDPOINT = 'endpoint', ENDPOINT = 'endpoint',
ENABLED = 'enabled', ENABLED = 'enabled',
@ -240,7 +198,6 @@ export const webhookEventNames = {
'User deleted': 'user.deleted', 'User deleted': 'user.deleted',
'User access enabled': 'user.access_enabled', 'User access enabled': 'user.access_enabled',
'User access revoked': 'user.access_revoked', 'User access revoked': 'user.access_revoked',
'User deactivated': 'user.deactivated',
}; };
export const emailTemplateEventNames = { export const emailTemplateEventNames = {
@ -371,21 +328,3 @@ export const webhookPayloadExample: string = `{
}, },
"auth_recipe":"google" "auth_recipe":"google"
}`; }`;
export enum EmailTemplateEditors {
UNLAYER_EDITOR = 'unlayer_editor',
PLAIN_HTML_EDITOR = 'plain_html_editor',
}
export const ResponseTypes = {
token: 'token',
code: 'code',
id_token: 'id_token',
};
export const ResponseModes = {
query: 'query',
form_post: 'form_post',
fragment: 'fragment',
web_message: 'web_message',
};

View File

@ -30,17 +30,8 @@ export const EnvVariablesQuery = `
LINKEDIN_CLIENT_SECRET LINKEDIN_CLIENT_SECRET
APPLE_CLIENT_ID APPLE_CLIENT_ID
APPLE_CLIENT_SECRET APPLE_CLIENT_SECRET
DISCORD_CLIENT_ID
DISCORD_CLIENT_SECRET
TWITTER_CLIENT_ID TWITTER_CLIENT_ID
TWITTER_CLIENT_SECRET TWITTER_CLIENT_SECRET
MICROSOFT_CLIENT_ID
MICROSOFT_CLIENT_SECRET
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID
TWITCH_CLIENT_ID
TWITCH_CLIENT_SECRET
ROBLOX_CLIENT_ID
ROBLOX_CLIENT_SECRET
DEFAULT_ROLES DEFAULT_ROLES
PROTECTED_ROLES PROTECTED_ROLES
ROLES ROLES
@ -54,20 +45,15 @@ export const EnvVariablesQuery = `
SMTP_PORT SMTP_PORT
SMTP_USERNAME SMTP_USERNAME
SMTP_PASSWORD SMTP_PASSWORD
SMTP_LOCAL_NAME
SENDER_EMAIL SENDER_EMAIL
SENDER_NAME
ALLOWED_ORIGINS ALLOWED_ORIGINS
ORGANIZATION_NAME ORGANIZATION_NAME
ORGANIZATION_LOGO ORGANIZATION_LOGO
ADMIN_SECRET ADMIN_SECRET
APP_COOKIE_SECURE
ADMIN_COOKIE_SECURE
DISABLE_LOGIN_PAGE DISABLE_LOGIN_PAGE
DISABLE_MAGIC_LINK_LOGIN DISABLE_MAGIC_LINK_LOGIN
DISABLE_EMAIL_VERIFICATION DISABLE_EMAIL_VERIFICATION
DISABLE_BASIC_AUTHENTICATION DISABLE_BASIC_AUTHENTICATION
DISABLE_MOBILE_BASIC_AUTHENTICATION
DISABLE_SIGN_UP DISABLE_SIGN_UP
DISABLE_STRONG_PASSWORD DISABLE_STRONG_PASSWORD
DISABLE_REDIS_FOR_ENV DISABLE_REDIS_FOR_ENV
@ -78,11 +64,6 @@ export const EnvVariablesQuery = `
ACCESS_TOKEN_EXPIRY_TIME ACCESS_TOKEN_EXPIRY_TIME
DISABLE_MULTI_FACTOR_AUTHENTICATION DISABLE_MULTI_FACTOR_AUTHENTICATION
ENFORCE_MULTI_FACTOR_AUTHENTICATION ENFORCE_MULTI_FACTOR_AUTHENTICATION
DEFAULT_AUTHORIZE_RESPONSE_TYPE
DEFAULT_AUTHORIZE_RESPONSE_MODE
DISABLE_PLAYGROUND
DISABLE_TOTP_LOGIN
DISABLE_MAIL_OTP_LOGIN
} }
} }
`; `;
@ -100,7 +81,6 @@ export const UserDetailsQuery = `
id id
email email
email_verified email_verified
phone_number_verified
given_name given_name
family_name family_name
middle_name middle_name
@ -132,7 +112,6 @@ export const WebhooksDataQuery = `
_webhooks(params: $params){ _webhooks(params: $params){
webhooks{ webhooks{
id id
event_description
event_name event_name
endpoint endpoint
enabled enabled
@ -188,12 +167,3 @@ export const WebhookLogsQuery = `
} }
} }
`; `;
export const GetAvailableRolesQuery = `
query {
_env {
ROLES
PROTECTED_ROLES
}
}
`;

View File

@ -6,5 +6,5 @@ ReactDOM.render(
<div> <div>
<App /> <App />
</div>, </div>,
document.getElementById('root'), document.getElementById('root')
); );

View File

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

View File

@ -154,7 +154,7 @@ const EmailTemplates = () => {
<Td>{templateData[EmailTemplateInputDataFields.SUBJECT]}</Td> <Td>{templateData[EmailTemplateInputDataFields.SUBJECT]}</Td>
<Td> <Td>
{dayjs(templateData.created_at * 1000).format( {dayjs(templateData.created_at * 1000).format(
'MMM DD, YYYY', 'MMM DD, YYYY'
)} )}
</Td> </Td>
<Td> <Td>

View File

@ -50,17 +50,8 @@ const Environment = () => {
LINKEDIN_CLIENT_SECRET: '', LINKEDIN_CLIENT_SECRET: '',
APPLE_CLIENT_ID: '', APPLE_CLIENT_ID: '',
APPLE_CLIENT_SECRET: '', APPLE_CLIENT_SECRET: '',
DISCORD_CLIENT_ID: '',
DISCORD_CLIENT_SECRET: '',
TWITTER_CLIENT_ID: '', TWITTER_CLIENT_ID: '',
TWITTER_CLIENT_SECRET: '', TWITTER_CLIENT_SECRET: '',
MICROSOFT_CLIENT_ID: '',
MICROSOFT_CLIENT_SECRET: '',
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID: '',
TWITCH_CLIENT_ID: '',
TWITCH_CLIENT_SECRET: '',
ROBLOX_CLIENT_ID: '',
ROBLOX_CLIENT_SECRET: '',
ROLES: [], ROLES: [],
DEFAULT_ROLES: [], DEFAULT_ROLES: [],
PROTECTED_ROLES: [], PROTECTED_ROLES: [],
@ -74,21 +65,16 @@ const Environment = () => {
SMTP_PORT: '', SMTP_PORT: '',
SMTP_USERNAME: '', SMTP_USERNAME: '',
SMTP_PASSWORD: '', SMTP_PASSWORD: '',
SMTP_LOCAL_NAME: '',
SENDER_EMAIL: '', SENDER_EMAIL: '',
SENDER_NAME: '',
ALLOWED_ORIGINS: [], ALLOWED_ORIGINS: [],
ORGANIZATION_NAME: '', ORGANIZATION_NAME: '',
ORGANIZATION_LOGO: '', ORGANIZATION_LOGO: '',
CUSTOM_ACCESS_TOKEN_SCRIPT: '', CUSTOM_ACCESS_TOKEN_SCRIPT: '',
ADMIN_SECRET: '', ADMIN_SECRET: '',
APP_COOKIE_SECURE: false,
ADMIN_COOKIE_SECURE: false,
DISABLE_LOGIN_PAGE: false, DISABLE_LOGIN_PAGE: false,
DISABLE_MAGIC_LINK_LOGIN: false, DISABLE_MAGIC_LINK_LOGIN: false,
DISABLE_EMAIL_VERIFICATION: false, DISABLE_EMAIL_VERIFICATION: false,
DISABLE_BASIC_AUTHENTICATION: false, DISABLE_BASIC_AUTHENTICATION: false,
DISABLE_MOBILE_BASIC_AUTHENTICATION: false,
DISABLE_SIGN_UP: false, DISABLE_SIGN_UP: false,
DISABLE_STRONG_PASSWORD: false, DISABLE_STRONG_PASSWORD: false,
OLD_ADMIN_SECRET: '', OLD_ADMIN_SECRET: '',
@ -98,11 +84,6 @@ const Environment = () => {
ACCESS_TOKEN_EXPIRY_TIME: '', ACCESS_TOKEN_EXPIRY_TIME: '',
DISABLE_MULTI_FACTOR_AUTHENTICATION: false, DISABLE_MULTI_FACTOR_AUTHENTICATION: false,
ENFORCE_MULTI_FACTOR_AUTHENTICATION: false, ENFORCE_MULTI_FACTOR_AUTHENTICATION: false,
DEFAULT_AUTHORIZE_RESPONSE_TYPE: '',
DEFAULT_AUTHORIZE_RESPONSE_MODE: '',
DISABLE_PLAYGROUND: false,
DISABLE_TOTP_LOGIN: false,
DISABLE_MAIL_OTP_LOGIN: true,
}); });
const [fieldVisibility, setFieldVisibility] = React.useState< const [fieldVisibility, setFieldVisibility] = React.useState<
@ -113,9 +94,7 @@ const Environment = () => {
FACEBOOK_CLIENT_SECRET: false, FACEBOOK_CLIENT_SECRET: false,
LINKEDIN_CLIENT_SECRET: false, LINKEDIN_CLIENT_SECRET: false,
APPLE_CLIENT_SECRET: false, APPLE_CLIENT_SECRET: false,
DISCORD_CLIENT_SECRET: false,
TWITTER_CLIENT_SECRET: false, TWITTER_CLIENT_SECRET: false,
TWITCH_CLIENT_SECRET: false,
JWT_SECRET: false, JWT_SECRET: false,
SMTP_PASSWORD: false, SMTP_PASSWORD: false,
ADMIN_SECRET: false, ADMIN_SECRET: false,
@ -176,7 +155,7 @@ const Environment = () => {
// @ts-ignore // @ts-ignore
[property]: envVariables[property], [property]: envVariables[property],
}), }),
{}, {}
); );
if ( if (
updatedEnvVariables[HiddenInputType.ADMIN_SECRET] === '' || updatedEnvVariables[HiddenInputType.ADMIN_SECRET] === '' ||
@ -221,7 +200,7 @@ const Environment = () => {
} variables`, } variables`,
isClosable: true, isClosable: true,
status: 'success', status: 'success',
position: 'top-right', position: 'bottom-right',
}); });
}; };

View File

@ -29,7 +29,7 @@ import {
MenuItem, MenuItem,
useToast, useToast,
Spinner, Spinner,
TableContainer, TableContainer
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { import {
FaAngleLeft, FaAngleLeft,
@ -165,25 +165,14 @@ export default function Users() {
}; };
const userVerificationHandler = async (user: userDataTypes) => { const userVerificationHandler = async (user: userDataTypes) => {
const { id, email, phone_number } = user; const { id, email } = user;
let params = {}; const res = await client
if (email) { .mutation(UpdateUser, {
params = { params: {
id, id,
email, email,
email_verified: true, email_verified: true,
}; },
}
if (phone_number) {
params = {
id,
phone_number,
phone_number_verified: true,
};
}
const res = await client
.mutation(UpdateUser, {
params,
}) })
.toPromise(); .toPromise();
if (res.error) { if (res.error) {
@ -191,14 +180,14 @@ export default function Users() {
title: 'User verification failed', title: 'User verification failed',
isClosable: true, isClosable: true,
status: 'error', status: 'error',
position: 'top-right', position: 'bottom-right',
}); });
} else if (res.data?._update_user?.id) { } else if (res.data?._update_user?.id) {
toast({ toast({
title: 'User verification successful', title: 'User verification successful',
isClosable: true, isClosable: true,
status: 'success', status: 'success',
position: 'top-right', position: 'bottom-right',
}); });
} }
updateUserList(); updateUserList();
@ -206,7 +195,7 @@ export default function Users() {
const updateAccessHandler = async ( const updateAccessHandler = async (
id: string, id: string,
action: updateAccessActions, action: updateAccessActions
) => { ) => {
switch (action) { switch (action) {
case updateAccessActions.ENABLE: case updateAccessActions.ENABLE:
@ -222,14 +211,14 @@ export default function Users() {
title: 'User access enable failed', title: 'User access enable failed',
isClosable: true, isClosable: true,
status: 'error', status: 'error',
position: 'top-right', position: 'bottom-right',
}); });
} else { } else {
toast({ toast({
title: 'User access enabled successfully', title: 'User access enabled successfully',
isClosable: true, isClosable: true,
status: 'success', status: 'success',
position: 'top-right', position: 'bottom-right',
}); });
} }
updateUserList(); updateUserList();
@ -247,14 +236,14 @@ export default function Users() {
title: 'User access revoke failed', title: 'User access revoke failed',
isClosable: true, isClosable: true,
status: 'error', status: 'error',
position: 'top-right', position: 'bottom-right',
}); });
} else { } else {
toast({ toast({
title: 'User access revoked successfully', title: 'User access revoked successfully',
isClosable: true, isClosable: true,
status: 'success', status: 'success',
position: 'top-right', position: 'bottom-right',
}); });
} }
updateUserList(); updateUserList();
@ -274,12 +263,11 @@ export default function Users() {
.toPromise(); .toPromise();
if (res.data?._update_user?.id) { if (res.data?._update_user?.id) {
toast({ toast({
title: `Multi factor authentication ${ title: `Multi factor authentication ${user.is_multi_factor_auth_enabled ? 'disabled' : 'enabled'
user.is_multi_factor_auth_enabled ? 'disabled' : 'enabled'
} for user`, } for user`,
isClosable: true, isClosable: true,
status: 'success', status: 'success',
position: 'top-right', position: 'bottom-right',
}); });
updateUserList(); updateUserList();
return; return;
@ -288,7 +276,7 @@ export default function Users() {
title: 'Multi factor authentication update failed for user', title: 'Multi factor authentication update failed for user',
isClosable: true, isClosable: true,
status: 'error', status: 'error',
position: 'top-right', position: 'bottom-right',
}); });
}; };
@ -309,7 +297,7 @@ export default function Users() {
<Table variant="simple"> <Table variant="simple">
<Thead> <Thead>
<Tr> <Tr>
<Th>Email / Phone</Th> <Th>Email</Th>
<Th>Created At</Th> <Th>Created At</Th>
<Th>Signup Methods</Th> <Th>Signup Methods</Th>
<Th>Roles</Th> <Th>Roles</Th>
@ -325,15 +313,10 @@ export default function Users() {
</Thead> </Thead>
<Tbody> <Tbody>
{userList.map((user: userDataTypes) => { {userList.map((user: userDataTypes) => {
const { const { email_verified, created_at, ...rest }: any = user;
email_verified,
phone_number_verified,
created_at,
...rest
}: any = user;
return ( return (
<Tr key={user.id} style={{ fontSize: 14 }}> <Tr key={user.id} style={{ fontSize: 14 }}>
<Td maxW="300">{user.email || user.phone_number}</Td> <Td maxW="300">{user.email}</Td>
<Td> <Td>
{dayjs(user.created_at * 1000).format('MMM DD, YYYY')} {dayjs(user.created_at * 1000).format('MMM DD, YYYY')}
</Td> </Td>
@ -343,15 +326,9 @@ export default function Users() {
<Tag <Tag
size="sm" size="sm"
variant="outline" variant="outline"
colorScheme={ colorScheme={user.email_verified ? 'green' : 'yellow'}
user.email_verified || user.phone_number_verified
? 'green'
: 'yellow'
}
> >
{( {user.email_verified.toString()}
user.email_verified || user.phone_number_verified
).toString()}
</Tag> </Tag>
</Td> </Td>
<Td> <Td>
@ -390,8 +367,7 @@ export default function Users() {
</Flex> </Flex>
</MenuButton> </MenuButton>
<MenuList> <MenuList>
{!user.email_verified && {!user.email_verified && (
!user.phone_number_verified && (
<MenuItem <MenuItem
onClick={() => userVerificationHandler(user)} onClick={() => userVerificationHandler(user)}
> >
@ -411,7 +387,7 @@ export default function Users() {
onClick={() => onClick={() =>
updateAccessHandler( updateAccessHandler(
user.id, user.id,
updateAccessActions.ENABLE, updateAccessActions.ENABLE
) )
} }
> >
@ -422,7 +398,7 @@ export default function Users() {
onClick={() => onClick={() =>
updateAccessHandler( updateAccessHandler(
user.id, user.id,
updateAccessActions.REVOKE, updateAccessActions.REVOKE
) )
} }
> >
@ -431,17 +407,13 @@ export default function Users() {
)} )}
{user.is_multi_factor_auth_enabled ? ( {user.is_multi_factor_auth_enabled ? (
<MenuItem <MenuItem
onClick={() => onClick={() => multiFactorAuthUpdateHandler(user)}
multiFactorAuthUpdateHandler(user)
}
> >
Disable MultiFactor Authentication Disable MultiFactor Authentication
</MenuItem> </MenuItem>
) : ( ) : (
<MenuItem <MenuItem
onClick={() => onClick={() => multiFactorAuthUpdateHandler(user)}
multiFactorAuthUpdateHandler(user)
}
> >
Enable MultiFactor Authentication Enable MultiFactor Authentication
</MenuItem> </MenuItem>

View File

@ -56,7 +56,6 @@ interface paginationPropTypes {
interface webhookDataTypes { interface webhookDataTypes {
[WebhookInputDataFields.ID]: string; [WebhookInputDataFields.ID]: string;
[WebhookInputDataFields.EVENT_NAME]: string; [WebhookInputDataFields.EVENT_NAME]: string;
[WebhookInputDataFields.EVENT_DESCRIPTION]?: string;
[WebhookInputDataFields.ENDPOINT]: string; [WebhookInputDataFields.ENDPOINT]: string;
[WebhookInputDataFields.ENABLED]: boolean; [WebhookInputDataFields.ENABLED]: boolean;
[WebhookInputDataFields.HEADERS]?: Record<string, string>; [WebhookInputDataFields.HEADERS]?: Record<string, string>;
@ -135,7 +134,6 @@ const Webhooks = () => {
<Thead> <Thead>
<Tr> <Tr>
<Th>Event Name</Th> <Th>Event Name</Th>
<Th>Event Description</Th>
<Th>Endpoint</Th> <Th>Endpoint</Th>
<Th>Enabled</Th> <Th>Enabled</Th>
<Th>Headers</Th> <Th>Headers</Th>
@ -149,10 +147,7 @@ const Webhooks = () => {
style={{ fontSize: 14 }} style={{ fontSize: 14 }}
> >
<Td maxW="300"> <Td maxW="300">
{webhook[WebhookInputDataFields.EVENT_NAME].split('-')[0]} {webhook[WebhookInputDataFields.EVENT_NAME]}
</Td>
<Td maxW="300">
{webhook[WebhookInputDataFields.EVENT_DESCRIPTION]}
</Td> </Td>
<Td>{webhook[WebhookInputDataFields.ENDPOINT]}</Td> <Td>{webhook[WebhookInputDataFields.ENDPOINT]}</Td>
<Td> <Td>
@ -175,12 +170,12 @@ const Webhooks = () => {
label={JSON.stringify( label={JSON.stringify(
webhook[WebhookInputDataFields.HEADERS], webhook[WebhookInputDataFields.HEADERS],
null, null,
' ', ' '
)} )}
> >
<Tag size="sm" variant="outline" colorScheme="gray"> <Tag size="sm" variant="outline" colorScheme="gray">
{Object.keys( {Object.keys(
webhook[WebhookInputDataFields.HEADERS] || {}, webhook[WebhookInputDataFields.HEADERS] || {}
)?.length.toString()} )?.length.toString()}
</Tag> </Tag>
</Tooltip> </Tooltip>
@ -269,7 +264,7 @@ const Webhooks = () => {
</Text> </Text>
</Text> </Text>
<Flex alignItems="center"> <Flex alignItems="center">
<Text>Go to page:</Text>{' '} <Text flexShrink="0">Go to page:</Text>{' '}
<NumberInput <NumberInput
ml={2} ml={2}
mr={8} mr={8}

View File

@ -67,7 +67,7 @@ export const validateEmail = (email: string) => {
return email return email
.toLowerCase() .toLowerCase()
.match( .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,}))$/, /^(([^<>()[\]\\.,;:\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 ? true
: false; : false;
@ -78,7 +78,7 @@ export const validateURI = (uri: string) => {
return uri return uri
.toLowerCase() .toLowerCase()
.match( .match(
/(?:^|\s)((https?:\/\/)?(?:localhost|[\w-]+(?:\.[\w-]+)+)(:\d+)?(\/\S*)?)/, /(?:^|\s)((https?:\/\/)?(?:localhost|[\w-]+(?:\.[\w-]+)+)(:\d+)?(\/\S*)?)/
) )
? true ? true
: false; : false;

View File

@ -27,7 +27,7 @@ const parseCSV = (file: File, delimiter: string): Promise<dataTypes[]> => {
value: email.trim(), value: email.trim(),
isInvalid: !validateEmail(email.trim()), isInvalid: !validateEmail(email.trim()),
}; };
}), })
); );
}; };

File diff suppressed because it is too large Load Diff

20
go.mod
View File

@ -1,20 +0,0 @@
module server
go 1.21.5
require (
github.com/99designs/gqlgen v0.17.43 // indirect
github.com/agnivade/levenshtein v1.1.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sosodev/duration v1.1.0 // indirect
github.com/urfave/cli/v2 v2.25.5 // indirect
github.com/vektah/gqlparser/v2 v2.5.11 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
golang.org/x/mod v0.10.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/tools v0.9.3 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

31
go.sum
View File

@ -1,31 +0,0 @@
github.com/99designs/gqlgen v0.17.43 h1:I4SYg6ahjowErAQcHFVKy5EcWuwJ3+Xw9z2fLpuFCPo=
github.com/99designs/gqlgen v0.17.43/go.mod h1:lO0Zjy8MkZgBdv4T1U91x09r0e0WFOdhVUutlQs1Rsc=
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sosodev/duration v1.1.0 h1:kQcaiGbJaIsRqgQy7VGlZrVw1giWO+lDoX3MCPnpVO4=
github.com/sosodev/duration v1.1.0/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg=
github.com/urfave/cli/v2 v2.25.5 h1:d0NIAyhh5shGscroL7ek/Ya9QYQE0KNabJgiUinIQkc=
github.com/urfave/cli/v2 v2.25.5/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
github.com/vektah/gqlparser/v2 v2.5.11 h1:JJxLtXIoN7+3x6MBdtIP59TP1RANnY7pXOaDnADQSf8=
github.com/vektah/gqlparser/v2 v2.5.11/go.mod h1:1rCcfwB2ekJofmluGWXMSEnPMZgbxzwj6FaZ/4OT8Cc=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM=
golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

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

View File

@ -1,25 +0,0 @@
package providers
import "context"
// AuthenticatorConfig defines authenticator config
type AuthenticatorConfig struct {
// ScannerImage is the base64 of QR code image
ScannerImage string
// Secrets is the secret key
Secret string
// RecoveryCode is the list of recovery codes
RecoveryCodes []string
// RecoveryCodeMap is the map of recovery codes
RecoveryCodeMap map[string]bool
}
// Provider defines authenticators provider
type Provider interface {
// Generate totp: to generate totp, store secret into db and returns base64 of QR code image
Generate(ctx context.Context, id string) (*AuthenticatorConfig, error)
// Validate totp: user passcode with secret stored in our db
Validate(ctx context.Context, passcode string, userID string) (bool, error)
// ValidateRecoveryCode totp: allows user to validate using recovery code incase if they lost their device
ValidateRecoveryCode(ctx context.Context, recoveryCode, userID string) (bool, error)
}

View File

@ -1,23 +0,0 @@
package totp
import (
"context"
)
type provider struct {
ctx context.Context
}
// TOTPConfig defines totp config
type TOTPConfig struct {
ScannerImage string
Secret string
}
// NewProvider returns a new totp provider
func NewProvider() (*provider, error) {
ctx := context.Background()
return &provider{
ctx: ctx,
}, nil
}

View File

@ -1,151 +0,0 @@
package totp
import (
"bytes"
"context"
"encoding/json"
"fmt"
"image/png"
"time"
"github.com/google/uuid"
"github.com/pquerna/otp/totp"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/authenticators/providers"
"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/refs"
)
// Generate generates a Time-Based One-Time Password (TOTP) for a user and returns the base64-encoded QR code for frontend display.
func (p *provider) Generate(ctx context.Context, id string) (*providers.AuthenticatorConfig, error) {
var buf bytes.Buffer
//get user details
user, err := db.Provider.GetUserByID(ctx, id)
if err != nil {
return nil, err
}
// generate totp, Authenticators hash is valid for 30 seconds
key, err := totp.Generate(totp.GenerateOpts{
Issuer: "authorizer",
AccountName: refs.StringValue(user.Email),
})
if err != nil {
return nil, err
}
//generating image for key and encoding to base64 for displaying in frontend
img, err := key.Image(200, 200)
if err != nil {
return nil, err
}
png.Encode(&buf, img)
encodedText := crypto.EncryptB64(buf.String())
secret := key.Secret()
recoveryCodes := []string{}
for i := 0; i < 10; i++ {
recoveryCodes = append(recoveryCodes, uuid.NewString())
}
// Converting recoveryCodes to string
recoverCodesMap := map[string]bool{}
for i := 0; i < len(recoveryCodes); i++ {
recoverCodesMap[recoveryCodes[i]] = false
}
// Converting recoveryCodesMap to string
jsonData, err := json.Marshal(recoverCodesMap)
if err != nil {
return nil, err
}
recoveryCodesString := string(jsonData)
totpModel := &models.Authenticator{
Secret: secret,
RecoveryCodes: refs.NewStringRef(recoveryCodesString),
UserID: user.ID,
Method: constants.EnvKeyTOTPAuthenticator,
}
authenticator, err := db.Provider.GetAuthenticatorDetailsByUserId(ctx, user.ID, constants.EnvKeyTOTPAuthenticator)
if err != nil {
log.Debug("Failed to get authenticator details by user id, creating new record: ", err)
// continue
}
if authenticator == nil {
// if authenticator is nil then create new authenticator
_, err = db.Provider.AddAuthenticator(ctx, totpModel)
if err != nil {
return nil, err
}
} else {
authenticator.Secret = secret
authenticator.RecoveryCodes = refs.NewStringRef(recoveryCodesString)
// if authenticator is not nil then update authenticator
_, err = db.Provider.UpdateAuthenticator(ctx, authenticator)
if err != nil {
return nil, err
}
}
return &providers.AuthenticatorConfig{
ScannerImage: encodedText,
Secret: secret,
RecoveryCodes: recoveryCodes,
RecoveryCodeMap: recoverCodesMap,
}, nil
}
// Validate validates a Time-Based One-Time Password (TOTP) against the stored TOTP secret for a user.
func (p *provider) Validate(ctx context.Context, passcode string, userID string) (bool, error) {
// get totp details
totpModel, err := db.Provider.GetAuthenticatorDetailsByUserId(ctx, userID, constants.EnvKeyTOTPAuthenticator)
if err != nil {
return false, err
}
// validate totp
status := totp.Validate(passcode, totpModel.Secret)
// checks if user not signed in for totp and totp code is correct then VerifiedAt will be stored in db
if totpModel.VerifiedAt == nil && status {
timeNow := time.Now().Unix()
totpModel.VerifiedAt = &timeNow
_, err = db.Provider.UpdateAuthenticator(ctx, totpModel)
if err != nil {
return false, err
}
}
return status, nil
}
// ValidateRecoveryCode validates a Time-Based One-Time Password (TOTP) recovery code against the stored TOTP recovery code for a user.
func (p *provider) ValidateRecoveryCode(ctx context.Context, recoveryCode, userID string) (bool, error) {
// get totp details
totpModel, err := db.Provider.GetAuthenticatorDetailsByUserId(ctx, userID, constants.EnvKeyTOTPAuthenticator)
if err != nil {
return false, err
}
// convert recoveryCodes to map
recoveryCodesMap := map[string]bool{}
err = json.Unmarshal([]byte(refs.StringValue(totpModel.RecoveryCodes)), &recoveryCodesMap)
if err != nil {
return false, err
}
// check if recovery code is valid
if val, ok := recoveryCodesMap[recoveryCode]; !ok {
return false, fmt.Errorf("invalid recovery code")
} else if val {
return false, fmt.Errorf("recovery code already used")
}
// update recovery code map
recoveryCodesMap[recoveryCode] = true
// convert recoveryCodesMap to string
jsonData, err := json.Marshal(recoveryCodesMap)
if err != nil {
return false, err
}
recoveryCodesString := string(jsonData)
totpModel.RecoveryCodes = refs.NewStringRef(recoveryCodesString)
// update recovery code map in db
_, err = db.Provider.UpdateAuthenticator(ctx, totpModel)
if err != nil {
return false, err
}
return true, nil
}

View File

@ -1,26 +0,0 @@
package authenticators
import (
"github.com/authorizerdev/authorizer/server/authenticators/providers"
"github.com/authorizerdev/authorizer/server/authenticators/providers/totp"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/memorystore"
)
// Provider is the global authenticators provider.
var Provider providers.Provider
// InitTOTPStore initializes the TOTP authenticator store if it's not disabled in the environment variables.
// It sets the global Provider variable to a new TOTP provider.
func InitTOTPStore() error {
var err error
isTOTPEnvServiceDisabled, _ := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableTOTPLogin)
if !isTOTPEnvServiceDisabled {
Provider, err = totp.NewProvider()
if err != nil {
return err
}
}
return nil
}

View File

@ -3,12 +3,8 @@ package constants
const ( const (
// AuthRecipeMethodBasicAuth is the basic_auth auth method // AuthRecipeMethodBasicAuth is the basic_auth auth method
AuthRecipeMethodBasicAuth = "basic_auth" AuthRecipeMethodBasicAuth = "basic_auth"
// AuthRecipeMethodMobileBasicAuth is the mobile basic_auth method, where user can signup using mobile number and password
AuthRecipeMethodMobileBasicAuth = "mobile_basic_auth"
// AuthRecipeMethodMagicLinkLogin is the magic_link_login auth method // AuthRecipeMethodMagicLinkLogin is the magic_link_login auth method
AuthRecipeMethodMagicLinkLogin = "magic_link_login" AuthRecipeMethodMagicLinkLogin = "magic_link_login"
// AuthRecipeMethodMobileOTP is the mobile_otp auth method
AuthRecipeMethodMobileOTP = "mobile_otp"
// AuthRecipeMethodGoogle is the google auth method // AuthRecipeMethodGoogle is the google auth method
AuthRecipeMethodGoogle = "google" AuthRecipeMethodGoogle = "google"
// AuthRecipeMethodGithub is the github auth method // AuthRecipeMethodGithub is the github auth method
@ -19,14 +15,6 @@ const (
AuthRecipeMethodLinkedIn = "linkedin" AuthRecipeMethodLinkedIn = "linkedin"
// AuthRecipeMethodApple is the apple auth method // AuthRecipeMethodApple is the apple auth method
AuthRecipeMethodApple = "apple" AuthRecipeMethodApple = "apple"
// AuthRecipeMethodDiscord is the discord auth method
AuthRecipeMethodDiscord = "discord"
// AuthRecipeMethodTwitter is the twitter auth method // AuthRecipeMethodTwitter is the twitter auth method
AuthRecipeMethodTwitter = "twitter" AuthRecipeMethodTwitter = "twitter"
// AuthRecipeMethodMicrosoft is the microsoft auth method
AuthRecipeMethodMicrosoft = "microsoft"
// AuthRecipeMethodTwitch is the twitch auth method
AuthRecipeMethodTwitch = "twitch"
// AuthRecipeMethodRoblox is the roblox auth method
AuthRecipeMethodRoblox = "roblox"
) )

View File

@ -1,7 +0,0 @@
package constants
// Authenticators Methods
const (
// EnvKeyTOTPAuthenticator key for env variable TOTP
EnvKeyTOTPAuthenticator = "totp"
)

View File

@ -5,6 +5,4 @@ const (
AppCookieName = "cookie" AppCookieName = "cookie"
// AdminCookieName is the name of the cookie that is used to store the admin token // AdminCookieName is the name of the cookie that is used to store the admin token
AdminCookieName = "authorizer-admin" AdminCookieName = "authorizer-admin"
// MfaCookieName is the name of the cookie that is used to store the mfa session
MfaCookieName = "mfa"
) )

View File

@ -5,8 +5,6 @@ const (
DbTypePostgres = "postgres" DbTypePostgres = "postgres"
// DbTypeSqlite is the sqlite database type // DbTypeSqlite is the sqlite database type
DbTypeSqlite = "sqlite" DbTypeSqlite = "sqlite"
// DbTypeLibSQL is the libsql / Turso database type
DbTypeLibSQL = "libsql"
// DbTypeMysql is the mysql database type // DbTypeMysql is the mysql database type
DbTypeMysql = "mysql" DbTypeMysql = "mysql"
// DbTypeSqlserver is the sqlserver database type // DbTypeSqlserver is the sqlserver database type
@ -27,8 +25,4 @@ const (
DbTypeCockroachDB = "cockroachdb" DbTypeCockroachDB = "cockroachdb"
// DbTypePlanetScaleDB is the planetscale database type // DbTypePlanetScaleDB is the planetscale database type
DbTypePlanetScaleDB = "planetscale" DbTypePlanetScaleDB = "planetscale"
// DbTypeDynamoDB is the Dynamo database type
DbTypeDynamoDB = "dynamodb"
// DbTypeCouchbaseDB is the Couchbase database type
DbTypeCouchbaseDB = "couchbase"
) )

View File

@ -21,12 +21,6 @@ const (
EnvKeyDatabaseType = "DATABASE_TYPE" EnvKeyDatabaseType = "DATABASE_TYPE"
// EnvKeyDatabaseURL key for env variable DATABASE_URL // EnvKeyDatabaseURL key for env variable DATABASE_URL
EnvKeyDatabaseURL = "DATABASE_URL" EnvKeyDatabaseURL = "DATABASE_URL"
// EnvAwsRegion key for env variable AWS REGION
EnvAwsRegion = "AWS_REGION"
// EnvAwsAccessKeyID key for env variable AWS_ACCESS_KEY_ID
EnvAwsAccessKeyID = "AWS_ACCESS_KEY_ID"
// EnvAwsAccessKey key for env variable AWS_SECRET_ACCESS_KEY
EnvAwsSecretAccessKey = "AWS_SECRET_ACCESS_KEY"
// EnvKeyDatabaseName key for env variable DATABASE_NAME // EnvKeyDatabaseName key for env variable DATABASE_NAME
EnvKeyDatabaseName = "DATABASE_NAME" EnvKeyDatabaseName = "DATABASE_NAME"
// EnvKeyDatabaseUsername key for env variable DATABASE_USERNAME // EnvKeyDatabaseUsername key for env variable DATABASE_USERNAME
@ -43,13 +37,6 @@ const (
EnvKeyDatabaseCertKey = "DATABASE_CERT_KEY" EnvKeyDatabaseCertKey = "DATABASE_CERT_KEY"
// EnvKeyDatabaseCACert key for env variable DATABASE_CA_CERT // EnvKeyDatabaseCACert key for env variable DATABASE_CA_CERT
EnvKeyDatabaseCACert = "DATABASE_CA_CERT" EnvKeyDatabaseCACert = "DATABASE_CA_CERT"
// EnvCouchbaseBucket key for env variable COUCHBASE_BUCKET
EnvCouchbaseBucket = "COUCHBASE_BUCKET"
// EnvCouchbaseBucketRAMQuotaMB key for env variable COUCHBASE_BUCKET_RAM_QUOTA
// This value should be parsed as number
EnvCouchbaseBucketRAMQuotaMB = "COUCHBASE_BUCKET_RAM_QUOTA"
// EnvCouchbaseBucket key for env variable COUCHBASE_SCOPE
EnvCouchbaseScope = "COUCHBASE_SCOPE"
// EnvKeySmtpHost key for env variable SMTP_HOST // EnvKeySmtpHost key for env variable SMTP_HOST
EnvKeySmtpHost = "SMTP_HOST" EnvKeySmtpHost = "SMTP_HOST"
// EnvKeySmtpPort key for env variable SMTP_PORT // EnvKeySmtpPort key for env variable SMTP_PORT
@ -58,16 +45,10 @@ const (
EnvKeySmtpUsername = "SMTP_USERNAME" EnvKeySmtpUsername = "SMTP_USERNAME"
// EnvKeySmtpPassword key for env variable SMTP_PASSWORD // EnvKeySmtpPassword key for env variable SMTP_PASSWORD
EnvKeySmtpPassword = "SMTP_PASSWORD" EnvKeySmtpPassword = "SMTP_PASSWORD"
// EnvKeySmtpLocalName key for env variable SMTP_LOCAL_NAME
EnvKeySmtpLocalName = "SMTP_LOCAL_NAME"
// EnvKeySenderEmail key for env variable SENDER_EMAIL // EnvKeySenderEmail key for env variable SENDER_EMAIL
EnvKeySenderEmail = "SENDER_EMAIL" EnvKeySenderEmail = "SENDER_EMAIL"
// EnvKeySenderName key for env variable SENDER_NAME
EnvKeySenderName = "SENDER_NAME"
// EnvKeyIsEmailServiceEnabled key for env variable IS_EMAIL_SERVICE_ENABLED // EnvKeyIsEmailServiceEnabled key for env variable IS_EMAIL_SERVICE_ENABLED
EnvKeyIsEmailServiceEnabled = "IS_EMAIL_SERVICE_ENABLED" EnvKeyIsEmailServiceEnabled = "IS_EMAIL_SERVICE_ENABLED"
// EnvKeyIsSMSServiceEnabled key for env variable IS_SMS_SERVICE_ENABLED
EnvKeyIsSMSServiceEnabled = "IS_SMS_SERVICE_ENABLED"
// EnvKeyAppCookieSecure key for env variable APP_COOKIE_SECURE // EnvKeyAppCookieSecure key for env variable APP_COOKIE_SECURE
EnvKeyAppCookieSecure = "APP_COOKIE_SECURE" EnvKeyAppCookieSecure = "APP_COOKIE_SECURE"
// EnvKeyAdminCookieSecure key for env variable ADMIN_COOKIE_SECURE // EnvKeyAdminCookieSecure key for env variable ADMIN_COOKIE_SECURE
@ -108,28 +89,10 @@ const (
EnvKeyAppleClientID = "APPLE_CLIENT_ID" EnvKeyAppleClientID = "APPLE_CLIENT_ID"
// EnvKeyAppleClientSecret key for env variable APPLE_CLIENT_SECRET // EnvKeyAppleClientSecret key for env variable APPLE_CLIENT_SECRET
EnvKeyAppleClientSecret = "APPLE_CLIENT_SECRET" EnvKeyAppleClientSecret = "APPLE_CLIENT_SECRET"
// EnvKeyDiscordClientID key for env variable DISCORD_CLIENT_ID
EnvKeyDiscordClientID = "DISCORD_CLIENT_ID"
// EnvKeyDiscordClientSecret key for env variable DISCORD_CLIENT_SECRET
EnvKeyDiscordClientSecret = "DISCORD_CLIENT_SECRET"
// EnvKeyTwitterClientID key for env variable TWITTER_CLIENT_ID // EnvKeyTwitterClientID key for env variable TWITTER_CLIENT_ID
EnvKeyTwitterClientID = "TWITTER_CLIENT_ID" EnvKeyTwitterClientID = "TWITTER_CLIENT_ID"
// EnvKeyTwitterClientSecret key for env variable TWITTER_CLIENT_SECRET // EnvKeyTwitterClientSecret key for env variable TWITTER_CLIENT_SECRET
EnvKeyTwitterClientSecret = "TWITTER_CLIENT_SECRET" EnvKeyTwitterClientSecret = "TWITTER_CLIENT_SECRET"
// EnvKeyMicrosoftClientID key for env variable MICROSOFT_CLIENT_ID
EnvKeyMicrosoftClientID = "MICROSOFT_CLIENT_ID"
// EnvKeyMicrosoftActiveDirectoryTenantID key for env variable MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID
EnvKeyMicrosoftActiveDirectoryTenantID = "MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID"
// EnvKeyMicrosoftClientSecret key for env variable MICROSOFT_CLIENT_SECRET
EnvKeyMicrosoftClientSecret = "MICROSOFT_CLIENT_SECRET"
// EnvKeyTwitchClientID key for env variable TWITCH_CLIENT_ID
EnvKeyTwitchClientID = "TWITCH_CLIENT_ID"
// EnvKeyTwitchClientSecret key for env variable TWITCH_CLIENT_SECRET
EnvKeyTwitchClientSecret = "TWITCH_CLIENT_SECRET"
// EnvKeyRobloxClientID key for env variable ROBLOX_CLIENT_ID
EnvKeyRobloxClientID = "ROBLOX_CLIENT_ID"
// EnvKeyRobloxClientSecret key for env variable ROBLOX_CLIENT_SECRET
EnvKeyRobloxClientSecret = "ROBLOX_CLIENT_SECRET"
// EnvKeyOrganizationName key for env variable ORGANIZATION_NAME // EnvKeyOrganizationName key for env variable ORGANIZATION_NAME
EnvKeyOrganizationName = "ORGANIZATION_NAME" EnvKeyOrganizationName = "ORGANIZATION_NAME"
// EnvKeyOrganizationLogo key for env variable ORGANIZATION_LOGO // EnvKeyOrganizationLogo key for env variable ORGANIZATION_LOGO
@ -154,8 +117,6 @@ const (
EnvKeyDisableEmailVerification = "DISABLE_EMAIL_VERIFICATION" EnvKeyDisableEmailVerification = "DISABLE_EMAIL_VERIFICATION"
// EnvKeyDisableBasicAuthentication key for env variable DISABLE_BASIC_AUTH // EnvKeyDisableBasicAuthentication key for env variable DISABLE_BASIC_AUTH
EnvKeyDisableBasicAuthentication = "DISABLE_BASIC_AUTHENTICATION" EnvKeyDisableBasicAuthentication = "DISABLE_BASIC_AUTHENTICATION"
// EnvKeyDisableBasicAuthentication key for env variable DISABLE_MOBILE_BASIC_AUTH
EnvKeyDisableMobileBasicAuthentication = "DISABLE_MOBILE_BASIC_AUTHENTICATION"
// EnvKeyDisableMagicLinkLogin key for env variable DISABLE_MAGIC_LINK_LOGIN // EnvKeyDisableMagicLinkLogin key for env variable DISABLE_MAGIC_LINK_LOGIN
EnvKeyDisableMagicLinkLogin = "DISABLE_MAGIC_LINK_LOGIN" EnvKeyDisableMagicLinkLogin = "DISABLE_MAGIC_LINK_LOGIN"
// EnvKeyDisableLoginPage key for env variable DISABLE_LOGIN_PAGE // EnvKeyDisableLoginPage key for env variable DISABLE_LOGIN_PAGE
@ -172,18 +133,6 @@ const (
// EnvKeyDisableMultiFactorAuthentication is key for env variable DISABLE_MULTI_FACTOR_AUTHENTICATION // EnvKeyDisableMultiFactorAuthentication is key for env variable DISABLE_MULTI_FACTOR_AUTHENTICATION
// this variable is used to completely disable multi factor authentication. It will have no effect on profile preference // this variable is used to completely disable multi factor authentication. It will have no effect on profile preference
EnvKeyDisableMultiFactorAuthentication = "DISABLE_MULTI_FACTOR_AUTHENTICATION" EnvKeyDisableMultiFactorAuthentication = "DISABLE_MULTI_FACTOR_AUTHENTICATION"
// EnvKeyDisableTOTPLogin is key for env variable DISABLE_TOTP_LOGIN
// this variable is used to completely disable totp verification
EnvKeyDisableTOTPLogin = "DISABLE_TOTP_LOGIN"
// EnvKeyDisableMailOTPLogin is key for env variable DISABLE_MAIL_OTP_LOGIN
// this variable is used to completely disable totp verification
EnvKeyDisableMailOTPLogin = "DISABLE_MAIL_OTP_LOGIN"
// EnvKeyDisablePhoneVerification is key for env variable DISABLE_PHONE_VERIFICATION
// this variable is used to disable phone verification
EnvKeyDisablePhoneVerification = "DISABLE_PHONE_VERIFICATION"
// EnvKeyDisablePlayGround is key for env variable DISABLE_PLAYGROUND
// this variable will disable or enable playground use in dashboard
EnvKeyDisablePlayGround = "DISABLE_PLAYGROUND"
// Slice variables // Slice variables
// EnvKeyRoles key for env variable ROLES // EnvKeyRoles key for env variable ROLES
@ -194,22 +143,4 @@ const (
EnvKeyDefaultRoles = "DEFAULT_ROLES" EnvKeyDefaultRoles = "DEFAULT_ROLES"
// EnvKeyAllowedOrigins key for env variable ALLOWED_ORIGINS // EnvKeyAllowedOrigins key for env variable ALLOWED_ORIGINS
EnvKeyAllowedOrigins = "ALLOWED_ORIGINS" EnvKeyAllowedOrigins = "ALLOWED_ORIGINS"
// For oauth/openid/authorize
// EnvKeyDefaultAuthorizeResponseType key for env variable DEFAULT_AUTHORIZE_RESPONSE_TYPE
// This env is used for setting default response type in authorize handler
EnvKeyDefaultAuthorizeResponseType = "DEFAULT_AUTHORIZE_RESPONSE_TYPE"
// EnvKeyDefaultAuthorizeResponseMode key for env variable DEFAULT_AUTHORIZE_RESPONSE_MODE
// This env is used for setting default response mode in authorize handler
EnvKeyDefaultAuthorizeResponseMode = "DEFAULT_AUTHORIZE_RESPONSE_MODE"
// Twilio env variables
// EnvKeyTwilioAPIKey key for env variable TWILIO_API_KEY
EnvKeyTwilioAPIKey = "TWILIO_API_KEY"
// EnvKeyTwilioAPISecret key for env variable TWILIO_API_SECRET
EnvKeyTwilioAPISecret = "TWILIO_API_SECRET"
// EnvKeyTwilioAccountSID key for env variable TWILIO_ACCOUNT_SID
EnvKeyTwilioAccountSID = "TWILIO_ACCOUNT_SID"
// EnvKeyTwilioSender key for env variable TWILIO_SENDER
EnvKeyTwilioSender = "TWILIO_SENDER"
) )

View File

@ -1,22 +0,0 @@
package constants
const (
// - query: for Authorization Code grant. 302 Found triggers redirect.
ResponseModeQuery = "query"
// - fragment: for Implicit grant. 302 Found triggers redirect.
ResponseModeFragment = "fragment"
// - form_post: 200 OK with response parameters embedded in an HTML form as hidden parameters.
ResponseModeFormPost = "form_post"
// - web_message: For Silent Authentication. Uses HTML5 web messaging.
ResponseModeWebMessage = "web_message"
// For the Authorization Code grant, use response_type=code to include the authorization code.
ResponseTypeCode = "code"
// For the Implicit grant, use response_type=token to include an access token.
ResponseTypeToken = "token"
// For the Implicit grant of id_token, use response_type=id_token to include an identifier token.
ResponseTypeIDToken = "id_token"
// Constant indicating the "signup" screen hint for customizing authentication process and redirect to a signup page.
ScreenHintSignUp = "signup"
)

View File

@ -16,12 +16,4 @@ const (
LinkedInEmailURL = "https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))" LinkedInEmailURL = "https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))"
TwitterUserInfoURL = "https://api.twitter.com/2/users/me?user.fields=id,name,profile_image_url,username" TwitterUserInfoURL = "https://api.twitter.com/2/users/me?user.fields=id,name,profile_image_url,username"
// RobloxUserInfoURL is the URL to get user info from Roblox
RobloxUserInfoURL = "https://apis.roblox.com/oauth/v1/userinfo"
DiscordUserInfoURL = "https://discord.com/api/oauth2/@me"
// Get microsoft user info.
// Ref: https://learn.microsoft.com/en-us/azure/active-directory/develop/userinfo
MicrosoftUserInfoURL = "https://graph.microsoft.com/oidc/userinfo"
) )

View File

@ -14,14 +14,3 @@ const (
// VerificationTypeOTP is the otp verification type // VerificationTypeOTP is the otp verification type
VerificationTypeOTP = "verify_otp" VerificationTypeOTP = "verify_otp"
) )
var (
// VerificationTypes is slice of all verification types
VerificationTypes = []string{
VerificationTypeBasicAuthSignup,
VerificationTypeMagicLinkLogin,
VerificationTypeUpdateEmail,
VerificationTypeForgotPassword,
VerificationTypeInviteMember,
}
)

View File

@ -15,6 +15,4 @@ const (
UserAccessEnabledWebhookEvent = `user.access_enabled` UserAccessEnabledWebhookEvent = `user.access_enabled`
// UserDeletedWebhookEvent name for user deleted event // UserDeletedWebhookEvent name for user deleted event
UserDeletedWebhookEvent = `user.deleted` UserDeletedWebhookEvent = `user.deleted`
// UserDeactivatedWebhookEvent name for user deactivated event
UserDeactivatedWebhookEvent = `user.deactivated`
) )

View File

@ -29,20 +29,10 @@ func SetSession(gc *gin.Context, sessionID string) {
domain = "." + domain domain = "." + domain
} }
// Since app cookie can come from cross site it becomes important to set this in lax mode when insecure.
// Example person using custom UI on their app domain and making request to authorizer domain.
// For more information check:
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
// https://github.com/gin-gonic/gin/blob/master/context.go#L86
// TODO add ability to sameSite = none / strict from dashboard
if !appCookieSecure {
gc.SetSameSite(http.SameSiteLaxMode)
} else {
gc.SetSameSite(http.SameSiteNoneMode)
}
// TODO allow configuring from dashboard // TODO allow configuring from dashboard
year := 60 * 60 * 24 * 365 year := 60 * 60 * 24 * 365
gc.SetSameSite(http.SameSiteNoneMode)
gc.SetCookie(constants.AppCookieName+"_session", sessionID, year, "/", host, secure, httpOnly) gc.SetCookie(constants.AppCookieName+"_session", sessionID, year, "/", host, secure, httpOnly)
gc.SetCookie(constants.AppCookieName+"_session_domain", sessionID, year, "/", domain, secure, httpOnly) gc.SetCookie(constants.AppCookieName+"_session_domain", sessionID, year, "/", domain, secure, httpOnly)
} }

View File

@ -1,89 +0,0 @@
package cookie
import (
"net/http"
"net/url"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/memorystore"
"github.com/authorizerdev/authorizer/server/parsers"
"github.com/gin-gonic/gin"
)
// SetMfaSession sets the mfa session cookie in the response
func SetMfaSession(gc *gin.Context, sessionID string) {
appCookieSecure, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyAppCookieSecure)
if err != nil {
log.Debug("Error while getting app cookie secure from env variable: %v", err)
appCookieSecure = true
}
secure := appCookieSecure
httpOnly := appCookieSecure
hostname := parsers.GetHost(gc)
host, _ := parsers.GetHostParts(hostname)
domain := parsers.GetDomainName(hostname)
if domain != "localhost" {
domain = "." + domain
}
// Since app cookie can come from cross site it becomes important to set this in lax mode when insecure.
// Example person using custom UI on their app domain and making request to authorizer domain.
// For more information check:
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
// https://github.com/gin-gonic/gin/blob/master/context.go#L86
// TODO add ability to sameSite = none / strict from dashboard
if !appCookieSecure {
gc.SetSameSite(http.SameSiteLaxMode)
} else {
gc.SetSameSite(http.SameSiteNoneMode)
}
// TODO allow configuring from dashboard
age := 60
gc.SetCookie(constants.MfaCookieName+"_session", sessionID, age, "/", host, secure, httpOnly)
gc.SetCookie(constants.MfaCookieName+"_session_domain", sessionID, age, "/", domain, secure, httpOnly)
}
// DeleteMfaSession deletes the mfa session cookies to expire
func DeleteMfaSession(gc *gin.Context) {
appCookieSecure, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyAppCookieSecure)
if err != nil {
log.Debug("Error while getting app cookie secure from env variable: %v", err)
appCookieSecure = true
}
secure := appCookieSecure
httpOnly := appCookieSecure
hostname := parsers.GetHost(gc)
host, _ := parsers.GetHostParts(hostname)
domain := parsers.GetDomainName(hostname)
if domain != "localhost" {
domain = "." + domain
}
gc.SetSameSite(http.SameSiteNoneMode)
gc.SetCookie(constants.MfaCookieName+"_session", "", -1, "/", host, secure, httpOnly)
gc.SetCookie(constants.MfaCookieName+"_session_domain", "", -1, "/", domain, secure, httpOnly)
}
// GetMfaSession gets the mfa session cookie from context
func GetMfaSession(gc *gin.Context) (string, error) {
var cookie *http.Cookie
var err error
cookie, err = gc.Request.Cookie(constants.MfaCookieName + "_session")
if err != nil {
cookie, err = gc.Request.Cookie(constants.MfaCookieName + "_session_domain")
if err != nil {
return "", err
}
}
decodedValue, err := url.PathUnescape(cookie.Value)
if err != nil {
return "", err
}
return decodedValue, nil
}

View File

@ -1,9 +1,7 @@
package crypto package crypto
import ( import (
"crypto/sha256"
"crypto/x509" "crypto/x509"
"encoding/hex"
"encoding/json" "encoding/json"
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
@ -127,27 +125,12 @@ func EncryptEnvData(data map[string]interface{}) (string, error) {
return EncryptB64(string(encryptedConfig)), nil return EncryptB64(string(encryptedConfig)), nil
} }
// getSHA256 calculates the SHA-256 hash of a string
func getSHA256(input string) string {
hash := sha256.New()
hash.Write([]byte(input))
return hex.EncodeToString(hash.Sum(nil))
}
// VerifyPassword compares a stored hashed password with a user-provided password
func VerifyPassword(storedHashedPassword, userProvidedPassword string) error {
// CompareHashAndPassword returns nil on success
passwordSHA256 := getSHA256(userProvidedPassword)
err := bcrypt.CompareHashAndPassword([]byte(storedHashedPassword), []byte(passwordSHA256))
return err
}
// EncryptPassword is used for encrypting password // EncryptPassword is used for encrypting password
func EncryptPassword(password string) (string, error) { func EncryptPassword(password string) (string, error) {
passwordSHA256 := getSHA256(password) pw, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
pw, err := bcrypt.GenerateFromPassword([]byte(passwordSHA256), bcrypt.DefaultCost)
if err != nil { if err != nil {
return "", err return "", err
} }
return string(pw), nil return string(pw), nil
} }

View File

@ -3,9 +3,7 @@ package crypto
import ( import (
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
"crypto/sha256"
"crypto/x509" "crypto/x509"
"encoding/base64"
"encoding/pem" "encoding/pem"
"errors" "errors"
) )
@ -118,24 +116,3 @@ func AsRSAStr(privateKey *rsa.PrivateKey, publickKey *rsa.PublicKey) (string, st
return privParsedPem, pubParsedPem, nil return privParsedPem, pubParsedPem, nil
} }
func EncryptRSA(message string, key rsa.PublicKey) (string, error) {
label := []byte("OAEP Encrypted")
rng := rand.Reader
ciphertext, err := rsa.EncryptOAEP(sha256.New(), rng, &key, []byte(message), label)
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(ciphertext), nil
}
func DecryptRSA(cipherText string, privateKey rsa.PrivateKey) (string, error) {
ct, _ := base64.StdEncoding.DecodeString(cipherText)
label := []byte("OAEP Encrypted")
rng := rand.Reader
plaintext, err := rsa.DecryptOAEP(sha256.New(), rng, &privateKey, ct, label)
if err != nil {
return "", err
}
return string(plaintext), nil
}

View File

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

View File

@ -1,16 +0,0 @@
package models
// Note: any change here should be reflected in providers/casandra/provider.go as it does not have model support in collection creation
// Authenticators model for db
type Authenticator struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
UserID string `gorm:"type:char(36)" json:"user_id" bson:"user_id" cql:"user_id" dynamo:"user_id" index:"user_id,hash"`
Method string `json:"method" bson:"method" cql:"method" dynamo:"method"`
Secret string `json:"secret" bson:"secret" cql:"secret" dynamo:"secret"`
RecoveryCodes *string `json:"recovery_codes" bson:"recovery_codes" cql:"recovery_codes" dynamo:"recovery_codes"`
VerifiedAt *int64 `json:"verified_at" bson:"verified_at" cql:"verified_at" dynamo:"verified_at"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
}

View File

@ -9,14 +9,14 @@ import (
// EmailTemplate model for database // EmailTemplate model for database
type EmailTemplate struct { type EmailTemplate struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb 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" dynamo:"id,hash"` ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
EventName string `gorm:"unique" json:"event_name" bson:"event_name" cql:"event_name" dynamo:"event_name" index:"event_name,hash"` EventName string `gorm:"unique" json:"event_name" bson:"event_name" cql:"event_name"`
Subject string `json:"subject" bson:"subject" cql:"subject" dynamo:"subject"` Subject string `gorm:"type:text" json:"subject" bson:"subject" cql:"subject"`
Template string `json:"template" bson:"template" cql:"template" dynamo:"template"` Template string `gorm:"type:text" json:"template" bson:"template" cql:"template"`
Design string `json:"design" bson:"design" cql:"design" dynamo:"design"` Design string `gorm:"type:text" json:"design" bson:"design" cql:"design"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"` CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"` UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
} }
// AsAPIEmailTemplate to return email template as graphql response object // AsAPIEmailTemplate to return email template as graphql response object

View File

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

View File

@ -1,6 +1,6 @@
package models package models
// CollectionList / Tables available for authorizer in the database // Collections / Tables available for authorizer in the database
type CollectionList struct { type CollectionList struct {
User string User string
VerificationRequest string VerificationRequest string
@ -10,8 +10,6 @@ type CollectionList struct {
WebhookLog string WebhookLog string
EmailTemplate string EmailTemplate string
OTP string OTP string
SMSVerificationRequest string
Authenticators string
} }
var ( var (
@ -27,7 +25,5 @@ var (
WebhookLog: Prefix + "webhook_logs", WebhookLog: Prefix + "webhook_logs",
EmailTemplate: Prefix + "email_templates", EmailTemplate: Prefix + "email_templates",
OTP: Prefix + "otps", OTP: Prefix + "otps",
SMSVerificationRequest: Prefix + "sms_verification_requests",
Authenticators: Prefix + "authenticators",
} }
) )

View File

@ -1,24 +1,12 @@
package models package models
const (
// FieldName email is the field name for email
FieldNameEmail = "email"
// FieldNamePhoneNumber is the field name for phone number
FieldNamePhoneNumber = "phone_number"
)
// OTP model for database // OTP model for database
type OTP struct { type OTP struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb 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" dynamo:"id,hash"` ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
Email string `gorm:"index" json:"email" bson:"email" cql:"email" dynamo:"email" index:"email,hash"` Email string `gorm:"unique" json:"email" bson:"email" cql:"email"`
PhoneNumber string `gorm:"index" json:"phone_number" bson:"phone_number" cql:"phone_number" dynamo:"phone_number"` Otp string `json:"otp" bson:"otp" cql:"otp"`
Otp string `json:"otp" bson:"otp" cql:"otp" dynamo:"otp"` ExpiresAt int64 `json:"expires_at" bson:"expires_at" cql:"expires_at"`
ExpiresAt int64 `json:"expires_at" bson:"expires_at" cql:"expires_at" dynamo:"expires_at"` CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"` UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
}
type Paging struct {
ID string `json:"id,omitempty" dynamo:"id,hash"`
} }

View File

@ -4,11 +4,11 @@ package models
// Session model for db // Session model for db
type Session struct { type Session struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb 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" dynamo:"id,hash"` ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
UserID string `gorm:"type:char(36)" json:"user_id" bson:"user_id" cql:"user_id" dynamo:"user_id" index:"user_id,hash"` UserID string `gorm:"type:char(36)" json:"user_id" bson:"user_id" cql:"user_id"`
UserAgent string `json:"user_agent" bson:"user_agent" cql:"user_agent" dynamo:"user_agent"` UserAgent string `json:"user_agent" bson:"user_agent" cql:"user_agent"`
IP string `json:"ip" bson:"ip" cql:"ip" dynamo:"ip"` IP string `json:"ip" bson:"ip" cql:"ip"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"` CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"` UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
} }

View File

@ -12,35 +12,33 @@ import (
// User model for db // User model for db
type User struct { type User struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb 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" dynamo:"id,hash"` ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
Email *string `gorm:"index" json:"email" bson:"email" cql:"email" dynamo:"email" index:"email,hash"` Email string `gorm:"unique" json:"email" bson:"email" cql:"email"`
EmailVerifiedAt *int64 `json:"email_verified_at" bson:"email_verified_at" cql:"email_verified_at" dynamo:"email_verified_at"` EmailVerifiedAt *int64 `json:"email_verified_at" bson:"email_verified_at" cql:"email_verified_at"`
Password *string `json:"password" bson:"password" cql:"password" dynamo:"password"` Password *string `gorm:"type:text" json:"password" bson:"password" cql:"password"`
SignupMethods string `json:"signup_methods" bson:"signup_methods" cql:"signup_methods" dynamo:"signup_methods"` SignupMethods string `json:"signup_methods" bson:"signup_methods" cql:"signup_methods"`
GivenName *string `json:"given_name" bson:"given_name" cql:"given_name" dynamo:"given_name"` GivenName *string `json:"given_name" bson:"given_name" cql:"given_name"`
FamilyName *string `json:"family_name" bson:"family_name" cql:"family_name" dynamo:"family_name"` FamilyName *string `json:"family_name" bson:"family_name" cql:"family_name"`
MiddleName *string `json:"middle_name" bson:"middle_name" cql:"middle_name" dynamo:"middle_name"` MiddleName *string `json:"middle_name" bson:"middle_name" cql:"middle_name"`
Nickname *string `json:"nickname" bson:"nickname" cql:"nickname" dynamo:"nickname"` Nickname *string `json:"nickname" bson:"nickname" cql:"nickname"`
Gender *string `json:"gender" bson:"gender" cql:"gender" dynamo:"gender"` Gender *string `json:"gender" bson:"gender" cql:"gender"`
Birthdate *string `json:"birthdate" bson:"birthdate" cql:"birthdate" dynamo:"birthdate"` Birthdate *string `json:"birthdate" bson:"birthdate" cql:"birthdate"`
PhoneNumber *string `gorm:"index" json:"phone_number" bson:"phone_number" cql:"phone_number" dynamo:"phone_number"` 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" dynamo:"phone_number_verified_at"` PhoneNumberVerifiedAt *int64 `json:"phone_number_verified_at" bson:"phone_number_verified_at" cql:"phone_number_verified_at"`
Picture *string `json:"picture" bson:"picture" cql:"picture" dynamo:"picture"` Picture *string `gorm:"type:text" json:"picture" bson:"picture" cql:"picture"`
Roles string `json:"roles" bson:"roles" cql:"roles" dynamo:"roles"` Roles string `json:"roles" bson:"roles" cql:"roles"`
RevokedTimestamp *int64 `json:"revoked_timestamp" bson:"revoked_timestamp" cql:"revoked_timestamp" dynamo:"revoked_timestamp"` RevokedTimestamp *int64 `json:"revoked_timestamp" bson:"revoked_timestamp" cql:"revoked_timestamp"`
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled" bson:"is_multi_factor_auth_enabled" cql:"is_multi_factor_auth_enabled" dynamo:"is_multi_factor_auth_enabled"` IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled" bson:"is_multi_factor_auth_enabled" cql:"is_multi_factor_auth_enabled"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"` UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"` CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
AppData *string `json:"app_data" bson:"app_data" cql:"app_data" dynamo:"app_data"`
} }
func (user *User) AsAPIUser() *model.User { func (user *User) AsAPIUser() *model.User {
isEmailVerified := user.EmailVerifiedAt != nil isEmailVerified := user.EmailVerifiedAt != nil
isPhoneVerified := user.PhoneNumberVerifiedAt != nil isPhoneVerified := user.PhoneNumberVerifiedAt != nil
appDataMap := make(map[string]interface{})
json.Unmarshal([]byte(refs.StringValue(user.AppData)), &appDataMap)
// id := user.ID // id := user.ID
// if strings.Contains(id, Collections.User+"/") { // if strings.Contains(id, Collections.User+"/") {
// id = strings.TrimPrefix(id, Collections.User+"/") // id = strings.TrimPrefix(id, Collections.User+"/")
@ -54,7 +52,7 @@ func (user *User) AsAPIUser() *model.User {
FamilyName: user.FamilyName, FamilyName: user.FamilyName,
MiddleName: user.MiddleName, MiddleName: user.MiddleName,
Nickname: user.Nickname, Nickname: user.Nickname,
PreferredUsername: user.Email, PreferredUsername: refs.NewStringRef(user.Email),
Gender: user.Gender, Gender: user.Gender,
Birthdate: user.Birthdate, Birthdate: user.Birthdate,
PhoneNumber: user.PhoneNumber, PhoneNumber: user.PhoneNumber,
@ -65,7 +63,6 @@ func (user *User) AsAPIUser() *model.User {
IsMultiFactorAuthEnabled: user.IsMultiFactorAuthEnabled, IsMultiFactorAuthEnabled: user.IsMultiFactorAuthEnabled,
CreatedAt: refs.NewInt64Ref(user.CreatedAt), CreatedAt: refs.NewInt64Ref(user.CreatedAt),
UpdatedAt: refs.NewInt64Ref(user.UpdatedAt), UpdatedAt: refs.NewInt64Ref(user.UpdatedAt),
AppData: appDataMap,
} }
} }

View File

@ -11,16 +11,16 @@ import (
// VerificationRequest model for db // VerificationRequest model for db
type VerificationRequest struct { type VerificationRequest struct {
Key string `json:"_key,omitempty" bson:"_key" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb 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" dynamo:"id,hash"` ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
Token string `json:"token" bson:"token" cql:"jwt_token" dynamo:"token" index:"token,hash"` 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" dynamo:"identifier"` 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" dynamo:"expires_at"` 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" dynamo:"email"` Email string `gorm:"uniqueIndex:idx_email_identifier;type:varchar(256)" json:"email" bson:"email" cql:"email"`
Nonce string `json:"nonce" bson:"nonce" cql:"nonce" dynamo:"nonce"` Nonce string `gorm:"type:text" json:"nonce" bson:"nonce" cql:"nonce"`
RedirectURI string `json:"redirect_uri" bson:"redirect_uri" cql:"redirect_uri" dynamo:"redirect_uri"` 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" dynamo:"created_at"` CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"` UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
} }
func (v *VerificationRequest) AsAPIVerificationRequest() *model.VerificationRequest { func (v *VerificationRequest) AsAPIVerificationRequest() *model.VerificationRequest {

View File

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

View File

@ -11,14 +11,14 @@ import (
// WebhookLog model for db // WebhookLog model for db
type WebhookLog struct { type WebhookLog struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb 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" dynamo:"id,hash"` ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
HttpStatus int64 `json:"http_status" bson:"http_status" cql:"http_status" dynamo:"http_status"` HttpStatus int64 `json:"http_status" bson:"http_status" cql:"http_status"`
Response string `json:"response" bson:"response" cql:"response" dynamo:"response"` Response string `gorm:"type:text" json:"response" bson:"response" cql:"response"`
Request string `json:"request" bson:"request" cql:"request" dynamo:"request"` Request string `gorm:"type:text" json:"request" bson:"request" cql:"request"`
WebhookID string `gorm:"type:char(36)" json:"webhook_id" bson:"webhook_id" cql:"webhook_id" dynamo:"webhook_id" index:"webhook_id,hash"` WebhookID string `gorm:"type:char(36)" json:"webhook_id" bson:"webhook_id" cql:"webhook_id"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"` CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"` UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
} }
// AsAPIWebhookLog to return webhook log as graphql response object // AsAPIWebhookLog to return webhook log as graphql response object

View File

@ -1,78 +0,0 @@
package arangodb
import (
"context"
"fmt"
"time"
"github.com/google/uuid"
arangoDriver "github.com/arangodb/go-driver"
"github.com/authorizerdev/authorizer/server/db/models"
)
func (p *provider) AddAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
exists, _ := p.GetAuthenticatorDetailsByUserId(ctx, authenticators.UserID, authenticators.Method)
if exists != nil {
return authenticators, nil
}
if authenticators.ID == "" {
authenticators.ID = uuid.New().String()
}
authenticators.Key = authenticators.ID
authenticators.CreatedAt = time.Now().Unix()
authenticators.UpdatedAt = time.Now().Unix()
authenticatorsCollection, _ := p.db.Collection(ctx, models.Collections.Authenticators)
meta, err := authenticatorsCollection.CreateDocument(arangoDriver.WithOverwrite(ctx), authenticators)
if err != nil {
return nil, err
}
authenticators.Key = meta.Key
authenticators.ID = meta.ID.String()
return authenticators, nil
}
func (p *provider) UpdateAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
authenticators.UpdatedAt = time.Now().Unix()
collection, _ := p.db.Collection(ctx, models.Collections.Authenticators)
meta, err := collection.UpdateDocument(ctx, authenticators.Key, authenticators)
if err != nil {
return nil, err
}
authenticators.Key = meta.Key
authenticators.ID = meta.ID.String()
return authenticators, nil
}
func (p *provider) GetAuthenticatorDetailsByUserId(ctx context.Context, userId string, authenticatorType string) (*models.Authenticator, error) {
var authenticators *models.Authenticator
query := fmt.Sprintf("FOR d in %s FILTER d.user_id == @user_id AND d.method == @method LIMIT 1 RETURN d", models.Collections.Authenticators)
bindVars := map[string]interface{}{
"user_id": userId,
"method": authenticatorType,
}
cursor, err := p.db.Query(ctx, query, bindVars)
if err != nil {
return nil, err
}
defer cursor.Close()
for {
if !cursor.HasMore() {
if authenticators == nil {
return authenticators, fmt.Errorf("authenticator not found")
}
break
}
_, err := cursor.ReadDocument(ctx, &authenticators)
if err != nil {
return nil, err
}
}
return authenticators, nil
}

View File

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"time" "time"
"github.com/arangodb/go-driver"
arangoDriver "github.com/arangodb/go-driver" arangoDriver "github.com/arangodb/go-driver"
"github.com/authorizerdev/authorizer/server/db/models" "github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/graph/model" "github.com/authorizerdev/authorizer/server/graph/model"
@ -12,14 +13,16 @@ import (
) )
// AddEmailTemplate to add EmailTemplate // AddEmailTemplate to add EmailTemplate
func (p *provider) AddEmailTemplate(ctx context.Context, emailTemplate *models.EmailTemplate) (*model.EmailTemplate, error) { func (p *provider) AddEmailTemplate(ctx context.Context, emailTemplate models.EmailTemplate) (*model.EmailTemplate, error) {
if emailTemplate.ID == "" { if emailTemplate.ID == "" {
emailTemplate.ID = uuid.New().String() emailTemplate.ID = uuid.New().String()
emailTemplate.Key = emailTemplate.ID emailTemplate.Key = emailTemplate.ID
} }
emailTemplate.Key = emailTemplate.ID emailTemplate.Key = emailTemplate.ID
emailTemplate.CreatedAt = time.Now().Unix() emailTemplate.CreatedAt = time.Now().Unix()
emailTemplate.UpdatedAt = time.Now().Unix() emailTemplate.UpdatedAt = time.Now().Unix()
emailTemplateCollection, _ := p.db.Collection(ctx, models.Collections.EmailTemplate) emailTemplateCollection, _ := p.db.Collection(ctx, models.Collections.EmailTemplate)
_, err := emailTemplateCollection.CreateDocument(ctx, emailTemplate) _, err := emailTemplateCollection.CreateDocument(ctx, emailTemplate)
if err != nil { if err != nil {
@ -29,63 +32,74 @@ func (p *provider) AddEmailTemplate(ctx context.Context, emailTemplate *models.E
} }
// UpdateEmailTemplate to update EmailTemplate // UpdateEmailTemplate to update EmailTemplate
func (p *provider) UpdateEmailTemplate(ctx context.Context, emailTemplate *models.EmailTemplate) (*model.EmailTemplate, error) { func (p *provider) UpdateEmailTemplate(ctx context.Context, emailTemplate models.EmailTemplate) (*model.EmailTemplate, error) {
emailTemplate.UpdatedAt = time.Now().Unix() emailTemplate.UpdatedAt = time.Now().Unix()
emailTemplateCollection, _ := p.db.Collection(ctx, models.Collections.EmailTemplate) emailTemplateCollection, _ := p.db.Collection(ctx, models.Collections.EmailTemplate)
meta, err := emailTemplateCollection.UpdateDocument(ctx, emailTemplate.Key, emailTemplate) meta, err := emailTemplateCollection.UpdateDocument(ctx, emailTemplate.Key, emailTemplate)
if err != nil { if err != nil {
return nil, err return nil, err
} }
emailTemplate.Key = meta.Key emailTemplate.Key = meta.Key
emailTemplate.ID = meta.ID.String() emailTemplate.ID = meta.ID.String()
return emailTemplate.AsAPIEmailTemplate(), nil return emailTemplate.AsAPIEmailTemplate(), nil
} }
// ListEmailTemplates to list EmailTemplate // ListEmailTemplates to list EmailTemplate
func (p *provider) ListEmailTemplate(ctx context.Context, pagination *model.Pagination) (*model.EmailTemplates, error) { func (p *provider) ListEmailTemplate(ctx context.Context, pagination model.Pagination) (*model.EmailTemplates, error) {
emailTemplates := []*model.EmailTemplate{} emailTemplates := []*model.EmailTemplate{}
query := fmt.Sprintf("FOR d in %s SORT d.created_at DESC LIMIT %d, %d RETURN d", models.Collections.EmailTemplate, pagination.Offset, pagination.Limit) query := fmt.Sprintf("FOR d in %s SORT d.created_at DESC LIMIT %d, %d RETURN d", models.Collections.EmailTemplate, pagination.Offset, pagination.Limit)
sctx := arangoDriver.WithQueryFullCount(ctx)
sctx := driver.WithQueryFullCount(ctx)
cursor, err := p.db.Query(sctx, query, nil) cursor, err := p.db.Query(sctx, query, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer cursor.Close() defer cursor.Close()
paginationClone := pagination paginationClone := pagination
paginationClone.Total = cursor.Statistics().FullCount() paginationClone.Total = cursor.Statistics().FullCount()
for { for {
var emailTemplate *models.EmailTemplate var emailTemplate models.EmailTemplate
meta, err := cursor.ReadDocument(ctx, &emailTemplate) meta, err := cursor.ReadDocument(ctx, &emailTemplate)
if arangoDriver.IsNoMoreDocuments(err) { if arangoDriver.IsNoMoreDocuments(err) {
break break
} else if err != nil { } else if err != nil {
return nil, err return nil, err
} }
if meta.Key != "" { if meta.Key != "" {
emailTemplates = append(emailTemplates, emailTemplate.AsAPIEmailTemplate()) emailTemplates = append(emailTemplates, emailTemplate.AsAPIEmailTemplate())
} }
} }
return &model.EmailTemplates{ return &model.EmailTemplates{
Pagination: paginationClone, Pagination: &paginationClone,
EmailTemplates: emailTemplates, EmailTemplates: emailTemplates,
}, nil }, nil
} }
// GetEmailTemplateByID to get EmailTemplate by id // GetEmailTemplateByID to get EmailTemplate by id
func (p *provider) GetEmailTemplateByID(ctx context.Context, emailTemplateID string) (*model.EmailTemplate, error) { func (p *provider) GetEmailTemplateByID(ctx context.Context, emailTemplateID string) (*model.EmailTemplate, error) {
var emailTemplate *models.EmailTemplate var emailTemplate models.EmailTemplate
query := fmt.Sprintf("FOR d in %s FILTER d._key == @email_template_id RETURN d", models.Collections.EmailTemplate) query := fmt.Sprintf("FOR d in %s FILTER d._key == @email_template_id RETURN d", models.Collections.EmailTemplate)
bindVars := map[string]interface{}{ bindVars := map[string]interface{}{
"email_template_id": emailTemplateID, "email_template_id": emailTemplateID,
} }
cursor, err := p.db.Query(ctx, query, bindVars) cursor, err := p.db.Query(ctx, query, bindVars)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer cursor.Close() defer cursor.Close()
for { for {
if !cursor.HasMore() { if !cursor.HasMore() {
if emailTemplate == nil { if emailTemplate.Key == "" {
return nil, fmt.Errorf("email template not found") return nil, fmt.Errorf("email template not found")
} }
break break
@ -100,19 +114,21 @@ func (p *provider) GetEmailTemplateByID(ctx context.Context, emailTemplateID str
// GetEmailTemplateByEventName to get EmailTemplate by event_name // GetEmailTemplateByEventName to get EmailTemplate by event_name
func (p *provider) GetEmailTemplateByEventName(ctx context.Context, eventName string) (*model.EmailTemplate, error) { func (p *provider) GetEmailTemplateByEventName(ctx context.Context, eventName string) (*model.EmailTemplate, error) {
var emailTemplate *models.EmailTemplate var emailTemplate models.EmailTemplate
query := fmt.Sprintf("FOR d in %s FILTER d.event_name == @event_name RETURN d", models.Collections.EmailTemplate) query := fmt.Sprintf("FOR d in %s FILTER d.event_name == @event_name RETURN d", models.Collections.EmailTemplate)
bindVars := map[string]interface{}{ bindVars := map[string]interface{}{
"event_name": eventName, "event_name": eventName,
} }
cursor, err := p.db.Query(ctx, query, bindVars) cursor, err := p.db.Query(ctx, query, bindVars)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer cursor.Close() defer cursor.Close()
for { for {
if !cursor.HasMore() { if !cursor.HasMore() {
if emailTemplate == nil { if emailTemplate.Key == "" {
return nil, fmt.Errorf("email template not found") return nil, fmt.Errorf("email template not found")
} }
break break

View File

@ -12,7 +12,7 @@ import (
) )
// AddEnv to save environment information in database // AddEnv to save environment information in database
func (p *provider) AddEnv(ctx context.Context, env *models.Env) (*models.Env, error) { func (p *provider) AddEnv(ctx context.Context, env models.Env) (models.Env, error) {
if env.ID == "" { if env.ID == "" {
env.ID = uuid.New().String() env.ID = uuid.New().String()
env.Key = env.ID env.Key = env.ID
@ -23,7 +23,7 @@ func (p *provider) AddEnv(ctx context.Context, env *models.Env) (*models.Env, er
configCollection, _ := p.db.Collection(ctx, models.Collections.Env) configCollection, _ := p.db.Collection(ctx, models.Collections.Env)
meta, err := configCollection.CreateDocument(arangoDriver.WithOverwrite(ctx), env) meta, err := configCollection.CreateDocument(arangoDriver.WithOverwrite(ctx), env)
if err != nil { if err != nil {
return nil, err return env, err
} }
env.Key = meta.Key env.Key = meta.Key
env.ID = meta.ID.String() env.ID = meta.ID.String()
@ -31,12 +31,12 @@ func (p *provider) AddEnv(ctx context.Context, env *models.Env) (*models.Env, er
} }
// UpdateEnv to update environment information in database // UpdateEnv to update environment information in database
func (p *provider) UpdateEnv(ctx context.Context, env *models.Env) (*models.Env, error) { func (p *provider) UpdateEnv(ctx context.Context, env models.Env) (models.Env, error) {
env.UpdatedAt = time.Now().Unix() env.UpdatedAt = time.Now().Unix()
collection, _ := p.db.Collection(ctx, models.Collections.Env) collection, _ := p.db.Collection(ctx, models.Collections.Env)
meta, err := collection.UpdateDocument(ctx, env.Key, env) meta, err := collection.UpdateDocument(ctx, env.Key, env)
if err != nil { if err != nil {
return nil, err return env, err
} }
env.Key = meta.Key env.Key = meta.Key
@ -45,24 +45,26 @@ func (p *provider) UpdateEnv(ctx context.Context, env *models.Env) (*models.Env,
} }
// GetEnv to get environment information from database // GetEnv to get environment information from database
func (p *provider) GetEnv(ctx context.Context) (*models.Env, error) { func (p *provider) GetEnv(ctx context.Context) (models.Env, error) {
var env *models.Env var env models.Env
query := fmt.Sprintf("FOR d in %s RETURN d", models.Collections.Env) query := fmt.Sprintf("FOR d in %s RETURN d", models.Collections.Env)
cursor, err := p.db.Query(ctx, query, nil) cursor, err := p.db.Query(ctx, query, nil)
if err != nil { if err != nil {
return nil, err return env, err
} }
defer cursor.Close() defer cursor.Close()
for { for {
if !cursor.HasMore() { if !cursor.HasMore() {
if env == nil { if env.Key == "" {
return env, fmt.Errorf("config not found") return env, fmt.Errorf("config not found")
} }
break break
} }
_, err := cursor.ReadDocument(ctx, &env) _, err := cursor.ReadDocument(ctx, &env)
if err != nil { if err != nil {
return nil, err return env, err
} }
} }

View File

@ -2,7 +2,6 @@ package arangodb
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"time" "time"
@ -13,20 +12,7 @@ import (
// UpsertOTP to add or update otp // UpsertOTP to add or update otp
func (p *provider) UpsertOTP(ctx context.Context, otpParam *models.OTP) (*models.OTP, error) { func (p *provider) UpsertOTP(ctx context.Context, otpParam *models.OTP) (*models.OTP, error) {
// check if email or phone number is present otp, _ := p.GetOTPByEmail(ctx, otpParam.Email)
if otpParam.Email == "" && otpParam.PhoneNumber == "" {
return nil, errors.New("email or phone_number is required")
}
uniqueField := models.FieldNameEmail
if otpParam.Email == "" && otpParam.PhoneNumber != "" {
uniqueField = models.FieldNamePhoneNumber
}
var otp *models.OTP
if uniqueField == models.FieldNameEmail {
otp, _ = p.GetOTPByEmail(ctx, otpParam.Email)
} else {
otp, _ = p.GetOTPByPhoneNumber(ctx, otpParam.PhoneNumber)
}
shouldCreate := false shouldCreate := false
if otp == nil { if otp == nil {
id := uuid.NewString() id := uuid.NewString()
@ -35,7 +21,6 @@ func (p *provider) UpsertOTP(ctx context.Context, otpParam *models.OTP) (*models
Key: id, Key: id,
Otp: otpParam.Otp, Otp: otpParam.Otp,
Email: otpParam.Email, Email: otpParam.Email,
PhoneNumber: otpParam.PhoneNumber,
ExpiresAt: otpParam.ExpiresAt, ExpiresAt: otpParam.ExpiresAt,
CreatedAt: time.Now().Unix(), CreatedAt: time.Now().Unix(),
} }
@ -44,8 +29,10 @@ func (p *provider) UpsertOTP(ctx context.Context, otpParam *models.OTP) (*models
otp.Otp = otpParam.Otp otp.Otp = otpParam.Otp
otp.ExpiresAt = otpParam.ExpiresAt otp.ExpiresAt = otpParam.ExpiresAt
} }
otp.UpdatedAt = time.Now().Unix() otp.UpdatedAt = time.Now().Unix()
otpCollection, _ := p.db.Collection(ctx, models.Collections.OTP) otpCollection, _ := p.db.Collection(ctx, models.Collections.OTP)
var meta driver.DocumentMeta var meta driver.DocumentMeta
var err error var err error
if shouldCreate { if shouldCreate {
@ -53,9 +40,11 @@ func (p *provider) UpsertOTP(ctx context.Context, otpParam *models.OTP) (*models
} else { } else {
meta, err = otpCollection.UpdateDocument(ctx, otp.Key, otp) meta, err = otpCollection.UpdateDocument(ctx, otp.Key, otp)
} }
if err != nil { if err != nil {
return nil, err return nil, err
} }
otp.Key = meta.Key otp.Key = meta.Key
otp.ID = meta.ID.String() otp.ID = meta.ID.String()
return otp, nil return otp, nil
@ -63,20 +52,22 @@ func (p *provider) UpsertOTP(ctx context.Context, otpParam *models.OTP) (*models
// GetOTPByEmail to get otp for a given email address // GetOTPByEmail to get otp for a given email address
func (p *provider) GetOTPByEmail(ctx context.Context, emailAddress string) (*models.OTP, error) { func (p *provider) GetOTPByEmail(ctx context.Context, emailAddress string) (*models.OTP, error) {
var otp *models.OTP var otp models.OTP
query := fmt.Sprintf("FOR d in %s FILTER d.email == @email RETURN d", models.Collections.OTP) query := fmt.Sprintf("FOR d in %s FILTER d.email == @email RETURN d", models.Collections.OTP)
bindVars := map[string]interface{}{ bindVars := map[string]interface{}{
"email": emailAddress, "email": emailAddress,
} }
cursor, err := p.db.Query(ctx, query, bindVars) cursor, err := p.db.Query(ctx, query, bindVars)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer cursor.Close() defer cursor.Close()
for { for {
if !cursor.HasMore() { if !cursor.HasMore() {
if otp == nil { if otp.Key == "" {
return nil, fmt.Errorf("otp with given email not found") return nil, fmt.Errorf("email template not found")
} }
break break
} }
@ -85,34 +76,8 @@ func (p *provider) GetOTPByEmail(ctx context.Context, emailAddress string) (*mod
return nil, err return nil, err
} }
} }
return otp, nil
}
// GetOTPByPhoneNumber to get otp for a given phone number return &otp, nil
func (p *provider) GetOTPByPhoneNumber(ctx context.Context, phoneNumber string) (*models.OTP, error) {
var otp *models.OTP
query := fmt.Sprintf("FOR d in %s FILTER d.phone_number == @phone_number RETURN d", models.Collections.OTP)
bindVars := map[string]interface{}{
"phone_number": phoneNumber,
}
cursor, err := p.db.Query(ctx, query, bindVars)
if err != nil {
return nil, err
}
defer cursor.Close()
for {
if !cursor.HasMore() {
if otp == nil {
return nil, fmt.Errorf("otp with given phone_number not found")
}
break
}
_, err := cursor.ReadDocument(ctx, &otp)
if err != nil {
return nil, err
}
}
return otp, nil
} }
// DeleteOTP to delete otp // DeleteOTP to delete otp
@ -122,5 +87,6 @@ func (p *provider) DeleteOTP(ctx context.Context, otp *models.OTP) error {
if err != nil { if err != nil {
return err return err
} }
return nil return nil
} }

View File

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

View File

@ -9,11 +9,12 @@ import (
) )
// AddSession to save session information in database // AddSession to save session information in database
func (p *provider) AddSession(ctx context.Context, session *models.Session) error { func (p *provider) AddSession(ctx context.Context, session models.Session) error {
if session.ID == "" { if session.ID == "" {
session.ID = uuid.New().String() session.ID = uuid.New().String()
session.Key = session.ID session.Key = session.ID
} }
session.CreatedAt = time.Now().Unix() session.CreatedAt = time.Now().Unix()
session.UpdatedAt = time.Now().Unix() session.UpdatedAt = time.Now().Unix()
sessionCollection, _ := p.db.Collection(ctx, models.Collections.Session) sessionCollection, _ := p.db.Collection(ctx, models.Collections.Session)
@ -23,8 +24,3 @@ func (p *provider) AddSession(ctx context.Context, session *models.Session) erro
} }
return nil return nil
} }
// DeleteSession to delete session information from database
func (p *provider) DeleteSession(ctx context.Context, userId string) error {
return nil
}

View File

@ -7,6 +7,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/arangodb/go-driver"
arangoDriver "github.com/arangodb/go-driver" arangoDriver "github.com/arangodb/go-driver"
"github.com/google/uuid" "github.com/google/uuid"
@ -14,11 +15,10 @@ import (
"github.com/authorizerdev/authorizer/server/db/models" "github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/graph/model" "github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/memorystore" "github.com/authorizerdev/authorizer/server/memorystore"
"github.com/authorizerdev/authorizer/server/refs"
) )
// AddUser to save user information in database // AddUser to save user information in database
func (p *provider) AddUser(ctx context.Context, user *models.User) (*models.User, error) { func (p *provider) AddUser(ctx context.Context, user models.User) (models.User, error) {
if user.ID == "" { if user.ID == "" {
user.ID = uuid.New().String() user.ID = uuid.New().String()
user.Key = user.ID user.Key = user.ID
@ -27,27 +27,17 @@ func (p *provider) AddUser(ctx context.Context, user *models.User) (*models.User
if user.Roles == "" { if user.Roles == "" {
defaultRoles, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyDefaultRoles) defaultRoles, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyDefaultRoles)
if err != nil { if err != nil {
return nil, err return user, err
} }
user.Roles = defaultRoles user.Roles = defaultRoles
} }
if user.PhoneNumber != nil && strings.TrimSpace(refs.StringValue(user.PhoneNumber)) != "" {
if u, _ := p.GetUserByPhoneNumber(ctx, refs.StringValue(user.PhoneNumber)); u != nil && u.ID != user.ID {
return user, fmt.Errorf("user with given phone number already exists")
}
} else if user.Email != nil && strings.TrimSpace(refs.StringValue(user.Email)) != "" {
if u, _ := p.GetUserByEmail(ctx, refs.StringValue(user.Email)); u != nil && u.ID != user.ID {
return user, fmt.Errorf("user with given email already exists")
}
}
user.CreatedAt = time.Now().Unix() user.CreatedAt = time.Now().Unix()
user.UpdatedAt = time.Now().Unix() user.UpdatedAt = time.Now().Unix()
userCollection, _ := p.db.Collection(ctx, models.Collections.User) userCollection, _ := p.db.Collection(ctx, models.Collections.User)
meta, err := userCollection.CreateDocument(arangoDriver.WithOverwrite(ctx), user) meta, err := userCollection.CreateDocument(arangoDriver.WithOverwrite(ctx), user)
if err != nil { if err != nil {
return nil, err return user, err
} }
user.Key = meta.Key user.Key = meta.Key
user.ID = meta.ID.String() user.ID = meta.ID.String()
@ -56,13 +46,12 @@ func (p *provider) AddUser(ctx context.Context, user *models.User) (*models.User
} }
// UpdateUser to update user information in database // UpdateUser to update user information in database
func (p *provider) UpdateUser(ctx context.Context, user *models.User) (*models.User, error) { func (p *provider) UpdateUser(ctx context.Context, user models.User) (models.User, error) {
user.UpdatedAt = time.Now().Unix() user.UpdatedAt = time.Now().Unix()
collection, _ := p.db.Collection(ctx, models.Collections.User) collection, _ := p.db.Collection(ctx, models.Collections.User)
meta, err := collection.UpdateDocument(ctx, user.Key, user) meta, err := collection.UpdateDocument(ctx, user.Key, user)
if err != nil { if err != nil {
return nil, err return user, err
} }
user.Key = meta.Key user.Key = meta.Key
@ -71,12 +60,13 @@ func (p *provider) UpdateUser(ctx context.Context, user *models.User) (*models.U
} }
// DeleteUser to delete user information from database // DeleteUser to delete user information from database
func (p *provider) DeleteUser(ctx context.Context, user *models.User) error { func (p *provider) DeleteUser(ctx context.Context, user models.User) error {
collection, _ := p.db.Collection(ctx, models.Collections.User) collection, _ := p.db.Collection(ctx, models.Collections.User)
_, err := collection.RemoveDocument(ctx, user.Key) _, err := collection.RemoveDocument(ctx, user.Key)
if err != nil { if err != nil {
return err return err
} }
query := fmt.Sprintf(`FOR d IN %s FILTER d.user_id == @user_id REMOVE { _key: d._key } IN %s`, models.Collections.Session, models.Collections.Session) query := fmt.Sprintf(`FOR d IN %s FILTER d.user_id == @user_id REMOVE { _key: d._key } IN %s`, models.Collections.Session, models.Collections.Session)
bindVars := map[string]interface{}{ bindVars := map[string]interface{}{
"user_id": user.Key, "user_id": user.Key,
@ -86,91 +76,106 @@ func (p *provider) DeleteUser(ctx context.Context, user *models.User) error {
return err return err
} }
defer cursor.Close() defer cursor.Close()
return nil return nil
} }
// ListUsers to get list of users from database // ListUsers to get list of users from database
func (p *provider) ListUsers(ctx context.Context, pagination *model.Pagination) (*model.Users, error) { func (p *provider) ListUsers(ctx context.Context, pagination model.Pagination) (*model.Users, error) {
var users []*model.User var users []*model.User
sctx := arangoDriver.WithQueryFullCount(ctx) sctx := driver.WithQueryFullCount(ctx)
query := fmt.Sprintf("FOR d in %s SORT d.created_at DESC LIMIT %d, %d RETURN d", models.Collections.User, pagination.Offset, pagination.Limit) query := fmt.Sprintf("FOR d in %s SORT d.created_at DESC LIMIT %d, %d RETURN d", models.Collections.User, pagination.Offset, pagination.Limit)
cursor, err := p.db.Query(sctx, query, nil) cursor, err := p.db.Query(sctx, query, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer cursor.Close() defer cursor.Close()
paginationClone := pagination paginationClone := pagination
paginationClone.Total = cursor.Statistics().FullCount() paginationClone.Total = cursor.Statistics().FullCount()
for { for {
var user *models.User var user models.User
meta, err := cursor.ReadDocument(ctx, &user) meta, err := cursor.ReadDocument(ctx, &user)
if arangoDriver.IsNoMoreDocuments(err) { if arangoDriver.IsNoMoreDocuments(err) {
break break
} else if err != nil { } else if err != nil {
return nil, err return nil, err
} }
if meta.Key != "" { if meta.Key != "" {
users = append(users, user.AsAPIUser()) users = append(users, user.AsAPIUser())
} }
} }
return &model.Users{ return &model.Users{
Pagination: paginationClone, Pagination: &paginationClone,
Users: users, Users: users,
}, nil }, nil
} }
// GetUserByEmail to get user information from database using email address // GetUserByEmail to get user information from database using email address
func (p *provider) GetUserByEmail(ctx context.Context, email string) (*models.User, error) { func (p *provider) GetUserByEmail(ctx context.Context, email string) (models.User, error) {
var user *models.User var user models.User
query := fmt.Sprintf("FOR d in %s FILTER d.email == @email RETURN d", models.Collections.User) query := fmt.Sprintf("FOR d in %s FILTER d.email == @email RETURN d", models.Collections.User)
bindVars := map[string]interface{}{ bindVars := map[string]interface{}{
"email": email, "email": email,
} }
cursor, err := p.db.Query(ctx, query, bindVars) cursor, err := p.db.Query(ctx, query, bindVars)
if err != nil { if err != nil {
return nil, err return user, err
} }
defer cursor.Close() defer cursor.Close()
for { for {
if !cursor.HasMore() { if !cursor.HasMore() {
if user == nil { if user.Key == "" {
return nil, fmt.Errorf("user not found") return user, fmt.Errorf("user not found")
} }
break break
} }
_, err := cursor.ReadDocument(ctx, &user) _, err := cursor.ReadDocument(ctx, &user)
if err != nil { if err != nil {
return nil, err return user, err
} }
} }
return user, nil return user, nil
} }
// GetUserByID to get user information from database using user ID // GetUserByID to get user information from database using user ID
func (p *provider) GetUserByID(ctx context.Context, id string) (*models.User, error) { func (p *provider) GetUserByID(ctx context.Context, id string) (models.User, error) {
var user *models.User var user models.User
query := fmt.Sprintf("FOR d in %s FILTER d._id == @id LIMIT 1 RETURN d", models.Collections.User) query := fmt.Sprintf("FOR d in %s FILTER d._id == @id LIMIT 1 RETURN d", models.Collections.User)
bindVars := map[string]interface{}{ bindVars := map[string]interface{}{
"id": id, "id": id,
} }
cursor, err := p.db.Query(ctx, query, bindVars) cursor, err := p.db.Query(ctx, query, bindVars)
if err != nil { if err != nil {
return nil, err return user, err
} }
defer cursor.Close() defer cursor.Close()
for { for {
if !cursor.HasMore() { if !cursor.HasMore() {
if user == nil { if user.Key == "" {
return nil, fmt.Errorf("user not found") return user, fmt.Errorf("user not found")
} }
break break
} }
_, err := cursor.ReadDocument(ctx, &user) _, err := cursor.ReadDocument(ctx, &user)
if err != nil { if err != nil {
return nil, err return user, err
} }
} }
return user, nil return user, nil
} }
@ -179,12 +184,14 @@ func (p *provider) GetUserByID(ctx context.Context, id string) (*models.User, er
func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{}, ids []string) error { func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{}, ids []string) error {
// set updated_at time for all users // set updated_at time for all users
data["updated_at"] = time.Now().Unix() data["updated_at"] = time.Now().Unix()
userInfoBytes, err := json.Marshal(data) userInfoBytes, err := json.Marshal(data)
if err != nil { if err != nil {
return err return err
} }
query := "" query := ""
if len(ids) > 0 { if ids != nil && len(ids) > 0 {
keysArray := "" keysArray := ""
for _, id := range ids { for _, id := range ids {
keysArray += fmt.Sprintf("'%s', ", id) keysArray += fmt.Sprintf("'%s', ", id)
@ -195,36 +202,12 @@ func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{},
} else { } else {
query = fmt.Sprintf("FOR u IN %s UPDATE u._key with %s IN %s", models.Collections.User, string(userInfoBytes), models.Collections.User) query = fmt.Sprintf("FOR u IN %s UPDATE u._key with %s IN %s", models.Collections.User, string(userInfoBytes), models.Collections.User)
} }
_, err = p.db.Query(ctx, query, nil) _, err = p.db.Query(ctx, query, nil)
if err != nil { if err != nil {
return err return err
} }
return nil return nil
} }
// GetUserByPhoneNumber to get user information from database using phone number
func (p *provider) GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*models.User, error) {
var user *models.User
query := fmt.Sprintf("FOR d in %s FILTER d.phone_number == @phone_number RETURN d", models.Collections.User)
bindVars := map[string]interface{}{
"phone_number": phoneNumber,
}
cursor, err := p.db.Query(ctx, query, bindVars)
if err != nil {
return nil, err
}
defer cursor.Close()
for {
if !cursor.HasMore() {
if user == nil {
return nil, fmt.Errorf("user not found")
}
break
}
_, err := cursor.ReadDocument(ctx, &user)
if err != nil {
return nil, err
}
}
return user, nil
}

View File

@ -5,102 +5,114 @@ import (
"fmt" "fmt"
"time" "time"
arangoDriver "github.com/arangodb/go-driver" "github.com/arangodb/go-driver"
"github.com/authorizerdev/authorizer/server/db/models" "github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/graph/model" "github.com/authorizerdev/authorizer/server/graph/model"
"github.com/google/uuid" "github.com/google/uuid"
) )
// AddVerification to save verification request in database // AddVerification to save verification request in database
func (p *provider) AddVerificationRequest(ctx context.Context, verificationRequest *models.VerificationRequest) (*models.VerificationRequest, error) { func (p *provider) AddVerificationRequest(ctx context.Context, verificationRequest models.VerificationRequest) (models.VerificationRequest, error) {
if verificationRequest.ID == "" { if verificationRequest.ID == "" {
verificationRequest.ID = uuid.New().String() verificationRequest.ID = uuid.New().String()
verificationRequest.Key = verificationRequest.ID verificationRequest.Key = verificationRequest.ID
} }
verificationRequest.CreatedAt = time.Now().Unix() verificationRequest.CreatedAt = time.Now().Unix()
verificationRequest.UpdatedAt = time.Now().Unix() verificationRequest.UpdatedAt = time.Now().Unix()
verificationRequestRequestCollection, _ := p.db.Collection(ctx, models.Collections.VerificationRequest) verificationRequestRequestCollection, _ := p.db.Collection(ctx, models.Collections.VerificationRequest)
meta, err := verificationRequestRequestCollection.CreateDocument(ctx, verificationRequest) meta, err := verificationRequestRequestCollection.CreateDocument(ctx, verificationRequest)
if err != nil { if err != nil {
return nil, err return verificationRequest, err
} }
verificationRequest.Key = meta.Key verificationRequest.Key = meta.Key
verificationRequest.ID = meta.ID.String() verificationRequest.ID = meta.ID.String()
return verificationRequest, nil return verificationRequest, nil
} }
// GetVerificationRequestByToken to get verification request from database using token // GetVerificationRequestByToken to get verification request from database using token
func (p *provider) GetVerificationRequestByToken(ctx context.Context, token string) (*models.VerificationRequest, error) { func (p *provider) GetVerificationRequestByToken(ctx context.Context, token string) (models.VerificationRequest, error) {
var verificationRequest *models.VerificationRequest var verificationRequest models.VerificationRequest
query := fmt.Sprintf("FOR d in %s FILTER d.token == @token LIMIT 1 RETURN d", models.Collections.VerificationRequest) query := fmt.Sprintf("FOR d in %s FILTER d.token == @token LIMIT 1 RETURN d", models.Collections.VerificationRequest)
bindVars := map[string]interface{}{ bindVars := map[string]interface{}{
"token": token, "token": token,
} }
cursor, err := p.db.Query(ctx, query, bindVars) cursor, err := p.db.Query(ctx, query, bindVars)
if err != nil { if err != nil {
return nil, err return verificationRequest, err
} }
defer cursor.Close() defer cursor.Close()
for { for {
if !cursor.HasMore() { if !cursor.HasMore() {
if verificationRequest == nil { if verificationRequest.Key == "" {
return verificationRequest, fmt.Errorf("verification request not found") return verificationRequest, fmt.Errorf("verification request not found")
} }
break break
} }
_, err := cursor.ReadDocument(ctx, &verificationRequest) _, err := cursor.ReadDocument(ctx, &verificationRequest)
if err != nil { if err != nil {
return nil, err return verificationRequest, err
} }
} }
return verificationRequest, nil return verificationRequest, nil
} }
// GetVerificationRequestByEmail to get verification request by email from database // GetVerificationRequestByEmail to get verification request by email from database
func (p *provider) GetVerificationRequestByEmail(ctx context.Context, email string, identifier string) (*models.VerificationRequest, error) { func (p *provider) GetVerificationRequestByEmail(ctx context.Context, email string, identifier string) (models.VerificationRequest, error) {
var verificationRequest *models.VerificationRequest var verificationRequest models.VerificationRequest
query := fmt.Sprintf("FOR d in %s FILTER d.email == @email FILTER d.identifier == @identifier LIMIT 1 RETURN d", models.Collections.VerificationRequest) query := fmt.Sprintf("FOR d in %s FILTER d.email == @email FILTER d.identifier == @identifier LIMIT 1 RETURN d", models.Collections.VerificationRequest)
bindVars := map[string]interface{}{ bindVars := map[string]interface{}{
"email": email, "email": email,
"identifier": identifier, "identifier": identifier,
} }
cursor, err := p.db.Query(ctx, query, bindVars) cursor, err := p.db.Query(ctx, query, bindVars)
if err != nil { if err != nil {
return nil, err return verificationRequest, err
} }
defer cursor.Close() defer cursor.Close()
for { for {
if !cursor.HasMore() { if !cursor.HasMore() {
if verificationRequest == nil { if verificationRequest.Key == "" {
return verificationRequest, fmt.Errorf("verification request not found") return verificationRequest, fmt.Errorf("verification request not found")
} }
break break
} }
_, err := cursor.ReadDocument(ctx, &verificationRequest) _, err := cursor.ReadDocument(ctx, &verificationRequest)
if err != nil { if err != nil {
return nil, err return verificationRequest, err
} }
} }
return verificationRequest, nil return verificationRequest, nil
} }
// ListVerificationRequests to get list of verification requests from database // ListVerificationRequests to get list of verification requests from database
func (p *provider) ListVerificationRequests(ctx context.Context, pagination *model.Pagination) (*model.VerificationRequests, error) { func (p *provider) ListVerificationRequests(ctx context.Context, pagination model.Pagination) (*model.VerificationRequests, error) {
var verificationRequests []*model.VerificationRequest var verificationRequests []*model.VerificationRequest
sctx := arangoDriver.WithQueryFullCount(ctx) sctx := driver.WithQueryFullCount(ctx)
query := fmt.Sprintf("FOR d in %s SORT d.created_at DESC LIMIT %d, %d RETURN d", models.Collections.VerificationRequest, pagination.Offset, pagination.Limit) query := fmt.Sprintf("FOR d in %s SORT d.created_at DESC LIMIT %d, %d RETURN d", models.Collections.VerificationRequest, pagination.Offset, pagination.Limit)
cursor, err := p.db.Query(sctx, query, nil) cursor, err := p.db.Query(sctx, query, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer cursor.Close() defer cursor.Close()
paginationClone := pagination paginationClone := pagination
paginationClone.Total = cursor.Statistics().FullCount() paginationClone.Total = cursor.Statistics().FullCount()
for { for {
var verificationRequest *models.VerificationRequest var verificationRequest models.VerificationRequest
meta, err := cursor.ReadDocument(ctx, &verificationRequest) meta, err := cursor.ReadDocument(ctx, &verificationRequest)
if arangoDriver.IsNoMoreDocuments(err) { if driver.IsNoMoreDocuments(err) {
break break
} else if err != nil { } else if err != nil {
return nil, err return nil, err
@ -111,16 +123,17 @@ func (p *provider) ListVerificationRequests(ctx context.Context, pagination *mod
} }
} }
return &model.VerificationRequests{ return &model.VerificationRequests{
VerificationRequests: verificationRequests, VerificationRequests: verificationRequests,
Pagination: paginationClone, Pagination: &paginationClone,
}, nil }, nil
} }
// DeleteVerificationRequest to delete verification request from database // DeleteVerificationRequest to delete verification request from database
func (p *provider) DeleteVerificationRequest(ctx context.Context, verificationRequest *models.VerificationRequest) error { func (p *provider) DeleteVerificationRequest(ctx context.Context, verificationRequest models.VerificationRequest) error {
collection, _ := p.db.Collection(ctx, models.Collections.VerificationRequest) collection, _ := p.db.Collection(nil, models.Collections.VerificationRequest)
_, err := collection.RemoveDocument(ctx, verificationRequest.Key) _, err := collection.RemoveDocument(nil, verificationRequest.Key)
if err != nil { if err != nil {
return err return err
} }

View File

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

View File

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"time" "time"
"github.com/arangodb/go-driver"
arangoDriver "github.com/arangodb/go-driver" arangoDriver "github.com/arangodb/go-driver"
"github.com/authorizerdev/authorizer/server/db/models" "github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/graph/model" "github.com/authorizerdev/authorizer/server/graph/model"
@ -12,11 +13,12 @@ import (
) )
// AddWebhookLog to add webhook log // AddWebhookLog to add webhook log
func (p *provider) AddWebhookLog(ctx context.Context, webhookLog *models.WebhookLog) (*model.WebhookLog, error) { func (p *provider) AddWebhookLog(ctx context.Context, webhookLog models.WebhookLog) (*model.WebhookLog, error) {
if webhookLog.ID == "" { if webhookLog.ID == "" {
webhookLog.ID = uuid.New().String() webhookLog.ID = uuid.New().String()
webhookLog.Key = webhookLog.ID webhookLog.Key = webhookLog.ID
} }
webhookLog.Key = webhookLog.ID webhookLog.Key = webhookLog.ID
webhookLog.CreatedAt = time.Now().Unix() webhookLog.CreatedAt = time.Now().Unix()
webhookLog.UpdatedAt = time.Now().Unix() webhookLog.UpdatedAt = time.Now().Unix()
@ -29,38 +31,46 @@ func (p *provider) AddWebhookLog(ctx context.Context, webhookLog *models.Webhook
} }
// ListWebhookLogs to list webhook logs // ListWebhookLogs to list webhook logs
func (p *provider) ListWebhookLogs(ctx context.Context, pagination *model.Pagination, webhookID string) (*model.WebhookLogs, error) { func (p *provider) ListWebhookLogs(ctx context.Context, pagination model.Pagination, webhookID string) (*model.WebhookLogs, error) {
webhookLogs := []*model.WebhookLog{} webhookLogs := []*model.WebhookLog{}
bindVariables := map[string]interface{}{} bindVariables := map[string]interface{}{}
query := fmt.Sprintf("FOR d in %s SORT d.created_at DESC LIMIT %d, %d RETURN d", models.Collections.WebhookLog, pagination.Offset, pagination.Limit) query := fmt.Sprintf("FOR d in %s SORT d.created_at DESC LIMIT %d, %d RETURN d", models.Collections.WebhookLog, pagination.Offset, pagination.Limit)
if webhookID != "" { if webhookID != "" {
query = fmt.Sprintf("FOR d in %s FILTER d.webhook_id == @webhook_id SORT d.created_at DESC LIMIT %d, %d RETURN d", models.Collections.WebhookLog, pagination.Offset, pagination.Limit) query = fmt.Sprintf("FOR d in %s FILTER d.webhook_id == @webhook_id SORT d.created_at DESC LIMIT %d, %d RETURN d", models.Collections.WebhookLog, pagination.Offset, pagination.Limit)
bindVariables = map[string]interface{}{ bindVariables = map[string]interface{}{
"webhook_id": webhookID, "webhook_id": webhookID,
} }
} }
sctx := arangoDriver.WithQueryFullCount(ctx)
sctx := driver.WithQueryFullCount(ctx)
cursor, err := p.db.Query(sctx, query, bindVariables) cursor, err := p.db.Query(sctx, query, bindVariables)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer cursor.Close() defer cursor.Close()
paginationClone := pagination paginationClone := pagination
paginationClone.Total = cursor.Statistics().FullCount() paginationClone.Total = cursor.Statistics().FullCount()
for { for {
var webhookLog *models.WebhookLog var webhookLog models.WebhookLog
meta, err := cursor.ReadDocument(ctx, &webhookLog) meta, err := cursor.ReadDocument(ctx, &webhookLog)
if arangoDriver.IsNoMoreDocuments(err) { if arangoDriver.IsNoMoreDocuments(err) {
break break
} else if err != nil { } else if err != nil {
return nil, err return nil, err
} }
if meta.Key != "" { if meta.Key != "" {
webhookLogs = append(webhookLogs, webhookLog.AsAPIWebhookLog()) webhookLogs = append(webhookLogs, webhookLog.AsAPIWebhookLog())
} }
} }
return &model.WebhookLogs{ return &model.WebhookLogs{
Pagination: paginationClone, Pagination: &paginationClone,
WebhookLogs: webhookLogs, WebhookLogs: webhookLogs,
}, nil }, nil
} }

View File

@ -1,133 +0,0 @@
package cassandradb
import (
"context"
"encoding/json"
"fmt"
"reflect"
"strings"
"time"
"github.com/gocql/gocql"
"github.com/google/uuid"
"github.com/authorizerdev/authorizer/server/db/models"
)
func (p *provider) AddAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
exists, _ := p.GetAuthenticatorDetailsByUserId(ctx, authenticators.UserID, authenticators.Method)
if exists != nil {
return authenticators, nil
}
if authenticators.ID == "" {
authenticators.ID = uuid.New().String()
}
authenticators.CreatedAt = time.Now().Unix()
authenticators.UpdatedAt = time.Now().Unix()
bytes, err := json.Marshal(authenticators)
if err != nil {
return nil, err
}
// use decoder instead of json.Unmarshall, because it converts int64 -> float64 after unmarshalling
decoder := json.NewDecoder(strings.NewReader(string(bytes)))
decoder.UseNumber()
authenticatorsMap := map[string]interface{}{}
err = decoder.Decode(&authenticatorsMap)
if err != nil {
return nil, err
}
fields := "("
values := "("
for key, value := range authenticatorsMap {
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.Authenticators, fields, values)
err = p.db.Query(query).Exec()
if err != nil {
return nil, err
}
return authenticators, nil
}
func (p *provider) UpdateAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
authenticators.UpdatedAt = time.Now().Unix()
bytes, err := json.Marshal(authenticators)
if err != nil {
return nil, err
}
// use decoder instead of json.Unmarshall, because it converts int64 -> float64 after unmarshalling
decoder := json.NewDecoder(strings.NewReader(string(bytes)))
decoder.UseNumber()
authenticatorsMap := map[string]interface{}{}
err = decoder.Decode(&authenticatorsMap)
if err != nil {
return nil, err
}
updateFields := ""
for key, value := range authenticatorsMap {
if key == "_id" {
continue
}
if key == "_key" {
continue
}
if value == nil {
updateFields += fmt.Sprintf("%s = null, ", key)
continue
}
valueType := reflect.TypeOf(value)
if valueType.Name() == "string" {
updateFields += fmt.Sprintf("%s = '%s', ", key, value.(string))
} 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.Authenticators, updateFields, authenticators.ID)
err = p.db.Query(query).Exec()
if err != nil {
return nil, err
}
return authenticators, nil
}
func (p *provider) GetAuthenticatorDetailsByUserId(ctx context.Context, userId string, authenticatorType string) (*models.Authenticator, error) {
var authenticators models.Authenticator
query := fmt.Sprintf("SELECT id, user_id, method, secret, recovery_codes, verified_at, created_at, updated_at FROM %s WHERE user_id = '%s' AND method = '%s' LIMIT 1 ALLOW FILTERING", KeySpace+"."+models.Collections.Authenticators, userId, authenticatorType)
err := p.db.Query(query).Consistency(gocql.One).Scan(&authenticators.ID, &authenticators.UserID, &authenticators.Method, &authenticators.Secret, &authenticators.RecoveryCodes, &authenticators.VerifiedAt, &authenticators.CreatedAt, &authenticators.UpdatedAt)
if err != nil {
return nil, err
}
return &authenticators, nil
}

View File

@ -15,28 +15,33 @@ import (
) )
// AddEmailTemplate to add EmailTemplate // AddEmailTemplate to add EmailTemplate
func (p *provider) AddEmailTemplate(ctx context.Context, emailTemplate *models.EmailTemplate) (*model.EmailTemplate, error) { func (p *provider) AddEmailTemplate(ctx context.Context, emailTemplate models.EmailTemplate) (*model.EmailTemplate, error) {
if emailTemplate.ID == "" { if emailTemplate.ID == "" {
emailTemplate.ID = uuid.New().String() emailTemplate.ID = uuid.New().String()
} }
emailTemplate.Key = emailTemplate.ID emailTemplate.Key = emailTemplate.ID
emailTemplate.CreatedAt = time.Now().Unix() emailTemplate.CreatedAt = time.Now().Unix()
emailTemplate.UpdatedAt = time.Now().Unix() emailTemplate.UpdatedAt = time.Now().Unix()
existingEmailTemplate, _ := p.GetEmailTemplateByEventName(ctx, emailTemplate.EventName) existingEmailTemplate, _ := p.GetEmailTemplateByEventName(ctx, emailTemplate.EventName)
if existingEmailTemplate != nil { if existingEmailTemplate != nil {
return nil, fmt.Errorf("Email template with %s event_name already exists", emailTemplate.EventName) return nil, fmt.Errorf("Email template with %s event_name already exists", emailTemplate.EventName)
} }
insertQuery := fmt.Sprintf("INSERT INTO %s (id, event_name, subject, design, template, created_at, updated_at) VALUES ('%s', '%s', '%s','%s','%s', %d, %d)", KeySpace+"."+models.Collections.EmailTemplate, emailTemplate.ID, emailTemplate.EventName, emailTemplate.Subject, emailTemplate.Design, emailTemplate.Template, emailTemplate.CreatedAt, emailTemplate.UpdatedAt) insertQuery := fmt.Sprintf("INSERT INTO %s (id, event_name, subject, design, template, created_at, updated_at) VALUES ('%s', '%s', '%s','%s','%s', %d, %d)", KeySpace+"."+models.Collections.EmailTemplate, emailTemplate.ID, emailTemplate.EventName, emailTemplate.Subject, emailTemplate.Design, emailTemplate.Template, emailTemplate.CreatedAt, emailTemplate.UpdatedAt)
err := p.db.Query(insertQuery).Exec() err := p.db.Query(insertQuery).Exec()
if err != nil { if err != nil {
return nil, err return nil, err
} }
return emailTemplate.AsAPIEmailTemplate(), nil return emailTemplate.AsAPIEmailTemplate(), nil
} }
// UpdateEmailTemplate to update EmailTemplate // UpdateEmailTemplate to update EmailTemplate
func (p *provider) UpdateEmailTemplate(ctx context.Context, emailTemplate *models.EmailTemplate) (*model.EmailTemplate, error) { func (p *provider) UpdateEmailTemplate(ctx context.Context, emailTemplate models.EmailTemplate) (*model.EmailTemplate, error) {
emailTemplate.UpdatedAt = time.Now().Unix() emailTemplate.UpdatedAt = time.Now().Unix()
bytes, err := json.Marshal(emailTemplate) bytes, err := json.Marshal(emailTemplate)
if err != nil { if err != nil {
return nil, err return nil, err
@ -49,18 +54,22 @@ func (p *provider) UpdateEmailTemplate(ctx context.Context, emailTemplate *model
if err != nil { if err != nil {
return nil, err return nil, err
} }
updateFields := "" updateFields := ""
for key, value := range emailTemplateMap { for key, value := range emailTemplateMap {
if key == "_id" { if key == "_id" {
continue continue
} }
if key == "_key" { if key == "_key" {
continue continue
} }
if value == nil { if value == nil {
updateFields += fmt.Sprintf("%s = null,", key) updateFields += fmt.Sprintf("%s = null,", key)
continue continue
} }
valueType := reflect.TypeOf(value) valueType := reflect.TypeOf(value)
if valueType.Name() == "string" { if valueType.Name() == "string" {
updateFields += fmt.Sprintf("%s = '%s', ", key, value.(string)) updateFields += fmt.Sprintf("%s = '%s', ", key, value.(string))
@ -81,7 +90,7 @@ func (p *provider) UpdateEmailTemplate(ctx context.Context, emailTemplate *model
} }
// ListEmailTemplates to list EmailTemplate // ListEmailTemplates to list EmailTemplate
func (p *provider) ListEmailTemplate(ctx context.Context, pagination *model.Pagination) (*model.EmailTemplates, error) { func (p *provider) ListEmailTemplate(ctx context.Context, pagination model.Pagination) (*model.EmailTemplates, error) {
emailTemplates := []*model.EmailTemplate{} emailTemplates := []*model.EmailTemplate{}
paginationClone := pagination paginationClone := pagination
@ -111,7 +120,7 @@ func (p *provider) ListEmailTemplate(ctx context.Context, pagination *model.Pagi
} }
return &model.EmailTemplates{ return &model.EmailTemplates{
Pagination: paginationClone, Pagination: &paginationClone,
EmailTemplates: emailTemplates, EmailTemplates: emailTemplates,
}, nil }, nil
} }

View File

@ -11,39 +11,43 @@ import (
) )
// AddEnv to save environment information in database // AddEnv to save environment information in database
func (p *provider) AddEnv(ctx context.Context, env *models.Env) (*models.Env, error) { func (p *provider) AddEnv(ctx context.Context, env models.Env) (models.Env, error) {
if env.ID == "" { if env.ID == "" {
env.ID = uuid.New().String() env.ID = uuid.New().String()
} }
env.CreatedAt = time.Now().Unix() env.CreatedAt = time.Now().Unix()
env.UpdatedAt = 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) 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() err := p.db.Query(insertEnvQuery).Exec()
if err != nil { if err != nil {
return nil, err return env, err
} }
return env, nil return env, nil
} }
// UpdateEnv to update environment information in database // UpdateEnv to update environment information in database
func (p *provider) UpdateEnv(ctx context.Context, env *models.Env) (*models.Env, error) { func (p *provider) UpdateEnv(ctx context.Context, env models.Env) (models.Env, error) {
env.UpdatedAt = time.Now().Unix() 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) 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() err := p.db.Query(updateEnvQuery).Exec()
if err != nil { if err != nil {
return nil, err return env, err
} }
return env, nil return env, nil
} }
// GetEnv to get environment information from database // GetEnv to get environment information from database
func (p *provider) GetEnv(ctx context.Context) (*models.Env, error) { func (p *provider) GetEnv(ctx context.Context) (models.Env, error) {
var env models.Env var env models.Env
query := fmt.Sprintf("SELECT id, env, hash, created_at, updated_at FROM %s LIMIT 1", KeySpace+"."+models.Collections.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) err := p.db.Query(query).Consistency(gocql.One).Scan(&env.ID, &env.EnvData, &env.Hash, &env.CreatedAt, &env.UpdatedAt)
if err != nil { if err != nil {
return nil, err return env, err
} }
return &env, nil
return env, nil
} }

View File

@ -2,7 +2,6 @@ package cassandradb
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"time" "time"
@ -13,20 +12,7 @@ import (
// UpsertOTP to add or update otp // UpsertOTP to add or update otp
func (p *provider) UpsertOTP(ctx context.Context, otpParam *models.OTP) (*models.OTP, error) { func (p *provider) UpsertOTP(ctx context.Context, otpParam *models.OTP) (*models.OTP, error) {
// check if email or phone number is present otp, _ := p.GetOTPByEmail(ctx, otpParam.Email)
if otpParam.Email == "" && otpParam.PhoneNumber == "" {
return nil, errors.New("email or phone_number is required")
}
uniqueField := models.FieldNameEmail
if otpParam.Email == "" && otpParam.PhoneNumber != "" {
uniqueField = models.FieldNamePhoneNumber
}
var otp *models.OTP
if uniqueField == models.FieldNameEmail {
otp, _ = p.GetOTPByEmail(ctx, otpParam.Email)
} else {
otp, _ = p.GetOTPByPhoneNumber(ctx, otpParam.PhoneNumber)
}
shouldCreate := false shouldCreate := false
if otp == nil { if otp == nil {
shouldCreate = true shouldCreate = true
@ -34,7 +20,6 @@ func (p *provider) UpsertOTP(ctx context.Context, otpParam *models.OTP) (*models
ID: uuid.NewString(), ID: uuid.NewString(),
Otp: otpParam.Otp, Otp: otpParam.Otp,
Email: otpParam.Email, Email: otpParam.Email,
PhoneNumber: otpParam.PhoneNumber,
ExpiresAt: otpParam.ExpiresAt, ExpiresAt: otpParam.ExpiresAt,
CreatedAt: time.Now().Unix(), CreatedAt: time.Now().Unix(),
UpdatedAt: time.Now().Unix(), UpdatedAt: time.Now().Unix(),
@ -47,7 +32,7 @@ func (p *provider) UpsertOTP(ctx context.Context, otpParam *models.OTP) (*models
otp.UpdatedAt = time.Now().Unix() otp.UpdatedAt = time.Now().Unix()
query := "" query := ""
if shouldCreate { if shouldCreate {
query = fmt.Sprintf(`INSERT INTO %s (id, email, phone_number, otp, expires_at, created_at, updated_at) VALUES ('%s', '%s', '%s', '%s', %d, %d, %d)`, KeySpace+"."+models.Collections.OTP, otp.ID, otp.Email, otp.PhoneNumber, otp.Otp, otp.ExpiresAt, otp.CreatedAt, otp.UpdatedAt) query = fmt.Sprintf(`INSERT INTO %s (id, email, otp, expires_at, created_at, updated_at) VALUES ('%s', '%s', '%s', %d, %d, %d)`, KeySpace+"."+models.Collections.OTP, otp.ID, otp.Email, otp.Otp, otp.ExpiresAt, otp.CreatedAt, otp.UpdatedAt)
} else { } else {
query = fmt.Sprintf(`UPDATE %s SET otp = '%s', expires_at = %d, updated_at = %d WHERE id = '%s'`, KeySpace+"."+models.Collections.OTP, otp.Otp, otp.ExpiresAt, otp.UpdatedAt, otp.ID) query = fmt.Sprintf(`UPDATE %s SET otp = '%s', expires_at = %d, updated_at = %d WHERE id = '%s'`, KeySpace+"."+models.Collections.OTP, otp.Otp, otp.ExpiresAt, otp.UpdatedAt, otp.ID)
} }
@ -63,19 +48,8 @@ func (p *provider) UpsertOTP(ctx context.Context, otpParam *models.OTP) (*models
// GetOTPByEmail to get otp for a given email address // GetOTPByEmail to get otp for a given email address
func (p *provider) GetOTPByEmail(ctx context.Context, emailAddress string) (*models.OTP, error) { func (p *provider) GetOTPByEmail(ctx context.Context, emailAddress string) (*models.OTP, error) {
var otp models.OTP var otp models.OTP
query := fmt.Sprintf(`SELECT id, email, phone_number, otp, expires_at, created_at, updated_at FROM %s WHERE email = '%s' LIMIT 1 ALLOW FILTERING`, KeySpace+"."+models.Collections.OTP, emailAddress) query := fmt.Sprintf(`SELECT id, email, otp, expires_at, created_at, updated_at FROM %s WHERE email = '%s' LIMIT 1 ALLOW FILTERING`, KeySpace+"."+models.Collections.OTP, emailAddress)
err := p.db.Query(query).Consistency(gocql.One).Scan(&otp.ID, &otp.Email, &otp.PhoneNumber, &otp.Otp, &otp.ExpiresAt, &otp.CreatedAt, &otp.UpdatedAt) err := p.db.Query(query).Consistency(gocql.One).Scan(&otp.ID, &otp.Email, &otp.Otp, &otp.ExpiresAt, &otp.CreatedAt, &otp.UpdatedAt)
if err != nil {
return nil, err
}
return &otp, nil
}
// GetOTPByPhoneNumber to get otp for a given phone number
func (p *provider) GetOTPByPhoneNumber(ctx context.Context, phoneNumber string) (*models.OTP, error) {
var otp models.OTP
query := fmt.Sprintf(`SELECT id, email, phone_number, otp, expires_at, created_at, updated_at FROM %s WHERE phone_number = '%s' LIMIT 1 ALLOW FILTERING`, KeySpace+"."+models.Collections.OTP, phoneNumber)
err := p.db.Query(query).Consistency(gocql.One).Scan(&otp.ID, &otp.Email, &otp.PhoneNumber, &otp.Otp, &otp.ExpiresAt, &otp.CreatedAt, &otp.UpdatedAt)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -161,12 +161,6 @@ func NewProvider() (*provider, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
userPhoneNumberIndexQuery := fmt.Sprintf("CREATE INDEX IF NOT EXISTS authorizer_user_phone_number ON %s.%s (phone_number)", KeySpace, models.Collections.User)
err = session.Query(userPhoneNumberIndexQuery).Exec()
if err != nil {
return nil, err
}
// add is_multi_factor_auth_enabled on users table // add is_multi_factor_auth_enabled on users table
userTableAlterQuery := fmt.Sprintf(`ALTER TABLE %s.%s ADD is_multi_factor_auth_enabled boolean`, KeySpace, models.Collections.User) userTableAlterQuery := fmt.Sprintf(`ALTER TABLE %s.%s ADD is_multi_factor_auth_enabled boolean`, KeySpace, models.Collections.User)
err = session.Query(userTableAlterQuery).Exec() err = session.Query(userTableAlterQuery).Exec()
@ -207,13 +201,6 @@ func NewProvider() (*provider, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
// add event_description to webhook table
webhookAlterQuery := fmt.Sprintf(`ALTER TABLE %s.%s ADD (event_description text);`, KeySpace, models.Collections.Webhook)
err = session.Query(webhookAlterQuery).Exec()
if err != nil {
log.Debug("Failed to alter table as column exists: ", err)
// continue
}
webhookLogCollectionQuery := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s.%s (id text, http_status bigint, response text, request text, webhook_id text,updated_at bigint, created_at bigint, PRIMARY KEY (id))", KeySpace, models.Collections.WebhookLog) webhookLogCollectionQuery := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s.%s (id text, http_status bigint, response text, request text, webhook_id text,updated_at bigint, created_at bigint, PRIMARY KEY (id))", KeySpace, models.Collections.WebhookLog)
err = session.Query(webhookLogCollectionQuery).Exec() err = session.Query(webhookLogCollectionQuery).Exec()
@ -254,32 +241,6 @@ func NewProvider() (*provider, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Add phone_number column to otp table
otpAlterQuery := fmt.Sprintf(`ALTER TABLE %s.%s ADD (phone_number text);`, KeySpace, models.Collections.OTP)
err = session.Query(otpAlterQuery).Exec()
if err != nil {
log.Debug("Failed to alter table as column exists: ", err)
// continue
}
// Add app_data column to users table
appDataAlterQuery := fmt.Sprintf(`ALTER TABLE %s.%s ADD (app_data text);`, KeySpace, models.Collections.User)
err = session.Query(appDataAlterQuery).Exec()
if err != nil {
log.Debug("Failed to alter user table as app_data column exists: ", err)
// continue
}
// Add phone number index
otpIndexQueryPhoneNumber := fmt.Sprintf("CREATE INDEX IF NOT EXISTS authorizer_otp_phone_number ON %s.%s (phone_number)", KeySpace, models.Collections.OTP)
err = session.Query(otpIndexQueryPhoneNumber).Exec()
if err != nil {
return nil, err
}
// add authenticators table
totpCollectionQuery := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s.%s (id text, user_id text, method text, secret text, recovery_codes text, verified_at bigint, updated_at bigint, created_at bigint, PRIMARY KEY (id))", KeySpace, models.Collections.Authenticators)
err = session.Query(totpCollectionQuery).Exec()
if err != nil {
return nil, err
}
return &provider{ return &provider{
db: session, db: session,

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