Compare commits

..

No commits in common. "discours" and "fix/dashboard-ui" have entirely different histories.

300 changed files with 10790 additions and 30205 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
@ -105,145 +87,145 @@ For manually testing using graphql playground, you can paste following queries a
```gql ```gql
mutation Signup { mutation Signup {
signup( signup(
params: { params: {
email: "lakhan@yopmail.com" email: "lakhan@yopmail.com"
password: "test" password: "test"
confirm_password: "test" confirm_password: "test"
given_name: "lakhan" given_name: "lakhan"
} }
) { ) {
message message
user { user {
id id
family_name family_name
given_name given_name
email email
email_verified email_verified
} }
} }
} }
mutation ResendEamil { mutation ResendEamil {
resend_verify_email( resend_verify_email(
params: { email: "lakhan@yopmail.com", identifier: "basic_auth_signup" } params: { email: "lakhan@yopmail.com", identifier: "basic_auth_signup" }
) { ) {
message message
} }
} }
query GetVerifyRequests { query GetVerifyRequests {
_verification_requests { _verification_requests {
id id
token token
expires expires
identifier identifier
} }
} }
mutation VerifyEmail { mutation VerifyEmail {
verify_email(params: { token: "" }) { verify_email(params: { token: "" }) {
access_token access_token
expires_at expires_at
user { user {
id id
email email
given_name given_name
email_verified email_verified
} }
} }
} }
mutation Login { mutation Login {
login(params: { email: "lakhan@yopmail.com", password: "test" }) { login(params: { email: "lakhan@yopmail.com", password: "test" }) {
access_token access_token
expires_at expires_at
user { user {
id id
family_name family_name
given_name given_name
email email
} }
} }
} }
query GetSession { query GetSession {
session { session {
access_token access_token
expires_at expires_at
user { user {
id id
given_name given_name
family_name family_name
email email
email_verified email_verified
signup_methods signup_methods
created_at created_at
updated_at updated_at
} }
} }
} }
mutation ForgotPassword { mutation ForgotPassword {
forgot_password(params: { email: "lakhan@yopmail.com" }) { forgot_password(params: { email: "lakhan@yopmail.com" }) {
message message
} }
} }
mutation ResetPassword { mutation ResetPassword {
reset_password( reset_password(
params: { token: "", password: "test", confirm_password: "test" } params: { token: "", password: "test", confirm_password: "test" }
) { ) {
message message
} }
} }
mutation UpdateProfile { mutation UpdateProfile {
update_profile(params: { family_name: "samani" }) { update_profile(params: { family_name: "samani" }) {
message message
} }
} }
query GetUsers { query GetUsers {
_users { _users {
id id
email email
email_verified email_verified
given_name given_name
family_name family_name
picture picture
signup_methods signup_methods
phone_number phone_number
} }
} }
mutation MagicLinkLogin { mutation MagicLinkLogin {
magic_link_login(params: { email: "test@yopmail.com" }) { magic_link_login(params: { email: "test@yopmail.com" }) {
message message
} }
} }
mutation Logout { mutation Logout {
logout { logout {
message message
} }
} }
mutation UpdateUser { mutation UpdateUser {
_update_user( _update_user(
params: { params: {
id: "dafc9400-d603-4ade-997c-83fcd54bbd67" id: "dafc9400-d603-4ade-997c-83fcd54bbd67"
roles: ["user", "admin"] roles: ["user", "admin"]
} }
) { ) {
email email
roles roles
} }
} }
mutation DeleteUser { mutation DeleteUser {
_delete_user(params: { email: "signup.test134523@yopmail.com" }) { _delete_user(params: { email: "signup.test134523@yopmail.com" }) {
message message
} }
} }
``` ```

3
.github/FUNDING.yml vendored
View File

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

View File

@ -7,9 +7,9 @@ on:
default: 'warning' default: 'warning'
type: choice type: choice
options: options:
- info - info
- warning - warning
- debug - debug
tags: tags:
description: 'Tags' description: 'Tags'
required: false required: false
@ -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,16 +20,14 @@ 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 WORKDIR /root/
WORKDIR /authorizer
RUN mkdir app dashboard RUN mkdir app dashboard
COPY --from=node-builder --chown=nobody:nobody /authorizer/app/build app/build COPY --from=node-builder /authorizer/app/build app/build
COPY --from=node-builder --chown=nobody:nobody /authorizer/app/favicon_io app/favicon_io COPY --from=node-builder /authorizer/app/favicon_io app/favicon_io
COPY --from=node-builder --chown=nobody:nobody /authorizer/dashboard/build dashboard/build COPY --from=node-builder /authorizer/dashboard/build dashboard/build
COPY --from=node-builder --chown=nobody:nobody /authorizer/dashboard/favicon_io dashboard/favicon_io COPY --from=node-builder /authorizer/dashboard/favicon_io dashboard/favicon_io
COPY --from=go-builder --chown=nobody:nobody /authorizer/build build COPY --from=go-builder /authorizer/build build
COPY templates templates COPY templates templates
EXPOSE 8080 EXPOSE 8080
USER authorizer
CMD [ "./build/server" ] CMD [ "./build/server" ]

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

@ -7,17 +7,19 @@
Authorizer Authorizer
</h1> </h1>
**Authorizer** is an open-source authentication and authorization solution for your applications. Bring your database and have complete control over the user information. You can self-host authorizer instances and connect to any database (Currently supports 11+ databases including [Postgres](https://www.postgresql.org/), [MySQL](https://www.mysql.com/), [SQLite](https://www.sqlite.org/index.html), [SQLServer](https://www.microsoft.com/en-us/sql-server/), [YugaByte](https://www.yugabyte.com/), [MariaDB](https://mariadb.org/), [PlanetScale](https://planetscale.com/), [CassandraDB](https://cassandra.apache.org/_/index.html), [ScyllaDB](https://www.scylladb.com/), [MongoDB](https://mongodb.com/), [ArangoDB](https://www.arangodb.com/)). **Authorizer** is an open-source authentication and authorization solution for your applications. Bring your database and have complete control over the user information. You can self-host authorizer instances and connect to any database (Currently supports [Postgres](https://www.postgresql.org/), [MySQL](https://www.mysql.com/), [SQLite](https://www.sqlite.org/index.html), [SQLServer](https://www.microsoft.com/en-us/sql-server/), [MongoDB](https://mongodb.com/), [ArangoDB](https://www.arangodb.com/)).
For more information check: ## Table of contents
- [Introduction](#introduction)
- [Getting Started](#getting-started)
- [Contributing](https://github.com/authorizerdev/authorizer/blob/main/.github/CONTRIBUTING.md)
- [Docs](http://docs.authorizer.dev/) - [Docs](http://docs.authorizer.dev/)
- [Discord Community](https://discord.gg/Zv2D5h6kkK) - [Join Community](https://discord.gg/Zv2D5h6kkK)
- [Contributing Guide](https://github.com/authorizerdev/authorizer/blob/main/.github/CONTRIBUTING.md)
# Introduction # Introduction
<img src="https://docs.authorizer.dev/images/authorizer-arch.png" style="height:20em"/> <img src="https://github.com/authorizerdev/authorizer/blob/main/assets/authorizer-architecture.png" style="height:20em"/>
#### We offer the following functionality #### We offer the following functionality
@ -27,22 +29,20 @@ For more information check:
- ✅ OAuth2 and OpenID compatible APIs - ✅ OAuth2 and OpenID compatible APIs
- ✅ APIs to update profile securely - ✅ APIs to update profile securely
- ✅ Forgot password flow using email - ✅ Forgot password flow using email
- ✅ Social logins (Google, Github, Facebook, LinkedIn, Apple more coming soon) - ✅ Social logins (Google, Github, Facebook, more coming soon)
- ✅ Role-based access management - ✅ Role-based access management
- ✅ Password-less login with magic link login - ✅ Password-less login with magic link login
- ✅ Multi factor authentication
- ✅ Email templating
- ✅ Webhooks
## Roadmap ## Roadmap
- [VueJS SDK](https://github.com/authorizerdev/authorizer-vue) - 2 Factor authentication
- [Svelte SDK](https://github.com/authorizerdev/authorizer-svelte) - VueJS SDK
- [Golang SDK](https://github.com/authorizerdev/authorizer-go) - Svelte SDK
- React Native SDK - React Native SDK
- Flutter SDK - Flutter SDK
- Android Native SDK - Android Native SDK
- iOS native SDK - iOS native SDK
- Golang SDK
- Python SDK - Python SDK
- PHP SDK - PHP SDK
- WordPress plugin - WordPress plugin
@ -63,13 +63,11 @@ For more information check:
Deploy production ready Authorizer instance using one click deployment options available below Deploy production ready Authorizer instance using one click deployment options available below
| **Infra provider** | **One-click link** | **Additional information** | | **Infra provider** | **One-click link** | **Additional information** |
| :----------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------: | | :----------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------: |
| 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?template=https://github.com/authorizerdev/authorizer-railway&amp;plugins=postgresql,redis"><img src="https://railway.app/button.svg" style="height: 44px" alt="Deploy on Railway"></a> | [docs](https://docs.authorizer.dev/deployment/railway) |
| Heroku | <a href="https://heroku.com/deploy?template=https://github.com/authorizerdev/authorizer-heroku"><img src="https://www.herokucdn.com/deploy/button.svg" alt="Deploy to Heroku" style="height: 44px;"></a> | [docs](https://docs.authorizer.dev/deployment/heroku) | | 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
}

965
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": "^0.26.0-beta.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

@ -4,12 +4,6 @@ import { AuthorizerProvider } from '@authorizerdev/authorizer-react';
import Root from './Root'; import Root from './Root';
import { createRandomString } from './utils/common'; import { createRandomString } from './utils/common';
declare global {
interface Window {
__authorizer__: any;
}
}
export default function App() { export default function App() {
const searchParams = new URLSearchParams(window.location.search); const searchParams = new URLSearchParams(window.location.search);
const state = searchParams.get('state') || createRandomString(); const state = searchParams.get('state') || createRandomString();
@ -27,12 +21,14 @@ 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> = {
// @ts-ignore
...window['__authorizer__'], ...window['__authorizer__'],
...urlProps, ...urlProps,
}; };
return ( return (
<div <div
style={{ style={{
@ -53,7 +49,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,49 +32,35 @@ 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 || <Footer>
config.is_mobile_basic_authentication_enabled) && <Link
!config.is_magic_link_login_enabled && ( to="#"
<Footer> onClick={() => setView(VIEW_TYPES.FORGOT_PASSWORD)}
<Link style={{ marginBottom: 10 }}
to="#" >
onClick={() => setView(VIEW_TYPES.FORGOT_PASSWORD)} Forgot Password?
style={{ marginBottom: 10 }} </Link>
> </Footer>
Forgot Password?
</Link>
</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

@ -1,28 +1,28 @@
// colors: https://tailwindcss.com/docs/customizing-colors // colors: https://tailwindcss.com/docs/customizing-colors
export const theme = { export const theme = {
colors: { colors: {
primary: '#3B82F6', primary: '#3B82F6',
primaryDisabled: '#60A5FA', primaryDisabled: '#60A5FA',
gray: '#D1D5DB', gray: '#D1D5DB',
danger: '#DC2626', danger: '#DC2626',
success: '#10B981', success: '#10B981',
textColor: '#374151', textColor: '#374151',
}, },
fonts: { fonts: {
// typography // typography
fontStack: '-apple-system, system-ui, sans-serif', fontStack: '-apple-system, system-ui, sans-serif',
// font sizes // font sizes
largeText: '18px', largeText: '18px',
mediumText: '14px', mediumText: '14px',
smallText: '12px', smallText: '12px',
tinyText: '10px', tinyText: '10px',
}, },
radius: { radius: {
card: '5px', card: '5px',
button: '5px', button: '5px',
input: '5px', input: '5px',
}, },
}; };

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,65 +1,65 @@
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}
variables={variables} variables={variables}
setVariables={setVariables} setVariables={setVariables}
inputType={TextInputType.ACCESS_TOKEN_EXPIRY_TIME} inputType={TextInputType.ACCESS_TOKEN_EXPIRY_TIME}
placeholder="0h15m0s" placeholder="0h15m0s"
/> />
</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"
> >
<Text fontSize="sm">Custom Scripts:</Text> <Text fontSize="sm">Custom Scripts:</Text>
<Text fontSize="xs" color="blackAlpha.500"> <Text fontSize="xs" color="blackAlpha.500">
(Used to add custom fields in ID token) (Used to add custom fields in ID token)
</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}
setVariables={setVariables} setVariables={setVariables}
inputType={TextAreaInputType.CUSTOM_ACCESS_TOKEN_SCRIPT} inputType={TextAreaInputType.CUSTOM_ACCESS_TOKEN_SCRIPT}
placeholder="Add script here" placeholder="Add script here"
minH="25vh" minH="25vh"
/> />
</Flex> </Flex>
</Flex> </Flex>
</Stack> </Stack>
</div> </div>
); );
}; };
export default AccessToken; export default AccessToken;

View File

@ -1,35 +1,35 @@
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}
setVariables={setVariables} setVariables={setVariables}
inputType={ArrayInputType.ALLOWED_ORIGINS} inputType={ArrayInputType.ALLOWED_ORIGINS}
/> />
</Center> </Center>
</Flex> </Flex>
</Stack> </Stack>
</div> </div>
); );
}; };
export default DomainWhiteListing; export default DomainWhiteListing;

View File

@ -1,150 +1,114 @@
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}
variables={variables} variables={variables}
setVariables={setVariables} setVariables={setVariables}
inputType={TextInputType.SMTP_HOST} inputType={TextInputType.SMTP_HOST}
/> />
</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}
variables={variables} variables={variables}
setVariables={setVariables} setVariables={setVariables}
inputType={TextInputType.SMTP_PORT} inputType={TextInputType.SMTP_PORT}
/> />
</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 Local Name:</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}
variables={variables} variables={variables}
setVariables={setVariables} setVariables={setVariables}
inputType={TextInputType.SMTP_LOCAL_NAME} inputType={TextInputType.SMTP_USERNAME}
/> />
</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 Username:</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}
variables={variables} variables={variables}
setVariables={setVariables} setVariables={setVariables}
inputType={TextInputType.SMTP_USERNAME} fieldVisibility={fieldVisibility}
/> setFieldVisibility={setFieldVisibility}
</Center> inputType={HiddenInputType.SMTP_PASSWORD}
</Flex> />
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}> </Center>
<Flex </Flex>
w={isNotSmallerScreen ? '30%' : '40%'} <Flex direction={isNotSmallerScreen ? "row" : "column"}>
justifyContent="start" <Flex w="30%" justifyContent="start" alignItems="center">
alignItems="center" <Text fontSize="sm">From Email:</Text>
> </Flex>
<Text fontSize="sm">SMTP Password:</Text> <Center
</Flex> w={isNotSmallerScreen ? "70%" : "100%"}
<Center mt={isNotSmallerScreen ? "0" : "3"}
w={isNotSmallerScreen ? '70%' : '100%'} >
mt={isNotSmallerScreen ? '0' : '3'} <InputField
> borderRadius={5}
<InputField variables={variables}
borderRadius={5} setVariables={setVariables}
variables={variables} inputType={TextInputType.SENDER_EMAIL}
setVariables={setVariables} />
fieldVisibility={fieldVisibility} </Center>
setFieldVisibility={setFieldVisibility} </Flex>
inputType={HiddenInputType.SMTP_PASSWORD} </Stack>
/> </div>
</Center> );
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">From Email:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.SENDER_EMAIL}
/>
</Center>
</Flex>
<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>
</div>
);
}; };
export default EmailConfigurations; export default EmailConfigurations;

View File

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { Divider, Flex, Stack, Text } from '@chakra-ui/react'; import { Flex, Stack, Text } from '@chakra-ui/react';
import InputField from '../InputField'; import InputField from '../InputField';
import { SwitchInputType } from '../../constants'; import { SwitchInputType } from '../../constants';
@ -8,163 +8,101 @@ 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} padding="2% 0%">
<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>
<Flex w="100%" alignItems="baseline" flexDir="column"> <Flex w="100%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Multi Factor Authentication (MFA):</Text> <Text fontSize="sm">Disable Multi Factor Authentication:</Text>
<Text fontSize="x-small">
Note: Enabling this will ignore Enforcing MFA shown below and will
also ignore the user MFA setting.
</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"> <Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
<Flex w="100%" alignItems="baseline" flexDir="column"> Enable Features
<Text fontSize="sm">Time Based OTP (TOTP):</Text> </Text>
<Text fontSize="x-small">Note: to enable totp mfa</Text> <Stack spacing={6} padding="2% 0%">
</Flex> <Flex>
<Flex w="100%" justifyContent="start" alignItems="center">
<Flex justifyContent="start" mb={3}> <Text fontSize="sm">Enforce Multi Factor Authentication:</Text>
<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 w="100%" alignItems="baseline" flexDir="column">
<Text fontSize="sm">
Enforce Multi Factor Authentication (MFA):
</Text>
<Text fontSize="x-small">
Note: If you disable enforcing after it was enabled, it will still
keep MFA enabled for older users.
</Text>
</Flex> </Flex>
<Flex justifyContent="start" mb={3}> <Flex justifyContent="start" mb={3}>
<InputField <InputField
@ -174,53 +112,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

@ -1,200 +1,154 @@
import React from 'react'; import React from "react";
import { Flex, Stack, Center, Text, useMediaQuery } from "@chakra-ui/react";
import { import {
Flex, HiddenInputType,
Stack, TextInputType,
Center, TextAreaInputType,
Text, } from "../../constants";
useMediaQuery, import GenerateKeysModal from "../GenerateKeysModal";
Button, import InputField from "../InputField";
useToast,
} from '@chakra-ui/react';
import {
HiddenInputType,
TextInputType,
TextAreaInputType,
} from '../../constants';
import GenerateKeysModal from '../GenerateKeysModal';
import InputField from '../InputField';
import { copyTextToClipboard } from '../../utils';
const JSTConfigurations = ({ const JSTConfigurations = ({
variables, variables,
setVariables, setVariables,
fieldVisibility, fieldVisibility,
setFieldVisibility, setFieldVisibility,
SelectInputType, SelectInputType,
getData, getData,
HMACEncryptionType, HMACEncryptionType,
RSAEncryptionType, RSAEncryptionType,
ECDSAEncryptionType, ECDSAEncryptionType,
}: any) => { }: any) => {
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)'); const [isNotSmallerScreen] = useMediaQuery("(min-width:600px)");
const toast = useToast();
const copyJSON = async () => { return (
try { <div>
await copyTextToClipboard( {" "}
JSON.stringify({ <Flex
type: variables.JWT_TYPE, borderRadius={5}
key: variables.JWT_PUBLIC_KEY || variables.JWT_SECRET, width="100%"
}), justifyContent="space-between"
); alignItems="center"
toast({ paddingTop="2%"
title: `JWT config copied successfully`, >
isClosable: true, <Text
status: 'success', fontSize={isNotSmallerScreen ? "md" : "sm"}
position: 'top-right', fontWeight="bold"
}); mb={5}
} catch (err) { >
console.error({ JWT (JSON Web Tokens) Configurations
message: `Failed to copy JWT config`, </Text>
error: err, <Flex mb={7}>
}); <GenerateKeysModal jwtType={variables.JWT_TYPE} getData={getData} />
toast({ </Flex>
title: `Failed to copy JWT config`, </Flex>
isClosable: true, <Stack spacing={6} padding="2% 0%">
status: 'error', <Flex direction={isNotSmallerScreen ? "row" : "column"}>
position: 'top-right', <Flex w="30%" justifyContent="start" alignItems="center">
}); <Text fontSize="sm">JWT Type:</Text>
} </Flex>
}; <Flex
w={isNotSmallerScreen ? "70%" : "100%"}
return ( mt={isNotSmallerScreen ? "0" : "2"}
<div> >
<Flex <InputField
borderRadius={5} borderRadius={5}
width="100%" variables={variables}
justifyContent="space-between" setVariables={setVariables}
alignItems="center" inputType={SelectInputType}
paddingTop="2%" value={SelectInputType}
> options={{
<Text ...HMACEncryptionType,
fontSize={isNotSmallerScreen ? 'md' : 'sm'} ...RSAEncryptionType,
fontWeight="bold" ...ECDSAEncryptionType,
mb={5} }}
> />
JWT (JSON Web Tokens) Configurations </Flex>
</Text> </Flex>
<Flex mb={7}> {Object.values(HMACEncryptionType).includes(variables.JWT_TYPE) ? (
<Button <Flex direction={isNotSmallerScreen ? "row" : "column"}>
colorScheme="blue" <Flex w="30%" justifyContent="start" alignItems="center">
h="1.75rem" <Text fontSize="sm">JWT Secret</Text>
size="sm" </Flex>
variant="ghost" <Center
onClick={copyJSON} w={isNotSmallerScreen ? "70%" : "100%"}
> mt={isNotSmallerScreen ? "0" : "2"}
Copy As JSON Config >
</Button> <InputField
<GenerateKeysModal jwtType={variables.JWT_TYPE} getData={getData} /> borderRadius={5}
</Flex> variables={variables}
</Flex> setVariables={setVariables}
<Stack spacing={6} padding="2% 0%"> fieldVisibility={fieldVisibility}
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}> setFieldVisibility={setFieldVisibility}
<Flex w="30%" justifyContent="start" alignItems="center"> inputType={HiddenInputType.JWT_SECRET}
<Text fontSize="sm">JWT Type:</Text> />
</Flex> </Center>
<Flex </Flex>
w={isNotSmallerScreen ? '70%' : '100%'} ) : (
mt={isNotSmallerScreen ? '0' : '2'} <>
> <Flex direction={isNotSmallerScreen ? "row" : "column"}>
<InputField <Flex w="30%" justifyContent="start" alignItems="center">
borderRadius={5} <Text fontSize="sm">Public Key</Text>
variables={variables} </Flex>
setVariables={setVariables} <Center
inputType={SelectInputType} w={isNotSmallerScreen ? "70%" : "100%"}
value={SelectInputType} mt={isNotSmallerScreen ? "0" : "2"}
options={{ >
...HMACEncryptionType, <InputField
...RSAEncryptionType, borderRadius={5}
...ECDSAEncryptionType, variables={variables}
}} setVariables={setVariables}
/> inputType={TextAreaInputType.JWT_PUBLIC_KEY}
</Flex> placeholder="Add public key here"
</Flex> minH="25vh"
{Object.values(HMACEncryptionType).includes(variables.JWT_TYPE) ? ( />
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}> </Center>
<Flex w="30%" justifyContent="start" alignItems="center"> </Flex>
<Text fontSize="sm">JWT Secret</Text> <Flex direction={isNotSmallerScreen ? "row" : "column"}>
</Flex> <Flex w="30%" justifyContent="start" alignItems="center">
<Center <Text fontSize="sm">Private Key</Text>
w={isNotSmallerScreen ? '70%' : '100%'} </Flex>
mt={isNotSmallerScreen ? '0' : '2'} <Center
> w={isNotSmallerScreen ? "70%" : "100%"}
<InputField mt={isNotSmallerScreen ? "0" : "2"}
borderRadius={5} >
variables={variables} <InputField
setVariables={setVariables} borderRadius={5}
fieldVisibility={fieldVisibility} variables={variables}
setFieldVisibility={setFieldVisibility} setVariables={setVariables}
inputType={HiddenInputType.JWT_SECRET} inputType={TextAreaInputType.JWT_PRIVATE_KEY}
/> placeholder="Add private key here"
</Center> minH="25vh"
</Flex> />
) : ( </Center>
<> </Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}> </>
<Flex w="30%" justifyContent="start" alignItems="center"> )}
<Text fontSize="sm">Public Key</Text> <Flex direction={isNotSmallerScreen ? "row" : "column"}>
</Flex> <Flex
<Center w={isNotSmallerScreen ? "30%" : "40%"}
w={isNotSmallerScreen ? '70%' : '100%'} justifyContent="start"
mt={isNotSmallerScreen ? '0' : '2'} alignItems="center"
> >
<InputField <Text fontSize="sm" orientation="vertical">
borderRadius={5} JWT Role Claim:
variables={variables} </Text>
setVariables={setVariables} </Flex>
inputType={TextAreaInputType.JWT_PUBLIC_KEY} <Center
placeholder="Add public key here" w={isNotSmallerScreen ? "70%" : "100%"}
minH="25vh" mt={isNotSmallerScreen ? "0" : "2"}
/> >
</Center> <InputField
</Flex> borderRadius={5}
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}> variables={variables}
<Flex w="30%" justifyContent="start" alignItems="center"> setVariables={setVariables}
<Text fontSize="sm">Private Key</Text> inputType={TextInputType.JWT_ROLE_CLAIM}
</Flex> />
<Center </Center>
w={isNotSmallerScreen ? '70%' : '100%'} </Flex>
mt={isNotSmallerScreen ? '0' : '2'} </Stack>
> </div>
<InputField );
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextAreaInputType.JWT_PRIVATE_KEY}
placeholder="Add private key here"
minH="25vh"
/>
</Center>
</Flex>
</>
)}
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex
w={isNotSmallerScreen ? '30%' : '40%'}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm" orientation="vertical">
JWT Role Claim:
</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '2'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.JWT_ROLE_CLAIM}
/>
</Center>
</Flex>
</Stack>
</div>
);
}; };
export default JSTConfigurations; export default JSTConfigurations;

View File

@ -15,18 +15,8 @@ import {
FaFacebookF, FaFacebookF,
FaLinkedin, FaLinkedin,
FaApple, FaApple,
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 +68,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,212 +264,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'}>
<Center
w={isNotSmallerScreen ? '55px' : '35px'}
h="35px"
marginRight="1.5%"
border="1px solid #3b5998"
borderRadius="5px"
>
<FaTwitter />
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.TWITTER_CLIENT_ID}
placeholder="Twitter 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.TWITTER_CLIENT_SECRET}
placeholder="Twitter 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"
>
<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,60 +1,60 @@
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}
variables={variables} variables={variables}
setVariables={setVariables} setVariables={setVariables}
inputType={TextInputType.ORGANIZATION_NAME} inputType={TextInputType.ORGANIZATION_NAME}
/> />
</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}
variables={variables} variables={variables}
setVariables={setVariables} setVariables={setVariables}
inputType={TextInputType.ORGANIZATION_LOGO} inputType={TextInputType.ORGANIZATION_LOGO}
/> />
</Center> </Center>
</Flex> </Flex>
</Stack> </Stack>
</div> </div>
); );
}; };
export default OrganizationInfo; export default OrganizationInfo;

View File

@ -1,138 +1,138 @@
import React from 'react'; import React from "react";
import { import {
Flex, Flex,
Stack, Stack,
Center, Center,
Text, Text,
Input, Input,
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,
fieldVisibility, fieldVisibility,
setFieldVisibility, setFieldVisibility,
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>
<Stack <Stack
spacing={6} spacing={6}
padding="0 5%" padding="0 5%"
marginTop="3%" marginTop="3%"
border="1px solid #ff7875" border="1px solid #ff7875"
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
borderRadius={5} borderRadius={5}
size="sm" size="sm"
placeholder="Enter Old Admin Secret" placeholder="Enter Old Admin Secret"
value={adminSecret.value as string} value={adminSecret.value as string}
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
right="5px" right="5px"
children={ children={
<Flex> <Flex>
{fieldVisibility[HiddenInputType.OLD_ADMIN_SECRET] ? ( {fieldVisibility[HiddenInputType.OLD_ADMIN_SECRET] ? (
<Center <Center
w="25px" w="25px"
margin="0 1.5%" margin="0 1.5%"
cursor="pointer" cursor="pointer"
onClick={() => onClick={() =>
setFieldVisibility({ setFieldVisibility({
...fieldVisibility, ...fieldVisibility,
[HiddenInputType.OLD_ADMIN_SECRET]: false, [HiddenInputType.OLD_ADMIN_SECRET]: false,
}) })
} }
> >
<FaRegEyeSlash color="#bfbfbf" /> <FaRegEyeSlash color="#bfbfbf" />
</Center> </Center>
) : ( ) : (
<Center <Center
w="25px" w="25px"
margin="0 1.5%" margin="0 1.5%"
cursor="pointer" cursor="pointer"
onClick={() => onClick={() =>
setFieldVisibility({ setFieldVisibility({
...fieldVisibility, ...fieldVisibility,
[HiddenInputType.OLD_ADMIN_SECRET]: true, [HiddenInputType.OLD_ADMIN_SECRET]: true,
}) })
} }
> >
<FaRegEye color="#bfbfbf" /> <FaRegEye color="#bfbfbf" />
</Center> </Center>
)} )}
</Flex> </Flex>
} }
/> />
</InputGroup> </InputGroup>
</Center> </Center>
</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}
mb={3} mb={3}
variables={variables} variables={variables}
setVariables={setVariables} setVariables={setVariables}
inputType={HiddenInputType.ADMIN_SECRET} inputType={HiddenInputType.ADMIN_SECRET}
fieldVisibility={fieldVisibility} fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility} setFieldVisibility={setFieldVisibility}
isDisabled={adminSecret.disableInputField} isDisabled={adminSecret.disableInputField}
placeholder="Enter New Admin Secret" placeholder="Enter New Admin Secret"
/> />
</Center> </Center>
</Flex> </Flex>
</Stack> </Stack>
</div> </div>
); );
}; };
export default SecurityAdminSecret; export default SecurityAdminSecret;

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,85 +136,70 @@ const UpdateEmailTemplate = ({
); );
}; };
const updateTemplate = async (params: emailTemplateDataType) => {
let res: any = {};
if (
view === UpdateModalViews.Edit &&
selectedTemplate?.[EmailTemplateInputDataFields.ID]
) {
res = await client
.mutation(EditEmailTemplate, {
params: {
...params,
id: selectedTemplate[EmailTemplateInputDataFields.ID],
},
})
.toPromise();
} else {
res = await client.mutation(AddEmailTemplate, { params }).toPromise();
}
setLoading(false);
if (res.error) {
toast({
title: capitalizeFirstLetter(res.error.message),
isClosable: true,
status: 'error',
position: 'top-right',
});
} else if (
res.data?._add_email_template ||
res.data?._update_email_template
) {
toast({
title: capitalizeFirstLetter(
res.data?._add_email_template?.message ||
res.data?._update_email_template?.message,
),
isClosable: true,
status: 'success',
position: 'top-right',
});
setTemplateData({
...initTemplateData,
});
setValidator({ ...initTemplateValidatorData });
fetchEmailTemplatesData();
}
};
const saveData = async () => { const saveData = async () => {
if (!validateData()) return; if (!validateData()) return;
setLoading(true); setLoading(true);
let params: emailTemplateDataType = { // @ts-ignore
[EmailTemplateInputDataFields.EVENT_NAME]: return await emailEditorRef.current.editor.exportHtml(async (data) => {
templateData[EmailTemplateInputDataFields.EVENT_NAME], const { design, html } = data;
[EmailTemplateInputDataFields.SUBJECT]: if (!html || !design) {
templateData[EmailTemplateInputDataFields.SUBJECT], setLoading(false);
[EmailTemplateInputDataFields.TEMPLATE]: return;
templateData[EmailTemplateInputDataFields.TEMPLATE], }
[EmailTemplateInputDataFields.DESIGN]: '', const params = {
}; [EmailTemplateInputDataFields.EVENT_NAME]:
if (editor === EmailTemplateEditors.UNLAYER_EDITOR) { templateData[EmailTemplateInputDataFields.EVENT_NAME],
// @ts-ignore [EmailTemplateInputDataFields.SUBJECT]:
await emailEditorRef.current.editor.exportHtml(async (data) => { templateData[EmailTemplateInputDataFields.SUBJECT],
const { design, html } = data; [EmailTemplateInputDataFields.TEMPLATE]: html.trim(),
if (!html || !design) { [EmailTemplateInputDataFields.DESIGN]: JSON.stringify(design),
setLoading(false); };
return; let res: any = {};
} if (
params = { view === UpdateModalViews.Edit &&
...params, selectedTemplate?.[EmailTemplateInputDataFields.ID]
[EmailTemplateInputDataFields.TEMPLATE]: html.trim(), ) {
[EmailTemplateInputDataFields.DESIGN]: JSON.stringify(design), res = await client
}; .mutation(EditEmailTemplate, {
await updateTemplate(params); params: {
}); ...params,
} else { id: selectedTemplate[EmailTemplateInputDataFields.ID],
await updateTemplate(params); },
} })
view === UpdateModalViews.ADD && onClose(); .toPromise();
} else {
res = await client.mutation(AddEmailTemplate, { params }).toPromise();
}
setLoading(false);
if (res.error) {
toast({
title: capitalizeFirstLetter(res.error.message),
isClosable: true,
status: 'error',
position: 'bottom-right',
});
} else if (
res.data?._add_email_template ||
res.data?._update_email_template
) {
toast({
title: capitalizeFirstLetter(
res.data?._add_email_template?.message ||
res.data?._update_email_template?.message
),
isClosable: true,
status: 'success',
position: 'bottom-right',
});
setTemplateData({
...initTemplateData,
});
setValidator({ ...initTemplateValidatorData });
fetchEmailTemplatesData();
}
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 ? (
@ -411,7 +333,7 @@ const UpdateEmailTemplate = ({
{templateVariables.map((i) => ( {templateVariables.map((i) => (
<Tr key={i.text}> <Tr key={i.text}>
<Td> <Td>
<Code fontSize="sm">{`{{.${i.text}}}`}</Code> <Code fontSize="sm">{`{{${i.text}}}`}</Code>
</Td> </Td>
<Td> <Td>
<Text <Text
@ -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,12 @@ 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',
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 +35,6 @@ 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',
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 +46,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 +61,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 +110,6 @@ 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_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 +123,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 +142,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 +160,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 +194,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 +324,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

@ -18,71 +18,50 @@ export const AdminSessionQuery = `
export const EnvVariablesQuery = ` export const EnvVariablesQuery = `
query { query {
_env{ _env{
CLIENT_ID CLIENT_ID,
CLIENT_SECRET CLIENT_SECRET,
GOOGLE_CLIENT_ID GOOGLE_CLIENT_ID,
GOOGLE_CLIENT_SECRET GOOGLE_CLIENT_SECRET,
GITHUB_CLIENT_ID GITHUB_CLIENT_ID,
GITHUB_CLIENT_SECRET GITHUB_CLIENT_SECRET,
FACEBOOK_CLIENT_ID FACEBOOK_CLIENT_ID,
FACEBOOK_CLIENT_SECRET FACEBOOK_CLIENT_SECRET,
LINKEDIN_CLIENT_ID LINKEDIN_CLIENT_ID,
LINKEDIN_CLIENT_SECRET LINKEDIN_CLIENT_SECRET,
APPLE_CLIENT_ID APPLE_CLIENT_ID,
APPLE_CLIENT_SECRET APPLE_CLIENT_SECRET,
DISCORD_CLIENT_ID DEFAULT_ROLES,
DISCORD_CLIENT_SECRET PROTECTED_ROLES,
TWITTER_CLIENT_ID ROLES,
TWITTER_CLIENT_SECRET JWT_TYPE,
MICROSOFT_CLIENT_ID JWT_SECRET,
MICROSOFT_CLIENT_SECRET JWT_ROLE_CLAIM,
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID JWT_PRIVATE_KEY,
TWITCH_CLIENT_ID JWT_PUBLIC_KEY,
TWITCH_CLIENT_SECRET REDIS_URL,
ROBLOX_CLIENT_ID SMTP_HOST,
ROBLOX_CLIENT_SECRET SMTP_PORT,
DEFAULT_ROLES SMTP_USERNAME,
PROTECTED_ROLES SMTP_PASSWORD,
ROLES SENDER_EMAIL,
JWT_TYPE ALLOWED_ORIGINS,
JWT_SECRET ORGANIZATION_NAME,
JWT_ROLE_CLAIM ORGANIZATION_LOGO,
JWT_PRIVATE_KEY ADMIN_SECRET,
JWT_PUBLIC_KEY DISABLE_LOGIN_PAGE,
REDIS_URL DISABLE_MAGIC_LINK_LOGIN,
SMTP_HOST DISABLE_EMAIL_VERIFICATION,
SMTP_PORT DISABLE_BASIC_AUTHENTICATION,
SMTP_USERNAME DISABLE_SIGN_UP,
SMTP_PASSWORD DISABLE_STRONG_PASSWORD,
SMTP_LOCAL_NAME DISABLE_REDIS_FOR_ENV,
SENDER_EMAIL CUSTOM_ACCESS_TOKEN_SCRIPT,
SENDER_NAME DATABASE_NAME,
ALLOWED_ORIGINS DATABASE_TYPE,
ORGANIZATION_NAME DATABASE_URL,
ORGANIZATION_LOGO ACCESS_TOKEN_EXPIRY_TIME,
ADMIN_SECRET DISABLE_MULTI_FACTOR_AUTHENTICATION,
APP_COOKIE_SECURE ENFORCE_MULTI_FACTOR_AUTHENTICATION,
ADMIN_COOKIE_SECURE
DISABLE_LOGIN_PAGE
DISABLE_MAGIC_LINK_LOGIN
DISABLE_EMAIL_VERIFICATION
DISABLE_BASIC_AUTHENTICATION
DISABLE_MOBILE_BASIC_AUTHENTICATION
DISABLE_SIGN_UP
DISABLE_STRONG_PASSWORD
DISABLE_REDIS_FOR_ENV
CUSTOM_ACCESS_TOKEN_SCRIPT
DATABASE_NAME
DATABASE_TYPE
DATABASE_URL
ACCESS_TOKEN_EXPIRY_TIME
DISABLE_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 +79,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 +110,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 +165,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,6 @@ 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_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 +63,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 +82,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 +92,6 @@ 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,
TWITCH_CLIENT_SECRET: false,
JWT_SECRET: false, JWT_SECRET: false,
SMTP_PASSWORD: false, SMTP_PASSWORD: false,
ADMIN_SECRET: false, ADMIN_SECRET: false,
@ -176,7 +152,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 +197,7 @@ const Environment = () => {
} variables`, } variables`,
isClosable: true, isClosable: true,
status: 'success', status: 'success',
position: 'top-right', position: 'bottom-right',
}); });
}; };

View File

@ -29,7 +29,6 @@ import {
MenuItem, MenuItem,
useToast, useToast,
Spinner, Spinner,
TableContainer,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { import {
FaAngleLeft, FaAngleLeft,
@ -165,25 +164,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 = {};
if (email) {
params = {
id,
email,
email_verified: true,
};
}
if (phone_number) {
params = {
id,
phone_number,
phone_number_verified: true,
};
}
const res = await client const res = await client
.mutation(UpdateUser, { .mutation(UpdateUser, {
params, params: {
id,
email,
email_verified: true,
},
}) })
.toPromise(); .toPromise();
if (res.error) { if (res.error) {
@ -191,14 +179,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 +194,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 +210,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 +235,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();
@ -279,7 +267,7 @@ export default function Users() {
} 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',
}); });
}; };
@ -305,278 +293,256 @@ export default function Users() {
</Flex> </Flex>
{!loading ? ( {!loading ? (
userList.length > 0 ? ( userList.length > 0 ? (
<TableContainer> <Table variant="simple">
<Table variant="simple"> <Thead>
<Thead> <Tr>
<Tr> <Th>Email</Th>
<Th>Email / Phone</Th> <Th>Created At</Th>
<Th>Created At</Th> <Th>Signup Methods</Th>
<Th>Signup Methods</Th> <Th>Roles</Th>
<Th>Roles</Th> <Th>Verified</Th>
<Th>Verified</Th> <Th>Access</Th>
<Th>Access</Th> <Th>MFA</Th>
<Th> <Th>Actions</Th>
<Tooltip label="MultiFactor Authentication Enabled / Disabled"> </Tr>
MFA </Thead>
</Tooltip> <Tbody>
</Th> {userList.map((user: userDataTypes) => {
<Th>Actions</Th> const { email_verified, created_at, ...rest }: any = user;
</Tr> return (
</Thead> <Tr key={user.id} style={{ fontSize: 14 }}>
<Tbody> <Td maxW="300">{user.email}</Td>
{userList.map((user: userDataTypes) => { <Td>
const { {dayjs(user.created_at * 1000).format('MMM DD, YYYY')}
email_verified, </Td>
phone_number_verified, <Td>{user.signup_methods}</Td>
created_at, <Td>{user.roles.join(', ')}</Td>
...rest <Td>
}: any = user; <Tag
return ( size="sm"
<Tr key={user.id} style={{ fontSize: 14 }}> variant="outline"
<Td maxW="300">{user.email || user.phone_number}</Td> colorScheme={user.email_verified ? 'green' : 'yellow'}
<Td> >
{dayjs(user.created_at * 1000).format('MMM DD, YYYY')} {user.email_verified.toString()}
</Td> </Tag>
<Td>{user.signup_methods}</Td> </Td>
<Td>{user.roles.join(', ')}</Td> <Td>
<Td> <Tag
<Tag size="sm"
size="sm" variant="outline"
variant="outline" colorScheme={user.revoked_timestamp ? 'red' : 'green'}
colorScheme={ >
user.email_verified || user.phone_number_verified {user.revoked_timestamp ? 'Revoked' : 'Enabled'}
? 'green' </Tag>
: 'yellow' </Td>
} <Td>
> <Tag
{( size="sm"
user.email_verified || user.phone_number_verified variant="outline"
).toString()} colorScheme={
</Tag> user.is_multi_factor_auth_enabled ? 'green' : 'red'
</Td>
<Td>
<Tag
size="sm"
variant="outline"
colorScheme={user.revoked_timestamp ? 'red' : 'green'}
>
{user.revoked_timestamp ? 'Revoked' : 'Enabled'}
</Tag>
</Td>
<Td>
<Tag
size="sm"
variant="outline"
colorScheme={
user.is_multi_factor_auth_enabled ? 'green' : 'red'
}
>
{user.is_multi_factor_auth_enabled
? 'Enabled'
: 'Disabled'}
</Tag>
</Td>
<Td>
<Menu>
<MenuButton as={Button} variant="unstyled" size="sm">
<Flex
justifyContent="space-between"
alignItems="center"
>
<Text fontSize="sm" fontWeight="light">
Menu
</Text>
<FaAngleDown style={{ marginLeft: 10 }} />
</Flex>
</MenuButton>
<MenuList>
{!user.email_verified &&
!user.phone_number_verified && (
<MenuItem
onClick={() => userVerificationHandler(user)}
>
Verify User
</MenuItem>
)}
<EditUserModal
user={rest}
updateUserList={updateUserList}
/>
<DeleteUserModal
user={rest}
updateUserList={updateUserList}
/>
{user.revoked_timestamp ? (
<MenuItem
onClick={() =>
updateAccessHandler(
user.id,
updateAccessActions.ENABLE,
)
}
>
Enable Access
</MenuItem>
) : (
<MenuItem
onClick={() =>
updateAccessHandler(
user.id,
updateAccessActions.REVOKE,
)
}
>
Revoke Access
</MenuItem>
)}
{user.is_multi_factor_auth_enabled ? (
<MenuItem
onClick={() =>
multiFactorAuthUpdateHandler(user)
}
>
Disable MultiFactor Authentication
</MenuItem>
) : (
<MenuItem
onClick={() =>
multiFactorAuthUpdateHandler(user)
}
>
Enable MultiFactor Authentication
</MenuItem>
)}
</MenuList>
</Menu>
</Td>
</Tr>
);
})}
</Tbody>
{(paginationProps.maxPages > 1 || paginationProps.total >= 5) && (
<TableCaption>
<Flex
justifyContent="space-between"
alignItems="center"
m="2% 0"
>
<Flex flex="1">
<Tooltip label="First Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: 1,
})
}
isDisabled={paginationProps.page <= 1}
mr={4}
icon={<FaAngleDoubleLeft />}
/>
</Tooltip>
<Tooltip label="Previous Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.page - 1,
})
}
isDisabled={paginationProps.page <= 1}
icon={<FaAngleLeft />}
/>
</Tooltip>
</Flex>
<Flex
flex="8"
justifyContent="space-evenly"
alignItems="center"
>
<Text mr={8}>
Page{' '}
<Text fontWeight="bold" as="span">
{paginationProps.page}
</Text>{' '}
of{' '}
<Text fontWeight="bold" as="span">
{paginationProps.maxPages}
</Text>
</Text>
<Flex alignItems="center">
<Text flexShrink="0">Go to page:</Text>{' '}
<NumberInput
ml={2}
mr={8}
w={28}
min={1}
max={paginationProps.maxPages}
onChange={(value) =>
paginationHandler({
page: parseInt(value),
})
}
value={paginationProps.page}
>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
</Flex>
<Select
w={32}
value={paginationProps.limit}
onChange={(e) =>
paginationHandler({
page: 1,
limit: parseInt(e.target.value),
})
} }
> >
{getLimits(paginationProps).map((pageSize) => ( {user.is_multi_factor_auth_enabled
<option key={pageSize} value={pageSize}> ? 'Enabled'
Show {pageSize} : 'Disabled'}
</option> </Tag>
))} </Td>
</Select> <Td>
</Flex> <Menu>
<Flex flex="1"> <MenuButton as={Button} variant="unstyled" size="sm">
<Tooltip label="Next Page"> <Flex
<IconButton justifyContent="space-between"
aria-label="icon button" alignItems="center"
onClick={() => >
paginationHandler({ <Text fontSize="sm" fontWeight="light">
page: paginationProps.page + 1, Menu
}) </Text>
} <FaAngleDown style={{ marginLeft: 10 }} />
isDisabled={ </Flex>
paginationProps.page >= paginationProps.maxPages </MenuButton>
} <MenuList>
icon={<FaAngleRight />} {!user.email_verified && (
/> <MenuItem
</Tooltip> onClick={() => userVerificationHandler(user)}
<Tooltip label="Last Page"> >
<IconButton Verify User
aria-label="icon button" </MenuItem>
onClick={() => )}
paginationHandler({ <EditUserModal
page: paginationProps.maxPages, user={rest}
}) updateUserList={updateUserList}
} />
isDisabled={ <DeleteUserModal
paginationProps.page >= paginationProps.maxPages user={rest}
} updateUserList={updateUserList}
ml={4} />
icon={<FaAngleDoubleRight />} {user.revoked_timestamp ? (
/> <MenuItem
</Tooltip> onClick={() =>
</Flex> updateAccessHandler(
user.id,
updateAccessActions.ENABLE
)
}
>
Enable Access
</MenuItem>
) : (
<MenuItem
onClick={() =>
updateAccessHandler(
user.id,
updateAccessActions.REVOKE
)
}
>
Revoke Access
</MenuItem>
)}
{user.is_multi_factor_auth_enabled ? (
<MenuItem
onClick={() => multiFactorAuthUpdateHandler(user)}
>
Disable MFA
</MenuItem>
) : (
<MenuItem
onClick={() => multiFactorAuthUpdateHandler(user)}
>
Enable MFA
</MenuItem>
)}
</MenuList>
</Menu>
</Td>
</Tr>
);
})}
</Tbody>
{(paginationProps.maxPages > 1 || paginationProps.total >= 5) && (
<TableCaption>
<Flex
justifyContent="space-between"
alignItems="center"
m="2% 0"
>
<Flex flex="1">
<Tooltip label="First Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: 1,
})
}
isDisabled={paginationProps.page <= 1}
mr={4}
icon={<FaAngleDoubleLeft />}
/>
</Tooltip>
<Tooltip label="Previous Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.page - 1,
})
}
isDisabled={paginationProps.page <= 1}
icon={<FaAngleLeft />}
/>
</Tooltip>
</Flex> </Flex>
</TableCaption> <Flex
)} flex="8"
</Table> justifyContent="space-evenly"
</TableContainer> alignItems="center"
>
<Text mr={8}>
Page{' '}
<Text fontWeight="bold" as="span">
{paginationProps.page}
</Text>{' '}
of{' '}
<Text fontWeight="bold" as="span">
{paginationProps.maxPages}
</Text>
</Text>
<Flex alignItems="center">
<Text flexShrink="0">Go to page:</Text>{' '}
<NumberInput
ml={2}
mr={8}
w={28}
min={1}
max={paginationProps.maxPages}
onChange={(value) =>
paginationHandler({
page: parseInt(value),
})
}
value={paginationProps.page}
>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
</Flex>
<Select
w={32}
value={paginationProps.limit}
onChange={(e) =>
paginationHandler({
page: 1,
limit: parseInt(e.target.value),
})
}
>
{getLimits(paginationProps).map((pageSize) => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</Select>
</Flex>
<Flex flex="1">
<Tooltip label="Next Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.page + 1,
})
}
isDisabled={
paginationProps.page >= paginationProps.maxPages
}
icon={<FaAngleRight />}
/>
</Tooltip>
<Tooltip label="Last Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.maxPages,
})
}
isDisabled={
paginationProps.page >= paginationProps.maxPages
}
ml={4}
icon={<FaAngleDoubleRight />}
/>
</Tooltip>
</Flex>
</Flex>
</TableCaption>
)}
</Table>
) : ( ) : (
<Flex <Flex
flexDirection="column" flexDirection="column"

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

@ -29,16 +29,19 @@ const fallbackCopyTextToClipboard = (text: string) => {
document.body.removeChild(textArea); document.body.removeChild(textArea);
}; };
export const copyTextToClipboard = async (text: string) => { export const copyTextToClipboard = (text: string) => {
if (!navigator.clipboard) { if (!navigator.clipboard) {
fallbackCopyTextToClipboard(text); fallbackCopyTextToClipboard(text);
return; return;
} }
try { navigator.clipboard.writeText(text).then(
navigator.clipboard.writeText(text); () => {
} catch (err) { console.log('Async: Copying to clipboard was successful!');
throw err; },
} (err) => {
console.error('Async: Could not copy text: ', err);
}
);
}; };
export const getObjectDiff = (obj1: any, obj2: any) => { export const getObjectDiff = (obj1: any, obj2: any) => {
@ -67,7 +70,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 +81,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,4 @@ 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 = "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,20 +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 = "APP_COOKIE_SECURE"
// EnvKeyAdminCookieSecure key for env variable ADMIN_COOKIE_SECURE
EnvKeyAdminCookieSecure = "ADMIN_COOKIE_SECURE"
// EnvKeyJwtType key for env variable JWT_TYPE // EnvKeyJwtType key for env variable JWT_TYPE
EnvKeyJwtType = "JWT_TYPE" EnvKeyJwtType = "JWT_TYPE"
// EnvKeyJwtSecret key for env variable JWT_SECRET // EnvKeyJwtSecret key for env variable JWT_SECRET
@ -108,28 +85,6 @@ 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 = "TWITTER_CLIENT_ID"
// EnvKeyTwitterClientSecret key for env variable TWITTER_CLIENT_SECRET
EnvKeyTwitterClientSecret = "TWITTER_CLIENT_SECRET"
// EnvKeyMicrosoftClientID key for env variable MICROSOFT_CLIENT_ID
EnvKeyMicrosoftClientID = "MICROSOFT_CLIENT_ID"
// EnvKeyMicrosoftActiveDirectoryTenantID key for env variable MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID
EnvKeyMicrosoftActiveDirectoryTenantID = "MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID"
// EnvKeyMicrosoftClientSecret key for env variable MICROSOFT_CLIENT_SECRET
EnvKeyMicrosoftClientSecret = "MICROSOFT_CLIENT_SECRET"
// 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 +109,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 +125,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 +135,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

@ -9,19 +9,9 @@ const (
// Ref: https://docs.github.com/en/developers/apps/building-github-apps/identifying-and-authorizing-users-for-github-apps#3-your-github-app-accesses-the-api-with-the-users-access-token // Ref: https://docs.github.com/en/developers/apps/building-github-apps/identifying-and-authorizing-users-for-github-apps#3-your-github-app-accesses-the-api-with-the-users-access-token
GithubUserInfoURL = "https://api.github.com/user" GithubUserInfoURL = "https://api.github.com/user"
// Get github user emails when user info email is empty Ref: https://stackoverflow.com/a/35387123 // Get github user emails when user info email is empty Ref: https://stackoverflow.com/a/35387123
GithubUserEmails = "https://api.github.com/user/emails" GithubUserEmails = "https://api/github.com/user/emails"
// Ref: https://docs.microsoft.com/en-us/linkedin/shared/integrations/people/profile-api // Ref: https://docs.microsoft.com/en-us/linkedin/shared/integrations/people/profile-api
LinkedInUserInfoURL = "https://api.linkedin.com/v2/me?projection=(id,localizedFirstName,localizedLastName,emailAddress,profilePicture(displayImage~:playableStreams))" LinkedInUserInfoURL = "https://api.linkedin.com/v2/me?projection=(id,localizedFirstName,localizedLastName,emailAddress,profilePicture(displayImage~:playableStreams))"
LinkedInEmailURL = "https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))" 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"
// 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

@ -12,16 +12,5 @@ const (
// VerificationTypeInviteMember is the invite_member verification type // VerificationTypeInviteMember is the invite_member verification type
VerificationTypeInviteMember = "invite_member" VerificationTypeInviteMember = "invite_member"
// VerificationTypeOTP is the otp verification type // VerificationTypeOTP is the otp verification type
VerificationTypeOTP = "verify_otp" VerificationTypeOTP = "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

@ -3,24 +3,15 @@ package cookie
import ( import (
"net/url" "net/url"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/memorystore"
"github.com/authorizerdev/authorizer/server/parsers" "github.com/authorizerdev/authorizer/server/parsers"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
// SetAdminCookie sets the admin cookie in the response // SetAdminCookie sets the admin cookie in the response
func SetAdminCookie(gc *gin.Context, token string) { func SetAdminCookie(gc *gin.Context, token string) {
adminCookieSecure, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyAdminCookieSecure) secure := true
if err != nil { httpOnly := true
log.Debug("Error while getting admin cookie secure from env variable: %v", err)
adminCookieSecure = true
}
secure := adminCookieSecure
httpOnly := adminCookieSecure
hostname := parsers.GetHost(gc) hostname := parsers.GetHost(gc)
host, _ := parsers.GetHostParts(hostname) host, _ := parsers.GetHostParts(hostname)
gc.SetCookie(constants.AdminCookieName, token, 3600, "/", host, secure, httpOnly) gc.SetCookie(constants.AdminCookieName, token, 3600, "/", host, secure, httpOnly)
@ -44,14 +35,8 @@ func GetAdminCookie(gc *gin.Context) (string, error) {
// DeleteAdminCookie sets the response cookie to empty // DeleteAdminCookie sets the response cookie to empty
func DeleteAdminCookie(gc *gin.Context) { func DeleteAdminCookie(gc *gin.Context) {
adminCookieSecure, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyAdminCookieSecure) secure := true
if err != nil { httpOnly := true
log.Debug("Error while getting admin cookie secure from env variable: %v", err)
adminCookieSecure = true
}
secure := adminCookieSecure
httpOnly := adminCookieSecure
hostname := parsers.GetHost(gc) hostname := parsers.GetHost(gc)
host, _ := parsers.GetHostParts(hostname) host, _ := parsers.GetHostParts(hostname)
gc.SetCookie(constants.AdminCookieName, "", -1, "/", host, secure, httpOnly) gc.SetCookie(constants.AdminCookieName, "", -1, "/", host, secure, httpOnly)

View File

@ -4,24 +4,15 @@ import (
"net/http" "net/http"
"net/url" "net/url"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/memorystore"
"github.com/authorizerdev/authorizer/server/parsers" "github.com/authorizerdev/authorizer/server/parsers"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
// SetSession sets the session cookie in the response // SetSession sets the session cookie in the response
func SetSession(gc *gin.Context, sessionID string) { func SetSession(gc *gin.Context, sessionID string) {
appCookieSecure, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyAppCookieSecure) secure := true
if err != nil { httpOnly := true
log.Debug("Error while getting app cookie secure from env variable: %v", err)
appCookieSecure = true
}
secure := appCookieSecure
httpOnly := appCookieSecure
hostname := parsers.GetHost(gc) hostname := parsers.GetHost(gc)
host, _ := parsers.GetHostParts(hostname) host, _ := parsers.GetHostParts(hostname)
domain := parsers.GetDomainName(hostname) domain := parsers.GetDomainName(hostname)
@ -29,34 +20,18 @@ 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)
} }
// DeleteSession sets session cookies to expire // DeleteSession sets session cookies to expire
func DeleteSession(gc *gin.Context) { func DeleteSession(gc *gin.Context) {
appCookieSecure, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyAppCookieSecure) secure := true
if err != nil { httpOnly := true
log.Debug("Error while getting app cookie secure from env variable: %v", err)
appCookieSecure = true
}
secure := appCookieSecure
httpOnly := appCookieSecure
hostname := parsers.GetHost(gc) hostname := parsers.GetHost(gc)
host, _ := parsers.GetHostParts(hostname) host, _ := parsers.GetHostParts(hostname)
domain := parsers.GetDomainName(hostname) domain := parsers.GetDomainName(hostname)

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,17 +1,15 @@
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
Session string Session string
Env string Env string
Webhook string Webhook string
WebhookLog string WebhookLog string
EmailTemplate string EmailTemplate string
OTP string OTP string
SMSVerificationRequest string
Authenticators string
} }
var ( var (
@ -19,15 +17,13 @@ var (
Prefix = "authorizer_" Prefix = "authorizer_"
// Collections / Tables available for authorizer in the database (used for dbs other than gorm) // Collections / Tables available for authorizer in the database (used for dbs other than gorm)
Collections = CollectionList{ Collections = CollectionList{
User: Prefix + "users", User: Prefix + "users",
VerificationRequest: Prefix + "verification_requests", VerificationRequest: Prefix + "verification_requests",
Session: Prefix + "sessions", Session: Prefix + "sessions",
Env: Prefix + "env", Env: Prefix + "env",
Webhook: Prefix + "webhooks", Webhook: Prefix + "webhooks",
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,42 +10,35 @@ 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), CreatedAt: refs.NewInt64Ref(w.CreatedAt),
CreatedAt: refs.NewInt64Ref(w.CreatedAt), UpdatedAt: refs.NewInt64Ref(w.UpdatedAt),
UpdatedAt: refs.NewInt64Ref(w.UpdatedAt),
} }
} }

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,39 +12,27 @@ 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()
otp = &models.OTP{ otp = &models.OTP{
ID: id, ID: id,
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(),
} }
shouldCreate = true shouldCreate = true
} else { } else {
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,47 +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)
if err != nil {
return nil, err
}
defer cursor.Close()
for {
if !cursor.HasMore() {
if otp == nil {
return nil, fmt.Errorf("otp with given email not found")
}
break
}
_, err := cursor.ReadDocument(ctx, &otp)
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("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) 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 phone_number not found") return nil, fmt.Errorf("email template not found")
} }
break break
} }
@ -112,7 +76,8 @@ func (p *provider) GetOTPByPhoneNumber(ctx context.Context, phoneNumber string)
return nil, err return nil, err
} }
} }
return otp, nil
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 {
return nil, err
}
// Prepare TLS Config
tlsConfig := &tls.Config{}
certPool := x509.NewCertPool()
if success := certPool.AppendCertsFromPEM(caCert); !success {
return nil, fmt.Errorf("invalid certificate")
}
tlsConfig.RootCAs = certPool
httpConfig.TLSConfig = tlsConfig
}
// Create new http connection
conn, err := http.NewConnection(httpConfig)
if err != nil { if err != nil {
return nil, err return nil, err
} }
clientConfig := arangoDriver.ClientConfig{
arangoClient, err := arangoDriver.NewClient(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, template, created_at, updated_at) VALUES ('%s', '%s', '%s','%s', %d, %d)", KeySpace+"."+models.Collections.EmailTemplate, emailTemplate.ID, emailTemplate.EventName, emailTemplate.Subject, 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
@ -94,14 +103,14 @@ func (p *provider) ListEmailTemplate(ctx context.Context, pagination *model.Pagi
// there is no offset in cassandra // there is no offset in cassandra
// so we fetch till limit + offset // so we fetch till limit + offset
// and return the results from offset to limit // and return the results from offset to limit
query := fmt.Sprintf("SELECT id, event_name, subject, design, template, created_at, updated_at FROM %s LIMIT %d", KeySpace+"."+models.Collections.EmailTemplate, pagination.Limit+pagination.Offset) query := fmt.Sprintf("SELECT id, event_name, subject, template, created_at, updated_at FROM %s LIMIT %d", KeySpace+"."+models.Collections.EmailTemplate, pagination.Limit+pagination.Offset)
scanner := p.db.Query(query).Iter().Scanner() scanner := p.db.Query(query).Iter().Scanner()
counter := int64(0) counter := int64(0)
for scanner.Next() { for scanner.Next() {
if counter >= pagination.Offset { if counter >= pagination.Offset {
var emailTemplate models.EmailTemplate var emailTemplate models.EmailTemplate
err := scanner.Scan(&emailTemplate.ID, &emailTemplate.EventName, &emailTemplate.Subject, &emailTemplate.Design, &emailTemplate.Template, &emailTemplate.CreatedAt, &emailTemplate.UpdatedAt) err := scanner.Scan(&emailTemplate.ID, &emailTemplate.EventName, &emailTemplate.Subject, &emailTemplate.Template, &emailTemplate.CreatedAt, &emailTemplate.UpdatedAt)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -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
} }
@ -119,8 +128,8 @@ func (p *provider) ListEmailTemplate(ctx context.Context, pagination *model.Pagi
// 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(`SELECT id, event_name, subject, design, template, created_at, updated_at FROM %s WHERE id = '%s' LIMIT 1`, KeySpace+"."+models.Collections.EmailTemplate, emailTemplateID) query := fmt.Sprintf(`SELECT id, event_name, subject, template, created_at, updated_at FROM %s WHERE id = '%s' LIMIT 1`, KeySpace+"."+models.Collections.EmailTemplate, emailTemplateID)
err := p.db.Query(query).Consistency(gocql.One).Scan(&emailTemplate.ID, &emailTemplate.EventName, &emailTemplate.Subject, &emailTemplate.Design, &emailTemplate.Template, &emailTemplate.CreatedAt, &emailTemplate.UpdatedAt) err := p.db.Query(query).Consistency(gocql.One).Scan(&emailTemplate.ID, &emailTemplate.EventName, &emailTemplate.Subject, &emailTemplate.Template, &emailTemplate.CreatedAt, &emailTemplate.UpdatedAt)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -130,8 +139,8 @@ 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(`SELECT id, event_name, subject, design, template, created_at, updated_at FROM %s WHERE event_name = '%s' LIMIT 1 ALLOW FILTERING`, KeySpace+"."+models.Collections.EmailTemplate, eventName) query := fmt.Sprintf(`SELECT id, event_name, subject, template, created_at, updated_at FROM %s WHERE event_name = '%s' LIMIT 1 ALLOW FILTERING`, KeySpace+"."+models.Collections.EmailTemplate, eventName)
err := p.db.Query(query).Consistency(gocql.One).Scan(&emailTemplate.ID, &emailTemplate.EventName, &emailTemplate.Subject, &emailTemplate.Design, &emailTemplate.Template, &emailTemplate.CreatedAt, &emailTemplate.UpdatedAt) err := p.db.Query(query).Consistency(gocql.One).Scan(&emailTemplate.ID, &emailTemplate.EventName, &emailTemplate.Subject, &emailTemplate.Template, &emailTemplate.CreatedAt, &emailTemplate.UpdatedAt)
if err != nil { if err != nil {
return nil, err return nil, err
} }

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
} }

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