Compare commits
51 Commits
0.1.0-alph
...
0.1.0-beta
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a50f8eba57 | ||
![]() |
814dc61414 | ||
![]() |
7abad9db01 | ||
![]() |
17777b111d | ||
![]() |
c7494a0bca | ||
![]() |
e23d6f92e5 | ||
![]() |
585091fefb | ||
![]() |
eabd88718d | ||
![]() |
072cd46809 | ||
![]() |
17676fa13b | ||
![]() |
8b510ed556 | ||
![]() |
3ac0b44446 | ||
![]() |
b1dd6f2c3b | ||
![]() |
f5ea94f63c | ||
![]() |
653befc737 | ||
![]() |
6fed439ec2 | ||
![]() |
5bd6fa5bc9 | ||
![]() |
75709e9f48 | ||
![]() |
b1b7f47f4c | ||
![]() |
e429a1f860 | ||
![]() |
6819597a79 | ||
![]() |
b34b385be5 | ||
![]() |
5acf59d16e | ||
![]() |
12d795b4e8 | ||
![]() |
f67bb3a9fc | ||
![]() |
173a55137f | ||
![]() |
c7726bb1b2 | ||
![]() |
4c2c91a2bd | ||
![]() |
b3a52c2466 | ||
![]() |
1ba53e2c49 | ||
![]() |
21e3425e76 | ||
![]() |
195270525c | ||
![]() |
70c8353042 | ||
![]() |
0c1dedb11c | ||
![]() |
12efb901a1 | ||
![]() |
4f969a0288 | ||
![]() |
adf4f1902d | ||
![]() |
36ea6a0a72 | ||
![]() |
f901b1fe4f | ||
![]() |
82991b9949 | ||
![]() |
a6a46b8d95 | ||
![]() |
5667d8dbb6 | ||
![]() |
602c33a1eb | ||
![]() |
f5ca38ef0b | ||
![]() |
5531f86da1 | ||
![]() |
cdb497123b | ||
![]() |
8d6e218bf4 | ||
![]() |
ee83d00866 | ||
![]() |
3b39c71835 | ||
![]() |
a4cb86b386 | ||
![]() |
00e1397586 |
@@ -4,4 +4,7 @@ DATABASE_TYPE=sqlite
|
||||
ADMIN_SECRET=admin
|
||||
DISABLE_EMAIL_VERIFICATION=true
|
||||
JWT_SECRET=random_string
|
||||
JWT_TYPE=HS256
|
||||
JWT_TYPE=HS256
|
||||
ROLES=user,admin
|
||||
DEFAULT_ROLE=user
|
||||
JWT_ROLE_CLAIM=role
|
94
.github/workflows/release.yaml
vendored
94
.github/workflows/release.yaml
vendored
@@ -3,58 +3,72 @@ on:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
releases-windows:
|
||||
releases:
|
||||
name: Release Authorizer Binary
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
goos: [windows]
|
||||
goarch: [amd64]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get install build-essential wget zip && \
|
||||
wget https://golang.org/dl/go1.17.1.linux-amd64.tar.gz && \
|
||||
sudo tar -C /usr/local -xzf go1.17.1.linux-amd64.tar.gz && \
|
||||
echo PATH=$PATH:/usr/local/go/bin >> ~/.bashrc && \
|
||||
source ~/.bashrc && \
|
||||
export PATH=$PATH:/usr/local/go/bin && \
|
||||
sudo apt-get install build-essential wget zip gcc-mingw-w64 && \
|
||||
sudo apt-get remove --auto-remove golang-go && \
|
||||
sudo rm -rf /usr/bin/go &&\
|
||||
wget --progress=dot:mega https://golang.org/dl/go1.17.1.linux-amd64.tar.gz -O go-linux.tar.gz && \
|
||||
sudo tar -zxf go-linux.tar.gz && \
|
||||
sudo mv go /usr/bin/ && \
|
||||
sudo mkdir -p /go/bin /go/src /go/pkg && \
|
||||
export GO_HOME=/usr/bin/go && \
|
||||
export GOPATH=/go && \
|
||||
export PATH=${GOPATH}/bin:${GO_HOME}/bin/:$PATH && \
|
||||
echo "/usr/bin/go/bin" >> $GITHUB_PATH
|
||||
echo "/usr/bin/x86_64-w64-mingw32-gcc" >> GITHUB_PATH
|
||||
go version && \
|
||||
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 && \
|
||||
mv github-assets-uploader /usr/sbin/ && \
|
||||
rm -f github-assets-uploader.tar.gz && \
|
||||
sudo mv github-assets-uploader /usr/sbin/ && \
|
||||
sudo rm -f github-assets-uploader.tar.gz && \
|
||||
github-assets-uploader -version
|
||||
- name: Print Go paths
|
||||
run: whereis go
|
||||
- name: Print Go Version
|
||||
run: go version
|
||||
- name: Install windows dependencies
|
||||
if: ${{ matrix.goos == 'windows'}}
|
||||
run: sudo apt-get install gcc-mingw-w64
|
||||
- name: Set VERSION env
|
||||
run: echo VERSION=$(basename ${GITHUB_REF}) >> ${GITHUB_ENV}
|
||||
- name: Echo Version
|
||||
run: echo ${VERSION}
|
||||
- name: Set CGO_ENABLED env
|
||||
run: echo CGO_ENABLED=1 >> ${GITHUB_ENV} && echo GOOS=${matrix.goos} >> ${GITHUB_ENV}
|
||||
- name: CAT ENV
|
||||
run: cat ${GITHUB_ENV}
|
||||
- name: Set Windows C Compiler GO env
|
||||
if: ${{ matrix.goos == 'windows'}}
|
||||
run: echo CC=/usr/bin/x86_64-w64-mingw32-gcc >> ${GITHUB_ENV}
|
||||
- name: Create build
|
||||
run: make clean && make build
|
||||
- name: Package files
|
||||
if: ${{ matrix.goos == 'windows'}}
|
||||
- name: Copy .env file
|
||||
run: mv .env.sample .env
|
||||
- name: Package files for windows
|
||||
run: |
|
||||
mv .env.sample .env && \
|
||||
zip -vr authorizer-${VERSION}-windows-${matrix.goarch}.zip .env app build template && \
|
||||
github-assets-uploader -f authorizer-${VERSION}-windows-${matrix.goarch}.zip -mediatype application/zip -repo authorizerdev/authorizer -token ${secrets.RELEASE_TOKEN} -tag ${VERSION}
|
||||
make clean && \
|
||||
CGO_ENABLED=1 GOOS=windows CC=/usr/bin/x86_64-w64-mingw32-gcc make && \
|
||||
mv build/server build/server.exe && \
|
||||
zip -vr authorizer-${VERSION}-windows-amd64.zip .env app/build build templates
|
||||
- name: Package files for linux
|
||||
run: |
|
||||
make clean && \
|
||||
CGO_ENABLED=1 make && \
|
||||
tar cvfz authorizer-${VERSION}-linux-amd64.tar.gz .env app/build build templates
|
||||
- name: Upload assets
|
||||
run: |
|
||||
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}-linux-amd64.tar.gz -mediatype application/gzip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
# - uses: wangyoucao577/go-release-action@v1.20
|
||||
# with:
|
||||
# github_token: ${{ secrets.RELEASE_TOKEN }}
|
||||
# goos: ${{ matrix.goos }}
|
||||
# goarch: ${{ matrix.goarch }}
|
||||
# build_command: make clean && make
|
||||
# md5sum: FALSE
|
||||
# extra_files: .env.sample app build template
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v3
|
||||
with:
|
||||
images: lakhansamani/authorizer
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
build-args: |
|
||||
VERSION=${{ steps.meta.outputs.labels['org.opencontainers.image.version'] }}
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -6,3 +6,5 @@ build
|
||||
.env
|
||||
data.db
|
||||
.DS_Store
|
||||
.env.local
|
||||
*.tar.gz
|
@@ -3,9 +3,10 @@ WORKDIR /app
|
||||
COPY server server
|
||||
COPY Makefile .
|
||||
|
||||
ARG VERSION=0.1.0-beta.0
|
||||
ENV VERSION="${VERSION}"
|
||||
ARG VERSION="latest"
|
||||
ENV VERSION="$VERSION"
|
||||
|
||||
RUN echo "$VERSION"
|
||||
RUN apk add build-base &&\
|
||||
make clean && make && \
|
||||
chmod 777 build/server
|
||||
|
2
Makefile
2
Makefile
@@ -2,6 +2,6 @@ DEFAULT_VERSION=0.1.0-local
|
||||
VERSION := $(or $(VERSION),$(DEFAULT_VERSION))
|
||||
|
||||
cmd:
|
||||
cd server && CGO_ENABLED=1 GOOS=$(GOOS) CC=$(CC) go build -ldflags "-w -X main.Version=$(VERSION)" -o '../build/server'
|
||||
cd server && go build -ldflags "-w -X main.Version=$(VERSION)" -o '../build/server'
|
||||
clean:
|
||||
rm -rf build
|
124
README.md
124
README.md
@@ -7,10 +7,10 @@
|
||||
Authorizer
|
||||
</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 SQL database.
|
||||
|
||||
## Table of contents
|
||||
|
||||
- [Introduction](#introduction)
|
||||
- [Getting Started](#getting-started)
|
||||
- [Contributing](https://github.com/authorizerdev/authorizer/blob/main/.github/CONTRIBUTING.md)
|
||||
@@ -28,7 +28,8 @@
|
||||
- ✅ Email verification
|
||||
- ✅ APIs to update profile securely
|
||||
- ✅ Forgot password flow using email
|
||||
- ✅ Social logins (Google, Github, more coming soon)
|
||||
- ✅ Social logins (Google, Github, Facebook, more coming soon)
|
||||
- ✅ Role-based access management
|
||||
|
||||
## Project Status
|
||||
|
||||
@@ -37,7 +38,6 @@
|
||||
## Roadmap
|
||||
|
||||
- Password-less login with email and magic link
|
||||
- Role-based access management system
|
||||
- Support more JWT encryption algorithms (Currently supporting HS256)
|
||||
- 2 Factor authentication
|
||||
- Back office (Admin dashboard to manage user)
|
||||
@@ -65,9 +65,87 @@
|
||||
|
||||
## Trying out Authorizer
|
||||
|
||||
This guide helps you practice using Authorizer to evaluate it before you use it in a production environment. It includes instructions for installing the Authorizer server in standalone mode.
|
||||
This guide helps you practice using Authorizer to evaluate it before you use it in a production environment. It includes instructions for installing the Authorizer server in local or standalone mode.
|
||||
|
||||
## Installing a simple instance of Authorizer
|
||||
- [Install using source code](#install-using-source-code)
|
||||
- [Install using binaries](#install-using-binaries)
|
||||
- [Install instance on heroku](#install-instance-on-Heroku)
|
||||
|
||||
## Install using source code
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- OS: Linux or macOS or windows
|
||||
- Go: (Golang)(https://golang.org/dl/) >= v1.15
|
||||
|
||||
### Project Setup
|
||||
|
||||
1. Fork the [authorizer](https://github.com/authorizerdev/authorizer) repository (**Skip this step if you have access to repo**)
|
||||
2. `git clone https://github.com/authorizerdev/authorizer.git`
|
||||
3. `cd authorizer`
|
||||
4. `cp .env.sample .env`. Check all the supported env [here](https://docs.authorizer.dev/core/env/)
|
||||
5. Build the code `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
|
||||
6. Run binary `./build/server`
|
||||
|
||||
## Install using binaries
|
||||
|
||||
Deploy / Try Authorizer using binaries. With each [Authorizer Release](https://github.com/authorizerdev/authorizer/releases)
|
||||
binaries are baked with required deployment files and bundled. You can download a specific version of it for the following operating systems:
|
||||
|
||||
- Mac OSX
|
||||
- Linux
|
||||
- Windows
|
||||
|
||||
### Step 1: Download and unzip bundle
|
||||
|
||||
- Download the Bundle for the specific OS from the [release page](https://github.com/authorizerdev/authorizer/releases)
|
||||
|
||||
> Note: For windows, it includes `.zip` file. For Linux & MacOS, it includes `.tar.gz` file.
|
||||
|
||||
- Unzip using following command
|
||||
|
||||
- Mac / Linux
|
||||
|
||||
```sh
|
||||
tar -zxf AUTHORIZER_VERSION -c authorizer
|
||||
```
|
||||
|
||||
- Windows
|
||||
|
||||
```sh
|
||||
unzip AUTHORIZER_VERSION
|
||||
```
|
||||
|
||||
- Change directory to `authorizer`
|
||||
|
||||
```sh
|
||||
cd authorizer
|
||||
```
|
||||
|
||||
### Step 2: Configure environment variables
|
||||
|
||||
Required environment variables are pre-configured in `.env` file. But based on the production requirements, please configure more environment variables. You can refer to [environment variables docs](/core/env) for more information.
|
||||
|
||||
### Step 3: Start Authorizer
|
||||
|
||||
- Run following command to start authorizer
|
||||
|
||||
- For Mac / Linux users
|
||||
|
||||
```sh
|
||||
./build/server
|
||||
```
|
||||
|
||||
- For windows
|
||||
|
||||
```sh
|
||||
./build/server.exe
|
||||
```
|
||||
|
||||
> Note: For mac users, you might have to give binary the permission to execute. Here is the command you can use to grant permission `xattr -d com.apple.quarantine build/server`
|
||||
|
||||
## Install instance on Heroku
|
||||
|
||||
Deploy Authorizer using [heroku](https://github.com/authorizerdev/authorizer-heroku) and quickly play with it in 30seconds
|
||||
<br/><br/>
|
||||
@@ -92,30 +170,30 @@ This example demonstrates how you can use [`@authorizerdev/authorizer-js`](/auth
|
||||
<script src="https://unpkg.com/@authorizerdev/authorizer-js/lib/authorizer.min.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
const authorizerRef = new authorizerdev.Authorizer({
|
||||
authorizerURL: `AUTHORIZER_URL`,
|
||||
redirectURL: window.location.origin,
|
||||
});
|
||||
const authorizerRef = new authorizerdev.Authorizer({
|
||||
authorizerURL: `AUTHORIZER_URL`,
|
||||
redirectURL: window.location.origin,
|
||||
});
|
||||
|
||||
// use the button selector as per your application
|
||||
const logoutBtn = document.getElementById("logout");
|
||||
logoutBtn.addEventListener("click", async function () {
|
||||
await authorizerRef.logout();
|
||||
window.location.href = "/";
|
||||
});
|
||||
// use the button selector as per your application
|
||||
const logoutBtn = document.getElementById('logout');
|
||||
logoutBtn.addEventListener('click', async function () {
|
||||
await authorizerRef.logout();
|
||||
window.location.href = '/';
|
||||
});
|
||||
|
||||
async function onLoad() {
|
||||
const res = await authorizerRef.fingertipLogin();
|
||||
if (res && res.user) {
|
||||
// you can use user information here, eg:
|
||||
/**
|
||||
async function onLoad() {
|
||||
const res = await authorizerRef.browserLogin();
|
||||
if (res && res.user) {
|
||||
// you can use user information here, eg:
|
||||
/**
|
||||
const userSection = document.getElementById('user');
|
||||
const logoutSection = document.getElementById('logout-section');
|
||||
logoutSection.classList.toggle('hide');
|
||||
userSection.innerHTML = `Welcome, ${res.user.email}`;
|
||||
*/
|
||||
}
|
||||
}
|
||||
onLoad();
|
||||
}
|
||||
}
|
||||
onLoad();
|
||||
</script>
|
||||
```
|
||||
|
22
TODO.md
22
TODO.md
@@ -0,0 +1,22 @@
|
||||
# Task List
|
||||
|
||||
# Feature roles
|
||||
|
||||
For the first version we will only support setting roles master list via env
|
||||
|
||||
- [x] Support following ENV
|
||||
- [x] `ROLES` -> comma separated list of role names
|
||||
- [x] `DEFAULT_ROLE` -> default role to assign to users
|
||||
- [x] Add roles input for signup
|
||||
- [x] Add roles to update profile mutation
|
||||
- [x] Add roles input for login
|
||||
- [x] Return roles to user
|
||||
- [x] Return roles in users list for super admin
|
||||
- [x] Add roles to the JWT token generation
|
||||
- [x] Validate token should also validate the role, if roles to validate again is present in request
|
||||
|
||||
# Misc
|
||||
|
||||
- [x] Fix email template
|
||||
- [x] Add support for organization name in .env
|
||||
- [x] Add support for organization logo in .env
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
86
app/package-lock.json
generated
86
app/package-lock.json
generated
@@ -8,7 +8,7 @@
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@authorizerdev/authorizer-react": "^0.1.0-beta.8",
|
||||
"@authorizerdev/authorizer-react": "^0.1.0-beta.18",
|
||||
"@types/react": "^17.0.15",
|
||||
"@types/react-dom": "^17.0.9",
|
||||
"esbuild": "^0.12.17",
|
||||
@@ -22,9 +22,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@authorizerdev/authorizer-js": {
|
||||
"version": "0.1.0-beta.12",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.1.0-beta.12.tgz",
|
||||
"integrity": "sha512-vpZAWtnZz71q/Zn5qPbFqkId4Qe636jYrfTQPKl+PUW21UvJnpVcNuUUZM8VhNcIK+jVYnHLSWb9jJnuwZwrNg==",
|
||||
"version": "0.1.0-beta.19",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.1.0-beta.19.tgz",
|
||||
"integrity": "sha512-//uYjklwQfQKqLJHMAyjdrzh2nz6DycB3lEgl6bTXxmSbrz+l1kQyxB3y8wP/W30IrBQz8bZb+1sau+LD/FU7g==",
|
||||
"dependencies": {
|
||||
"node-fetch": "^2.6.1"
|
||||
},
|
||||
@@ -33,11 +33,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@authorizerdev/authorizer-react": {
|
||||
"version": "0.1.0-beta.8",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.1.0-beta.8.tgz",
|
||||
"integrity": "sha512-5diOeAleZFA0uf/8IS3+D261VXoOBnN9kA4ZvfJa4M376br5CGESFRAyLRWT0iWskTLX9g3BfdHusUjVbBxfmg==",
|
||||
"version": "0.1.0-beta.18",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.1.0-beta.18.tgz",
|
||||
"integrity": "sha512-lRWWlS9akZwwINRW1NatsbMob06NXht3HXNTUTlu1s8m1YjxmFRE/AL6UIplzAYTpR6eDWMxEEaS0qAVxovUcg==",
|
||||
"dependencies": {
|
||||
"@authorizerdev/authorizer-js": "^0.1.0-beta.12",
|
||||
"@authorizerdev/authorizer-js": "^0.1.0-beta.19",
|
||||
"final-form": "^4.20.2",
|
||||
"react-final-form": "^6.5.3",
|
||||
"styled-components": "^5.3.0"
|
||||
@@ -533,9 +533,12 @@
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
|
||||
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==",
|
||||
"version": "2.6.5",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz",
|
||||
"integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
}
|
||||
@@ -755,6 +758,11 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "4.3.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz",
|
||||
@@ -771,23 +779,37 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz",
|
||||
"integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw=="
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
|
||||
"dependencies": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@authorizerdev/authorizer-js": {
|
||||
"version": "0.1.0-beta.12",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.1.0-beta.12.tgz",
|
||||
"integrity": "sha512-vpZAWtnZz71q/Zn5qPbFqkId4Qe636jYrfTQPKl+PUW21UvJnpVcNuUUZM8VhNcIK+jVYnHLSWb9jJnuwZwrNg==",
|
||||
"version": "0.1.0-beta.19",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.1.0-beta.19.tgz",
|
||||
"integrity": "sha512-//uYjklwQfQKqLJHMAyjdrzh2nz6DycB3lEgl6bTXxmSbrz+l1kQyxB3y8wP/W30IrBQz8bZb+1sau+LD/FU7g==",
|
||||
"requires": {
|
||||
"node-fetch": "^2.6.1"
|
||||
}
|
||||
},
|
||||
"@authorizerdev/authorizer-react": {
|
||||
"version": "0.1.0-beta.8",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.1.0-beta.8.tgz",
|
||||
"integrity": "sha512-5diOeAleZFA0uf/8IS3+D261VXoOBnN9kA4ZvfJa4M376br5CGESFRAyLRWT0iWskTLX9g3BfdHusUjVbBxfmg==",
|
||||
"version": "0.1.0-beta.18",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.1.0-beta.18.tgz",
|
||||
"integrity": "sha512-lRWWlS9akZwwINRW1NatsbMob06NXht3HXNTUTlu1s8m1YjxmFRE/AL6UIplzAYTpR6eDWMxEEaS0qAVxovUcg==",
|
||||
"requires": {
|
||||
"@authorizerdev/authorizer-js": "^0.1.0-beta.12",
|
||||
"@authorizerdev/authorizer-js": "^0.1.0-beta.19",
|
||||
"final-form": "^4.20.2",
|
||||
"react-final-form": "^6.5.3",
|
||||
"styled-components": "^5.3.0"
|
||||
@@ -1181,9 +1203,12 @@
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
|
||||
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
|
||||
"version": "2.6.5",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz",
|
||||
"integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==",
|
||||
"requires": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
@@ -1360,6 +1385,11 @@
|
||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
|
||||
},
|
||||
"tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
|
||||
},
|
||||
"typescript": {
|
||||
"version": "4.3.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz",
|
||||
@@ -1369,6 +1399,20 @@
|
||||
"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": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
|
||||
},
|
||||
"whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
|
||||
"requires": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -10,7 +10,7 @@
|
||||
"author": "Lakhan Samani",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@authorizerdev/authorizer-react": "^0.1.0-beta.8",
|
||||
"@authorizerdev/authorizer-react": "^0.1.0-beta.18",
|
||||
"@types/react": "^17.0.15",
|
||||
"@types/react-dom": "^17.0.9",
|
||||
"esbuild": "^0.12.17",
|
||||
|
@@ -4,30 +4,52 @@ import { AuthorizerProvider } from '@authorizerdev/authorizer-react';
|
||||
import Root from './Root';
|
||||
|
||||
export default function App() {
|
||||
// @ts-ignore
|
||||
const globalState: Record<string, string> = window['__authorizer__'];
|
||||
return (
|
||||
<div style={{ display: 'flex', justifyContent: 'center' }}>
|
||||
<div
|
||||
style={{
|
||||
width: 400,
|
||||
margin: `10px auto`,
|
||||
border: `1px solid #D1D5DB`,
|
||||
padding: `25px 20px`,
|
||||
borderRadius: 5,
|
||||
}}
|
||||
>
|
||||
<BrowserRouter>
|
||||
<AuthorizerProvider
|
||||
config={{
|
||||
authorizerURL: globalState.authorizerURL,
|
||||
redirectURL: globalState.redirectURL,
|
||||
}}
|
||||
>
|
||||
<Root />
|
||||
</AuthorizerProvider>
|
||||
</BrowserRouter>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
// @ts-ignore
|
||||
const globalState: Record<string, string> = window['__authorizer__'];
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
marginTop: 20,
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={`${globalState.organizationLogo}`}
|
||||
alt="logo"
|
||||
style={{ height: 60, width: 60, objectFit: 'cover' }}
|
||||
/>
|
||||
<h1>{globalState.organizationName}</h1>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
width: 400,
|
||||
margin: `10px auto`,
|
||||
border: `1px solid #D1D5DB`,
|
||||
padding: `25px 20px`,
|
||||
borderRadius: 5,
|
||||
}}
|
||||
>
|
||||
<BrowserRouter>
|
||||
<AuthorizerProvider
|
||||
config={{
|
||||
authorizerURL: globalState.authorizerURL,
|
||||
redirectURL: globalState.redirectURL,
|
||||
}}
|
||||
>
|
||||
<Root />
|
||||
</AuthorizerProvider>
|
||||
</BrowserRouter>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@@ -10,9 +10,9 @@ export default function Root() {
|
||||
|
||||
useEffect(() => {
|
||||
if (token) {
|
||||
const url = new URL(config.redirectURL);
|
||||
const url = new URL(config.redirectURL || '/app');
|
||||
if (url.origin !== window.location.origin) {
|
||||
window.location.href = config.redirectURL;
|
||||
window.location.href = config.redirectURL || '/app';
|
||||
}
|
||||
}
|
||||
return () => {};
|
||||
|
@@ -2,9 +2,9 @@ import React, { Fragment } from 'react';
|
||||
import { Authorizer } from '@authorizerdev/authorizer-react';
|
||||
|
||||
export default function Login() {
|
||||
return (
|
||||
<Fragment>
|
||||
<Authorizer />
|
||||
</Fragment>
|
||||
);
|
||||
return (
|
||||
<Fragment>
|
||||
<Authorizer />
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
18
scripts/build-mac.sh
Normal file
18
scripts/build-mac.sh
Normal file
@@ -0,0 +1,18 @@
|
||||
VERSION="$1"
|
||||
make clean && CGO_ENABLED=1 VERSION=${VERSION} make
|
||||
FILE_NAME=authorizer-${VERSION}-darwin-amd64.tar.gz
|
||||
tar cvfz ${FILE_NAME} .env app/build build templates
|
||||
AUTH="Authorization: token $GITHUB_TOKEN"
|
||||
RELASE_INFO=$(curl -sH "$AUTH" https://api.github.com/repos/authorizerdev/authorizer/releases/tags/${VERSION})
|
||||
echo $RELASE_INFO
|
||||
|
||||
eval $(echo "$RELASE_INFO" | grep -m 1 "id.:" | grep -w id | tr : = | tr -cd '[[:alnum:]]=')
|
||||
[ "$id" ] || { echo "Error: Failed to get release id for tag: $VERSION"; echo "$RELASE_INFO" | awk 'length($0)<100' >&2; exit 1; }
|
||||
echo $id
|
||||
GH_ASSET="https://uploads.github.com/repos/authorizerdev/authorizer/releases/$id/assets?name=$(basename $FILE_NAME)"
|
||||
|
||||
echo $GH_ASSET
|
||||
|
||||
curl -H $AUTH -H "Content-Type: $(file -b --mime-type $FILE_NAME)" --data-binary @$FILE_NAME $GH_ASSET
|
||||
|
||||
curl "$GITHUB_OAUTH_BASIC" --data-binary @"$FILE_NAME" -H "Authorization: token $GITHUB_TOKEN" -H "Content-Type: application/octet-stream" $GH_ASSET
|
@@ -13,7 +13,6 @@ var (
|
||||
JWT_TYPE = ""
|
||||
JWT_SECRET = ""
|
||||
ALLOWED_ORIGINS = []string{}
|
||||
ALLOWED_CALLBACK_URLS = []string{}
|
||||
AUTHORIZER_URL = ""
|
||||
PORT = "8080"
|
||||
REDIS_URL = ""
|
||||
@@ -23,6 +22,11 @@ var (
|
||||
DISABLE_EMAIL_VERIFICATION = "false"
|
||||
DISABLE_BASIC_AUTHENTICATION = "false"
|
||||
|
||||
// ROLES
|
||||
ROLES = []string{}
|
||||
DEFAULT_ROLE = ""
|
||||
JWT_ROLE_CLAIM = "role"
|
||||
|
||||
// OAuth login
|
||||
GOOGLE_CLIENT_ID = ""
|
||||
GOOGLE_CLIENT_SECRET = ""
|
||||
@@ -32,4 +36,8 @@ var (
|
||||
FACEBOOK_CLIENT_SECRET = ""
|
||||
TWITTER_CLIENT_ID = ""
|
||||
TWITTER_CLIENT_SECRET = ""
|
||||
|
||||
// Org envs
|
||||
ORGANIZATION_NAME = "Authorizer"
|
||||
ORGANIZATION_LOGO = "https://authorizer.dev/images/logo.png"
|
||||
)
|
||||
|
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/enum"
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/driver/sqlite"
|
||||
@@ -17,13 +18,15 @@ type Manager interface {
|
||||
UpdateUser(user User) (User, error)
|
||||
GetUsers() ([]User, error)
|
||||
GetUserByEmail(email string) (User, error)
|
||||
UpdateVerificationTime(verifiedAt int64, id uint) error
|
||||
GetUserByID(email string) (User, error)
|
||||
UpdateVerificationTime(verifiedAt int64, id uuid.UUID) error
|
||||
AddVerification(verification VerificationRequest) (VerificationRequest, error)
|
||||
GetVerificationByToken(token string) (VerificationRequest, error)
|
||||
DeleteToken(email string) error
|
||||
GetVerificationRequests() ([]VerificationRequest, error)
|
||||
GetVerificationByEmail(email string) (VerificationRequest, error)
|
||||
DeleteUser(email string) error
|
||||
SaveRoles(roles []Role) error
|
||||
}
|
||||
|
||||
type manager struct {
|
||||
@@ -53,7 +56,7 @@ func InitDB() {
|
||||
if err != nil {
|
||||
log.Fatal("Failed to init db:", err)
|
||||
} else {
|
||||
db.AutoMigrate(&User{}, &VerificationRequest{})
|
||||
db.AutoMigrate(&User{}, &VerificationRequest{}, &Role{})
|
||||
}
|
||||
|
||||
Mgr = &manager{db: db}
|
||||
|
34
server/db/roles.go
Normal file
34
server/db/roles.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
type Role struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;"`
|
||||
Role string `gorm:"unique"`
|
||||
}
|
||||
|
||||
func (r *Role) BeforeCreate(tx *gorm.DB) (err error) {
|
||||
r.ID = uuid.New()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SaveRoles function to save roles
|
||||
func (mgr *manager) SaveRoles(roles []Role) error {
|
||||
res := mgr.db.Clauses(
|
||||
clause.OnConflict{
|
||||
DoNothing: true,
|
||||
}).Create(&roles)
|
||||
if res.Error != nil {
|
||||
log.Println(`Error saving roles`)
|
||||
return res.Error
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@@ -2,12 +2,15 @@ package db
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
ID uuid.UUID `gorm:"type:uuid;"`
|
||||
FirstName string
|
||||
LastName string
|
||||
Email string `gorm:"unique"`
|
||||
@@ -17,6 +20,13 @@ type User struct {
|
||||
CreatedAt int64 `gorm:"autoCreateTime"`
|
||||
UpdatedAt int64 `gorm:"autoUpdateTime"`
|
||||
Image string
|
||||
Roles string
|
||||
}
|
||||
|
||||
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
|
||||
u.ID = uuid.New()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SaveUser function to add user even with email conflict
|
||||
@@ -36,10 +46,11 @@ func (mgr *manager) SaveUser(user User) (User, error) {
|
||||
|
||||
// UpdateUser function to update user with ID conflict
|
||||
func (mgr *manager) UpdateUser(user User) (User, error) {
|
||||
user.UpdatedAt = time.Now().Unix()
|
||||
result := mgr.db.Clauses(
|
||||
clause.OnConflict{
|
||||
UpdateAll: true,
|
||||
Columns: []clause.Column{{Name: "id"}},
|
||||
Columns: []clause.Column{{Name: "email"}},
|
||||
}).Create(&user)
|
||||
|
||||
if result.Error != nil {
|
||||
@@ -71,7 +82,18 @@ func (mgr *manager) GetUserByEmail(email string) (User, error) {
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (mgr *manager) UpdateVerificationTime(verifiedAt int64, id uint) error {
|
||||
func (mgr *manager) GetUserByID(id string) (User, error) {
|
||||
var user User
|
||||
result := mgr.db.Where("id = ?", id).First(&user)
|
||||
|
||||
if result.Error != nil {
|
||||
return user, result.Error
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (mgr *manager) UpdateVerificationTime(verifiedAt int64, id uuid.UUID) error {
|
||||
user := &User{
|
||||
ID: id,
|
||||
}
|
||||
|
@@ -3,12 +3,14 @@ package db
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
type VerificationRequest struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
Token string `gorm:"index"`
|
||||
ID uuid.UUID `gorm:"type:uuid;"`
|
||||
Token string `gorm:"index"`
|
||||
Identifier string
|
||||
ExpiresAt int64
|
||||
CreatedAt int64 `gorm:"autoCreateTime"`
|
||||
@@ -16,6 +18,12 @@ type VerificationRequest struct {
|
||||
Email string `gorm:"unique"`
|
||||
}
|
||||
|
||||
func (v *VerificationRequest) BeforeCreate(tx *gorm.DB) (err error) {
|
||||
v.ID = uuid.New()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// AddVerification function to add verification record
|
||||
func (mgr *manager) AddVerification(verification VerificationRequest) (VerificationRequest, error) {
|
||||
result := mgr.db.Clauses(clause.OnConflict{
|
||||
|
101
server/env.go
101
server/env.go
@@ -11,43 +11,33 @@ import (
|
||||
)
|
||||
|
||||
// build variables
|
||||
var Version string
|
||||
|
||||
// ParseArgs -> to parse the cli flag and get db url. This is useful with heroku button
|
||||
func ParseArgs() {
|
||||
dbURL := flag.String("database_url", "", "Database connection string")
|
||||
dbType := flag.String("databse_type", "", "Database type, possible values are postgres,mysql,sqlite")
|
||||
authorizerURL := flag.String("authorizer_url", "", "URL for authorizer instance, eg: https://xyz.herokuapp.com")
|
||||
|
||||
flag.Parse()
|
||||
if *dbURL != "" {
|
||||
constants.DATABASE_URL = *dbURL
|
||||
}
|
||||
|
||||
if *dbType != "" {
|
||||
constants.DATABASE_TYPE = *dbType
|
||||
}
|
||||
|
||||
if *authorizerURL != "" {
|
||||
constants.AUTHORIZER_URL = *authorizerURL
|
||||
}
|
||||
}
|
||||
var (
|
||||
Version string
|
||||
ARG_DB_URL *string
|
||||
ARG_DB_TYPE *string
|
||||
ARG_AUTHORIZER_URL *string
|
||||
ARG_ENV_FILE *string
|
||||
)
|
||||
|
||||
// InitEnv -> to initialize env and through error if required env are not present
|
||||
func InitEnv() {
|
||||
envPath := `.env`
|
||||
envFile := flag.String("env_file", "", "Env file path")
|
||||
ARG_DB_URL = flag.String("database_url", "", "Database connection string")
|
||||
ARG_DB_TYPE = flag.String("database_type", "", "Database type, possible values are postgres,mysql,sqlite")
|
||||
ARG_AUTHORIZER_URL = flag.String("authorizer_url", "", "URL for authorizer instance, eg: https://xyz.herokuapp.com")
|
||||
ARG_ENV_FILE = flag.String("env_file", "", "Env file path")
|
||||
|
||||
flag.Parse()
|
||||
if *envFile != "" {
|
||||
envPath = *envFile
|
||||
if *ARG_ENV_FILE != "" {
|
||||
envPath = *ARG_ENV_FILE
|
||||
}
|
||||
|
||||
err := godotenv.Load(envPath)
|
||||
if err != nil {
|
||||
log.Println("Error loading .env file")
|
||||
}
|
||||
|
||||
constants.VERSION = Version
|
||||
|
||||
constants.ADMIN_SECRET = os.Getenv("ADMIN_SECRET")
|
||||
constants.ENV = os.Getenv("ENV")
|
||||
constants.DATABASE_TYPE = os.Getenv("DATABASE_TYPE")
|
||||
@@ -73,6 +63,8 @@ func InitEnv() {
|
||||
constants.RESET_PASSWORD_URL = strings.TrimPrefix(os.Getenv("RESET_PASSWORD_URL"), "/")
|
||||
constants.DISABLE_BASIC_AUTHENTICATION = os.Getenv("DISABLE_BASIC_AUTHENTICATION")
|
||||
constants.DISABLE_EMAIL_VERIFICATION = os.Getenv("DISABLE_EMAIL_VERIFICATION")
|
||||
constants.DEFAULT_ROLE = os.Getenv("DEFAULT_ROLE")
|
||||
constants.JWT_ROLE_CLAIM = os.Getenv("JWT_ROLE_CLAIM")
|
||||
|
||||
if constants.ADMIN_SECRET == "" {
|
||||
panic("root admin secret is required")
|
||||
@@ -102,20 +94,18 @@ func InitEnv() {
|
||||
}
|
||||
constants.ALLOWED_ORIGINS = allowedOrigins
|
||||
|
||||
allowedCallbackSplit := strings.Split(os.Getenv("ALLOWED_CALLBACK_URLS"), ",")
|
||||
allowedCallbacks := []string{}
|
||||
for _, val := range allowedCallbackSplit {
|
||||
trimVal := strings.TrimSpace(val)
|
||||
if trimVal != "" {
|
||||
allowedCallbacks = append(allowedCallbacks, trimVal)
|
||||
}
|
||||
if *ARG_AUTHORIZER_URL != "" {
|
||||
constants.AUTHORIZER_URL = *ARG_AUTHORIZER_URL
|
||||
}
|
||||
|
||||
if *ARG_DB_URL != "" {
|
||||
constants.DATABASE_URL = *ARG_DB_URL
|
||||
}
|
||||
|
||||
if *ARG_DB_TYPE != "" {
|
||||
constants.DATABASE_TYPE = *ARG_DB_TYPE
|
||||
}
|
||||
if len(allowedCallbackSplit) == 0 {
|
||||
allowedCallbackSplit = []string{"*"}
|
||||
}
|
||||
constants.ALLOWED_CALLBACK_URLS = allowedCallbackSplit
|
||||
|
||||
ParseArgs()
|
||||
if constants.DATABASE_URL == "" {
|
||||
panic("Database url is required")
|
||||
}
|
||||
@@ -143,4 +133,41 @@ func InitEnv() {
|
||||
constants.DISABLE_EMAIL_VERIFICATION = "false"
|
||||
}
|
||||
}
|
||||
|
||||
rolesSplit := strings.Split(os.Getenv("ROLES"), ",")
|
||||
roles := []string{}
|
||||
defaultRole := ""
|
||||
|
||||
for _, val := range rolesSplit {
|
||||
trimVal := strings.TrimSpace(val)
|
||||
if trimVal != "" {
|
||||
roles = append(roles, trimVal)
|
||||
}
|
||||
|
||||
if trimVal == constants.DEFAULT_ROLE {
|
||||
defaultRole = trimVal
|
||||
}
|
||||
}
|
||||
if len(roles) > 0 && defaultRole == "" {
|
||||
panic(`Invalid DEFAULT_ROLE environment variable. It can be one from give ROLES environment variable value`)
|
||||
}
|
||||
|
||||
if len(roles) == 0 {
|
||||
roles = []string{"user", "admin"}
|
||||
constants.DEFAULT_ROLE = "user"
|
||||
}
|
||||
|
||||
constants.ROLES = roles
|
||||
|
||||
if constants.JWT_ROLE_CLAIM == "" {
|
||||
constants.JWT_ROLE_CLAIM = "role"
|
||||
}
|
||||
|
||||
if os.Getenv("ORGANIZATION_NAME") != "" {
|
||||
constants.ORGANIZATION_NAME = os.Getenv("ORGANIZATION_NAME")
|
||||
}
|
||||
|
||||
if os.Getenv("ORGANIZATION_LOGO") != "" {
|
||||
constants.ORGANIZATION_LOGO = os.Getenv("ORGANIZATION_LOGO")
|
||||
}
|
||||
}
|
||||
|
@@ -66,6 +66,7 @@ type ComplexityRoot struct {
|
||||
}
|
||||
|
||||
Mutation struct {
|
||||
AdminUpdateUser func(childComplexity int, params model.AdminUpdateUserInput) int
|
||||
DeleteUser func(childComplexity int, params model.DeleteUserInput) int
|
||||
ForgotPassword func(childComplexity int, params model.ForgotPasswordInput) int
|
||||
Login func(childComplexity int, params model.LoginInput) int
|
||||
@@ -80,7 +81,7 @@ type ComplexityRoot struct {
|
||||
Query struct {
|
||||
Meta func(childComplexity int) int
|
||||
Profile func(childComplexity int) int
|
||||
Token func(childComplexity int) int
|
||||
Token func(childComplexity int, role *string) int
|
||||
Users func(childComplexity int) int
|
||||
VerificationRequests func(childComplexity int) int
|
||||
}
|
||||
@@ -97,6 +98,7 @@ type ComplexityRoot struct {
|
||||
ID func(childComplexity int) int
|
||||
Image func(childComplexity int) int
|
||||
LastName func(childComplexity int) int
|
||||
Roles func(childComplexity int) int
|
||||
SignupMethod func(childComplexity int) int
|
||||
UpdatedAt func(childComplexity int) int
|
||||
}
|
||||
@@ -117,6 +119,7 @@ type MutationResolver interface {
|
||||
Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, error)
|
||||
Logout(ctx context.Context) (*model.Response, error)
|
||||
UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model.Response, error)
|
||||
AdminUpdateUser(ctx context.Context, params model.AdminUpdateUserInput) (*model.User, error)
|
||||
VerifyEmail(ctx context.Context, params model.VerifyEmailInput) (*model.AuthResponse, error)
|
||||
ResendVerifyEmail(ctx context.Context, params model.ResendVerifyEmailInput) (*model.Response, error)
|
||||
ForgotPassword(ctx context.Context, params model.ForgotPasswordInput) (*model.Response, error)
|
||||
@@ -126,7 +129,7 @@ type MutationResolver interface {
|
||||
type QueryResolver interface {
|
||||
Meta(ctx context.Context) (*model.Meta, error)
|
||||
Users(ctx context.Context) ([]*model.User, error)
|
||||
Token(ctx context.Context) (*model.AuthResponse, error)
|
||||
Token(ctx context.Context, role *string) (*model.AuthResponse, error)
|
||||
Profile(ctx context.Context) (*model.User, error)
|
||||
VerificationRequests(ctx context.Context) ([]*model.VerificationRequest, error)
|
||||
}
|
||||
@@ -237,6 +240,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
|
||||
return e.complexity.Meta.Version(childComplexity), true
|
||||
|
||||
case "Mutation.adminUpdateUser":
|
||||
if e.complexity.Mutation.AdminUpdateUser == nil {
|
||||
break
|
||||
}
|
||||
|
||||
args, err := ec.field_Mutation_adminUpdateUser_args(context.TODO(), rawArgs)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return e.complexity.Mutation.AdminUpdateUser(childComplexity, args["params"].(model.AdminUpdateUserInput)), true
|
||||
|
||||
case "Mutation.deleteUser":
|
||||
if e.complexity.Mutation.DeleteUser == nil {
|
||||
break
|
||||
@@ -359,7 +374,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.Query.Token(childComplexity), true
|
||||
args, err := ec.field_Query_token_args(context.TODO(), rawArgs)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return e.complexity.Query.Token(childComplexity, args["role"].(*string)), true
|
||||
|
||||
case "Query.users":
|
||||
if e.complexity.Query.Users == nil {
|
||||
@@ -431,6 +451,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
|
||||
return e.complexity.User.LastName(childComplexity), true
|
||||
|
||||
case "User.roles":
|
||||
if e.complexity.User.Roles == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.User.Roles(childComplexity), true
|
||||
|
||||
case "User.signupMethod":
|
||||
if e.complexity.User.SignupMethod == nil {
|
||||
break
|
||||
@@ -562,6 +589,8 @@ var sources = []*ast.Source{
|
||||
#
|
||||
# https://gqlgen.com/getting-started/
|
||||
scalar Int64
|
||||
scalar Map
|
||||
scalar Any
|
||||
|
||||
type Meta {
|
||||
version: String!
|
||||
@@ -583,6 +612,7 @@ type User {
|
||||
image: String
|
||||
createdAt: Int64
|
||||
updatedAt: Int64
|
||||
roles: [String!]!
|
||||
}
|
||||
|
||||
type VerificationRequest {
|
||||
@@ -618,11 +648,13 @@ input SignUpInput {
|
||||
password: String!
|
||||
confirmPassword: String!
|
||||
image: String
|
||||
roles: [String]
|
||||
}
|
||||
|
||||
input LoginInput {
|
||||
email: String!
|
||||
password: String!
|
||||
role: String
|
||||
}
|
||||
|
||||
input VerifyEmailInput {
|
||||
@@ -641,6 +673,16 @@ input UpdateProfileInput {
|
||||
lastName: String
|
||||
image: String
|
||||
email: String
|
||||
# roles: [String]
|
||||
}
|
||||
|
||||
input AdminUpdateUserInput {
|
||||
id: ID!
|
||||
email: String
|
||||
firstName: String
|
||||
lastName: String
|
||||
image: String
|
||||
roles: [String]
|
||||
}
|
||||
|
||||
input ForgotPasswordInput {
|
||||
@@ -662,6 +704,7 @@ type Mutation {
|
||||
login(params: LoginInput!): AuthResponse!
|
||||
logout: Response!
|
||||
updateProfile(params: UpdateProfileInput!): Response!
|
||||
adminUpdateUser(params: AdminUpdateUserInput!): User!
|
||||
verifyEmail(params: VerifyEmailInput!): AuthResponse!
|
||||
resendVerifyEmail(params: ResendVerifyEmailInput!): Response!
|
||||
forgotPassword(params: ForgotPasswordInput!): Response!
|
||||
@@ -672,7 +715,7 @@ type Mutation {
|
||||
type Query {
|
||||
meta: Meta!
|
||||
users: [User!]!
|
||||
token: AuthResponse
|
||||
token(role: String): AuthResponse
|
||||
profile: User!
|
||||
verificationRequests: [VerificationRequest!]!
|
||||
}
|
||||
@@ -684,6 +727,21 @@ var parsedSchema = gqlparser.MustLoadSchema(sources...)
|
||||
|
||||
// region ***************************** args.gotpl *****************************
|
||||
|
||||
func (ec *executionContext) field_Mutation_adminUpdateUser_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||
var err error
|
||||
args := map[string]interface{}{}
|
||||
var arg0 model.AdminUpdateUserInput
|
||||
if tmp, ok := rawArgs["params"]; ok {
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("params"))
|
||||
arg0, err = ec.unmarshalNAdminUpdateUserInput2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐAdminUpdateUserInput(ctx, tmp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
args["params"] = arg0
|
||||
return args, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) field_Mutation_deleteUser_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||
var err error
|
||||
args := map[string]interface{}{}
|
||||
@@ -819,6 +877,21 @@ func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs
|
||||
return args, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) field_Query_token_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||
var err error
|
||||
args := map[string]interface{}{}
|
||||
var arg0 *string
|
||||
if tmp, ok := rawArgs["role"]; ok {
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("role"))
|
||||
arg0, err = ec.unmarshalOString2ᚖstring(ctx, tmp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
args["role"] = arg0
|
||||
return args, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) field___Type_enumValues_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||
var err error
|
||||
args := map[string]interface{}{}
|
||||
@@ -1464,6 +1537,48 @@ func (ec *executionContext) _Mutation_updateProfile(ctx context.Context, field g
|
||||
return ec.marshalNResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐResponse(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Mutation_adminUpdateUser(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
}()
|
||||
fc := &graphql.FieldContext{
|
||||
Object: "Mutation",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: true,
|
||||
IsResolver: true,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
rawArgs := field.ArgumentMap(ec.Variables)
|
||||
args, err := ec.field_Mutation_adminUpdateUser_args(ctx, rawArgs)
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
fc.Args = args
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return ec.resolvers.Mutation().AdminUpdateUser(rctx, args["params"].(model.AdminUpdateUserInput))
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !graphql.HasFieldError(ctx, fc) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(*model.User)
|
||||
fc.Result = res
|
||||
return ec.marshalNUser2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐUser(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Mutation_verifyEmail(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
@@ -1760,9 +1875,16 @@ func (ec *executionContext) _Query_token(ctx context.Context, field graphql.Coll
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
rawArgs := field.ArgumentMap(ec.Variables)
|
||||
args, err := ec.field_Query_token_args(ctx, rawArgs)
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
fc.Args = args
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return ec.resolvers.Query().Token(rctx)
|
||||
return ec.resolvers.Query().Token(rctx, args["role"].(*string))
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
@@ -2249,6 +2371,41 @@ func (ec *executionContext) _User_updatedAt(ctx context.Context, field graphql.C
|
||||
return ec.marshalOInt642ᚖint64(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _User_roles(ctx context.Context, field graphql.CollectedField, obj *model.User) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
}()
|
||||
fc := &graphql.FieldContext{
|
||||
Object: "User",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.Roles, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !graphql.HasFieldError(ctx, fc) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.([]string)
|
||||
fc.Result = res
|
||||
return ec.marshalNString2ᚕstringᚄ(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _VerificationRequest_id(ctx context.Context, field graphql.CollectedField, obj *model.VerificationRequest) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
@@ -3563,6 +3720,66 @@ func (ec *executionContext) ___Type_ofType(ctx context.Context, field graphql.Co
|
||||
|
||||
// region **************************** input.gotpl *****************************
|
||||
|
||||
func (ec *executionContext) unmarshalInputAdminUpdateUserInput(ctx context.Context, obj interface{}) (model.AdminUpdateUserInput, error) {
|
||||
var it model.AdminUpdateUserInput
|
||||
var asMap = obj.(map[string]interface{})
|
||||
|
||||
for k, v := range asMap {
|
||||
switch k {
|
||||
case "id":
|
||||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("id"))
|
||||
it.ID, err = ec.unmarshalNID2string(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
case "email":
|
||||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email"))
|
||||
it.Email, err = ec.unmarshalOString2ᚖstring(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
case "firstName":
|
||||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("firstName"))
|
||||
it.FirstName, err = ec.unmarshalOString2ᚖstring(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
case "lastName":
|
||||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("lastName"))
|
||||
it.LastName, err = ec.unmarshalOString2ᚖstring(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
case "image":
|
||||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("image"))
|
||||
it.Image, err = ec.unmarshalOString2ᚖstring(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
case "roles":
|
||||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("roles"))
|
||||
it.Roles, err = ec.unmarshalOString2ᚕᚖstring(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return it, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalInputDeleteUserInput(ctx context.Context, obj interface{}) (model.DeleteUserInput, error) {
|
||||
var it model.DeleteUserInput
|
||||
var asMap = obj.(map[string]interface{})
|
||||
@@ -3625,6 +3842,14 @@ func (ec *executionContext) unmarshalInputLoginInput(ctx context.Context, obj in
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
case "role":
|
||||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("role"))
|
||||
it.Role, err = ec.unmarshalOString2ᚖstring(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3741,6 +3966,14 @@ func (ec *executionContext) unmarshalInputSignUpInput(ctx context.Context, obj i
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
case "roles":
|
||||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("roles"))
|
||||
it.Roles, err = ec.unmarshalOString2ᚕᚖstring(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4000,6 +4233,11 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
case "adminUpdateUser":
|
||||
out.Values[i] = ec._Mutation_adminUpdateUser(ctx, field)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
case "verifyEmail":
|
||||
out.Values[i] = ec._Mutation_verifyEmail(ctx, field)
|
||||
if out.Values[i] == graphql.Null {
|
||||
@@ -4198,6 +4436,11 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj
|
||||
out.Values[i] = ec._User_createdAt(ctx, field, obj)
|
||||
case "updatedAt":
|
||||
out.Values[i] = ec._User_updatedAt(ctx, field, obj)
|
||||
case "roles":
|
||||
out.Values[i] = ec._User_roles(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
default:
|
||||
panic("unknown field " + strconv.Quote(field.Name))
|
||||
}
|
||||
@@ -4493,6 +4736,11 @@ func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, o
|
||||
|
||||
// region ***************************** type.gotpl *****************************
|
||||
|
||||
func (ec *executionContext) unmarshalNAdminUpdateUserInput2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐAdminUpdateUserInput(ctx context.Context, v interface{}) (model.AdminUpdateUserInput, error) {
|
||||
res, err := ec.unmarshalInputAdminUpdateUserInput(ctx, v)
|
||||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNAuthResponse2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐAuthResponse(ctx context.Context, sel ast.SelectionSet, v model.AuthResponse) graphql.Marshaler {
|
||||
return ec._AuthResponse(ctx, sel, &v)
|
||||
}
|
||||
@@ -4610,6 +4858,36 @@ func (ec *executionContext) marshalNString2string(ctx context.Context, sel ast.S
|
||||
return res
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalNString2ᚕstringᚄ(ctx context.Context, v interface{}) ([]string, error) {
|
||||
var vSlice []interface{}
|
||||
if v != nil {
|
||||
if tmp1, ok := v.([]interface{}); ok {
|
||||
vSlice = tmp1
|
||||
} else {
|
||||
vSlice = []interface{}{v}
|
||||
}
|
||||
}
|
||||
var err error
|
||||
res := make([]string, len(vSlice))
|
||||
for i := range vSlice {
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i))
|
||||
res[i], err = ec.unmarshalNString2string(ctx, vSlice[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNString2ᚕstringᚄ(ctx context.Context, sel ast.SelectionSet, v []string) graphql.Marshaler {
|
||||
ret := make(graphql.Array, len(v))
|
||||
for i := range v {
|
||||
ret[i] = ec.marshalNString2string(ctx, sel, v[i])
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalNUpdateProfileInput2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐUpdateProfileInput(ctx context.Context, v interface{}) (model.UpdateProfileInput, error) {
|
||||
res, err := ec.unmarshalInputUpdateProfileInput(ctx, v)
|
||||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
@@ -5002,6 +5280,42 @@ func (ec *executionContext) marshalOString2string(ctx context.Context, sel ast.S
|
||||
return graphql.MarshalString(v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalOString2ᚕᚖstring(ctx context.Context, v interface{}) ([]*string, error) {
|
||||
if v == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var vSlice []interface{}
|
||||
if v != nil {
|
||||
if tmp1, ok := v.([]interface{}); ok {
|
||||
vSlice = tmp1
|
||||
} else {
|
||||
vSlice = []interface{}{v}
|
||||
}
|
||||
}
|
||||
var err error
|
||||
res := make([]*string, len(vSlice))
|
||||
for i := range vSlice {
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i))
|
||||
res[i], err = ec.unmarshalOString2ᚖstring(ctx, vSlice[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalOString2ᚕᚖstring(ctx context.Context, sel ast.SelectionSet, v []*string) graphql.Marshaler {
|
||||
if v == nil {
|
||||
return graphql.Null
|
||||
}
|
||||
ret := make(graphql.Array, len(v))
|
||||
for i := range v {
|
||||
ret[i] = ec.marshalOString2ᚖstring(ctx, sel, v[i])
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalOString2ᚖstring(ctx context.Context, v interface{}) (*string, error) {
|
||||
if v == nil {
|
||||
return nil, nil
|
||||
|
@@ -2,6 +2,15 @@
|
||||
|
||||
package model
|
||||
|
||||
type AdminUpdateUserInput struct {
|
||||
ID string `json:"id"`
|
||||
Email *string `json:"email"`
|
||||
FirstName *string `json:"firstName"`
|
||||
LastName *string `json:"lastName"`
|
||||
Image *string `json:"image"`
|
||||
Roles []*string `json:"roles"`
|
||||
}
|
||||
|
||||
type AuthResponse struct {
|
||||
Message string `json:"message"`
|
||||
AccessToken *string `json:"accessToken"`
|
||||
@@ -23,8 +32,9 @@ type ForgotPasswordInput struct {
|
||||
}
|
||||
|
||||
type LoginInput struct {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
Role *string `json:"role"`
|
||||
}
|
||||
|
||||
type Meta struct {
|
||||
@@ -52,12 +62,13 @@ type Response struct {
|
||||
}
|
||||
|
||||
type SignUpInput struct {
|
||||
FirstName *string `json:"firstName"`
|
||||
LastName *string `json:"lastName"`
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
ConfirmPassword string `json:"confirmPassword"`
|
||||
Image *string `json:"image"`
|
||||
FirstName *string `json:"firstName"`
|
||||
LastName *string `json:"lastName"`
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
ConfirmPassword string `json:"confirmPassword"`
|
||||
Image *string `json:"image"`
|
||||
Roles []*string `json:"roles"`
|
||||
}
|
||||
|
||||
type UpdateProfileInput struct {
|
||||
@@ -71,15 +82,16 @@ type UpdateProfileInput struct {
|
||||
}
|
||||
|
||||
type User struct {
|
||||
ID string `json:"id"`
|
||||
Email string `json:"email"`
|
||||
SignupMethod string `json:"signupMethod"`
|
||||
FirstName *string `json:"firstName"`
|
||||
LastName *string `json:"lastName"`
|
||||
EmailVerifiedAt *int64 `json:"emailVerifiedAt"`
|
||||
Image *string `json:"image"`
|
||||
CreatedAt *int64 `json:"createdAt"`
|
||||
UpdatedAt *int64 `json:"updatedAt"`
|
||||
ID string `json:"id"`
|
||||
Email string `json:"email"`
|
||||
SignupMethod string `json:"signupMethod"`
|
||||
FirstName *string `json:"firstName"`
|
||||
LastName *string `json:"lastName"`
|
||||
EmailVerifiedAt *int64 `json:"emailVerifiedAt"`
|
||||
Image *string `json:"image"`
|
||||
CreatedAt *int64 `json:"createdAt"`
|
||||
UpdatedAt *int64 `json:"updatedAt"`
|
||||
Roles []string `json:"roles"`
|
||||
}
|
||||
|
||||
type VerificationRequest struct {
|
||||
|
@@ -2,6 +2,8 @@
|
||||
#
|
||||
# https://gqlgen.com/getting-started/
|
||||
scalar Int64
|
||||
scalar Map
|
||||
scalar Any
|
||||
|
||||
type Meta {
|
||||
version: String!
|
||||
@@ -23,6 +25,7 @@ type User {
|
||||
image: String
|
||||
createdAt: Int64
|
||||
updatedAt: Int64
|
||||
roles: [String!]!
|
||||
}
|
||||
|
||||
type VerificationRequest {
|
||||
@@ -58,11 +61,13 @@ input SignUpInput {
|
||||
password: String!
|
||||
confirmPassword: String!
|
||||
image: String
|
||||
roles: [String]
|
||||
}
|
||||
|
||||
input LoginInput {
|
||||
email: String!
|
||||
password: String!
|
||||
role: String
|
||||
}
|
||||
|
||||
input VerifyEmailInput {
|
||||
@@ -81,6 +86,16 @@ input UpdateProfileInput {
|
||||
lastName: String
|
||||
image: String
|
||||
email: String
|
||||
# roles: [String]
|
||||
}
|
||||
|
||||
input AdminUpdateUserInput {
|
||||
id: ID!
|
||||
email: String
|
||||
firstName: String
|
||||
lastName: String
|
||||
image: String
|
||||
roles: [String]
|
||||
}
|
||||
|
||||
input ForgotPasswordInput {
|
||||
@@ -102,6 +117,7 @@ type Mutation {
|
||||
login(params: LoginInput!): AuthResponse!
|
||||
logout: Response!
|
||||
updateProfile(params: UpdateProfileInput!): Response!
|
||||
adminUpdateUser(params: AdminUpdateUserInput!): User!
|
||||
verifyEmail(params: VerifyEmailInput!): AuthResponse!
|
||||
resendVerifyEmail(params: ResendVerifyEmailInput!): Response!
|
||||
forgotPassword(params: ForgotPasswordInput!): Response!
|
||||
@@ -112,7 +128,7 @@ type Mutation {
|
||||
type Query {
|
||||
meta: Meta!
|
||||
users: [User!]!
|
||||
token: AuthResponse
|
||||
token(role: String): AuthResponse
|
||||
profile: User!
|
||||
verificationRequests: [VerificationRequest!]!
|
||||
}
|
||||
|
@@ -27,6 +27,10 @@ func (r *mutationResolver) UpdateProfile(ctx context.Context, params model.Updat
|
||||
return resolvers.UpdateProfile(ctx, params)
|
||||
}
|
||||
|
||||
func (r *mutationResolver) AdminUpdateUser(ctx context.Context, params model.AdminUpdateUserInput) (*model.User, error) {
|
||||
return resolvers.AdminUpdateUser(ctx, params)
|
||||
}
|
||||
|
||||
func (r *mutationResolver) VerifyEmail(ctx context.Context, params model.VerifyEmailInput) (*model.AuthResponse, error) {
|
||||
return resolvers.VerifyEmail(ctx, params)
|
||||
}
|
||||
@@ -55,8 +59,8 @@ func (r *queryResolver) Users(ctx context.Context) ([]*model.User, error) {
|
||||
return resolvers.Users(ctx)
|
||||
}
|
||||
|
||||
func (r *queryResolver) Token(ctx context.Context) (*model.AuthResponse, error) {
|
||||
return resolvers.Token(ctx)
|
||||
func (r *queryResolver) Token(ctx context.Context, role *string) (*model.AuthResponse, error) {
|
||||
return resolvers.Token(ctx, role)
|
||||
}
|
||||
|
||||
func (r *queryResolver) Profile(ctx context.Context) (*model.User, error) {
|
||||
@@ -73,7 +77,5 @@ func (r *Resolver) Mutation() generated.MutationResolver { return &mutationResol
|
||||
// Query returns generated.QueryResolver implementation.
|
||||
func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }
|
||||
|
||||
type (
|
||||
mutationResolver struct{ *Resolver }
|
||||
queryResolver struct{ *Resolver }
|
||||
)
|
||||
type mutationResolver struct{ *Resolver }
|
||||
type queryResolver struct{ *Resolver }
|
||||
|
@@ -25,7 +25,6 @@ func AppHandler() gin.HandlerFunc {
|
||||
|
||||
if state == "" {
|
||||
// cookie, err := utils.GetAuthToken(c)
|
||||
// log.Println(`cookie`, cookie)
|
||||
// if err != nil {
|
||||
// c.JSON(400, gin.H{"error": "invalid state"})
|
||||
// return
|
||||
@@ -67,13 +66,6 @@ func AppHandler() gin.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
log.Println(gin.H{
|
||||
"data": map[string]string{
|
||||
"authorizerURL": stateObj.AuthorizerURL,
|
||||
"redirectURL": stateObj.RedirectURL,
|
||||
},
|
||||
})
|
||||
|
||||
// debug the request state
|
||||
if pusher := c.Writer.Pusher(); pusher != nil {
|
||||
// use pusher.Push() to do server push
|
||||
@@ -83,8 +75,10 @@ func AppHandler() gin.HandlerFunc {
|
||||
}
|
||||
c.HTML(http.StatusOK, "app.tmpl", gin.H{
|
||||
"data": map[string]string{
|
||||
"authorizerURL": stateObj.AuthorizerURL,
|
||||
"redirectURL": stateObj.RedirectURL,
|
||||
"authorizerURL": stateObj.AuthorizerURL,
|
||||
"redirectURL": stateObj.RedirectURL,
|
||||
"organizationName": constants.ORGANIZATION_NAME,
|
||||
"organizationLogo": constants.ORGANIZATION_LOGO,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@@ -19,7 +19,7 @@ import (
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
func processGoogleUserInfo(code string, c *gin.Context) error {
|
||||
func processGoogleUserInfo(code string, role string, c *gin.Context) error {
|
||||
token, err := oauth.OAuthProvider.GoogleConfig.Exchange(oauth2.NoContext, code)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid google exchange code: %s", err.Error())
|
||||
@@ -50,6 +50,7 @@ func processGoogleUserInfo(code string, c *gin.Context) error {
|
||||
if err != nil {
|
||||
// user not registered, register user and generate session token
|
||||
user.SignupMethod = enum.Google.String()
|
||||
user.Roles = role
|
||||
} else {
|
||||
// user exists in db, check if method was google
|
||||
// if not append google to existing signup method and save it
|
||||
@@ -60,27 +61,26 @@ func processGoogleUserInfo(code string, c *gin.Context) error {
|
||||
}
|
||||
user.SignupMethod = signupMethod
|
||||
user.Password = existingUser.Password
|
||||
if !utils.IsValidRole(strings.Split(existingUser.Roles, ","), role) {
|
||||
return fmt.Errorf("invalid role")
|
||||
}
|
||||
|
||||
user.Roles = existingUser.Roles
|
||||
}
|
||||
|
||||
user, _ = db.Mgr.SaveUser(user)
|
||||
user, _ = db.Mgr.GetUserByEmail(user.Email)
|
||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||
|
||||
refreshToken, _, _ := utils.CreateAuthToken(utils.UserAuthInfo{
|
||||
ID: userIdStr,
|
||||
Email: user.Email,
|
||||
}, enum.RefreshToken)
|
||||
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, role)
|
||||
|
||||
accessToken, _, _ := utils.CreateAuthToken(utils.UserAuthInfo{
|
||||
ID: userIdStr,
|
||||
Email: user.Email,
|
||||
}, enum.AccessToken)
|
||||
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, role)
|
||||
utils.SetCookie(c, accessToken)
|
||||
session.SetToken(userIdStr, refreshToken)
|
||||
return nil
|
||||
}
|
||||
|
||||
func processGithubUserInfo(code string, c *gin.Context) error {
|
||||
func processGithubUserInfo(code string, role string, c *gin.Context) error {
|
||||
token, err := oauth.OAuthProvider.GithubConfig.Exchange(oauth2.NoContext, code)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid github exchange code: %s", err.Error())
|
||||
@@ -128,6 +128,7 @@ func processGithubUserInfo(code string, c *gin.Context) error {
|
||||
if err != nil {
|
||||
// user not registered, register user and generate session token
|
||||
user.SignupMethod = enum.Github.String()
|
||||
user.Roles = role
|
||||
} else {
|
||||
// user exists in db, check if method was google
|
||||
// if not append google to existing signup method and save it
|
||||
@@ -138,26 +139,26 @@ func processGithubUserInfo(code string, c *gin.Context) error {
|
||||
}
|
||||
user.SignupMethod = signupMethod
|
||||
user.Password = existingUser.Password
|
||||
|
||||
if !utils.IsValidRole(strings.Split(existingUser.Roles, ","), role) {
|
||||
return fmt.Errorf("invalid role")
|
||||
}
|
||||
|
||||
user.Roles = existingUser.Roles
|
||||
}
|
||||
|
||||
user, _ = db.Mgr.SaveUser(user)
|
||||
user, _ = db.Mgr.GetUserByEmail(user.Email)
|
||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||
refreshToken, _, _ := utils.CreateAuthToken(utils.UserAuthInfo{
|
||||
ID: userIdStr,
|
||||
Email: user.Email,
|
||||
}, enum.RefreshToken)
|
||||
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, role)
|
||||
|
||||
accessToken, _, _ := utils.CreateAuthToken(utils.UserAuthInfo{
|
||||
ID: userIdStr,
|
||||
Email: user.Email,
|
||||
}, enum.AccessToken)
|
||||
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, role)
|
||||
utils.SetCookie(c, accessToken)
|
||||
session.SetToken(userIdStr, refreshToken)
|
||||
return nil
|
||||
}
|
||||
|
||||
func processFacebookUserInfo(code string, c *gin.Context) error {
|
||||
func processFacebookUserInfo(code string, role string, c *gin.Context) error {
|
||||
token, err := oauth.OAuthProvider.FacebookConfig.Exchange(oauth2.NoContext, code)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid facebook exchange code: %s", err.Error())
|
||||
@@ -199,6 +200,7 @@ func processFacebookUserInfo(code string, c *gin.Context) error {
|
||||
if err != nil {
|
||||
// user not registered, register user and generate session token
|
||||
user.SignupMethod = enum.Github.String()
|
||||
user.Roles = role
|
||||
} else {
|
||||
// user exists in db, check if method was google
|
||||
// if not append google to existing signup method and save it
|
||||
@@ -209,20 +211,20 @@ func processFacebookUserInfo(code string, c *gin.Context) error {
|
||||
}
|
||||
user.SignupMethod = signupMethod
|
||||
user.Password = existingUser.Password
|
||||
|
||||
if !utils.IsValidRole(strings.Split(existingUser.Roles, ","), role) {
|
||||
return fmt.Errorf("invalid role")
|
||||
}
|
||||
|
||||
user.Roles = existingUser.Roles
|
||||
}
|
||||
|
||||
user, _ = db.Mgr.SaveUser(user)
|
||||
user, _ = db.Mgr.GetUserByEmail(user.Email)
|
||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||
refreshToken, _, _ := utils.CreateAuthToken(utils.UserAuthInfo{
|
||||
ID: userIdStr,
|
||||
Email: user.Email,
|
||||
}, enum.RefreshToken)
|
||||
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, role)
|
||||
|
||||
accessToken, _, _ := utils.CreateAuthToken(utils.UserAuthInfo{
|
||||
ID: userIdStr,
|
||||
Email: user.Email,
|
||||
}, enum.AccessToken)
|
||||
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, role)
|
||||
utils.SetCookie(c, accessToken)
|
||||
session.SetToken(userIdStr, refreshToken)
|
||||
return nil
|
||||
@@ -238,23 +240,27 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
||||
c.JSON(400, gin.H{"error": "invalid oauth state"})
|
||||
}
|
||||
session.DeleteToken(sessionState)
|
||||
// contains random token, redirect url, role
|
||||
sessionSplit := strings.Split(state, "___")
|
||||
|
||||
// TODO validate redirect url
|
||||
if len(sessionSplit) != 2 {
|
||||
if len(sessionSplit) < 2 {
|
||||
c.JSON(400, gin.H{"error": "invalid redirect url"})
|
||||
return
|
||||
}
|
||||
|
||||
role := sessionSplit[2]
|
||||
redirectURL := sessionSplit[1]
|
||||
|
||||
var err error
|
||||
code := c.Request.FormValue("code")
|
||||
switch provider {
|
||||
case enum.Google.String():
|
||||
err = processGoogleUserInfo(code, c)
|
||||
err = processGoogleUserInfo(code, role, c)
|
||||
case enum.Github.String():
|
||||
err = processGithubUserInfo(code, c)
|
||||
err = processGithubUserInfo(code, role, c)
|
||||
case enum.Facebook.String():
|
||||
err = processFacebookUserInfo(code, c)
|
||||
err = processFacebookUserInfo(code, role, c)
|
||||
default:
|
||||
err = fmt.Errorf(`invalid oauth provider`)
|
||||
}
|
||||
@@ -263,6 +269,6 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
||||
c.JSON(400, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.Redirect(http.StatusTemporaryRedirect, sessionSplit[1])
|
||||
c.Redirect(http.StatusTemporaryRedirect, redirectURL)
|
||||
}
|
||||
}
|
||||
|
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/authorizerdev/authorizer/server/enum"
|
||||
"github.com/authorizerdev/authorizer/server/oauth"
|
||||
"github.com/authorizerdev/authorizer/server/session"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
@@ -17,6 +18,7 @@ func OAuthLoginHandler() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// TODO validate redirect URL
|
||||
redirectURL := c.Query("redirectURL")
|
||||
role := c.Query("role")
|
||||
|
||||
if redirectURL == "" {
|
||||
c.JSON(400, gin.H{
|
||||
@@ -24,8 +26,21 @@ func OAuthLoginHandler() gin.HandlerFunc {
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if role != "" {
|
||||
// validate role
|
||||
if !utils.IsValidRole(constants.ROLES, role) {
|
||||
c.JSON(400, gin.H{
|
||||
"error": "invalid role",
|
||||
})
|
||||
return
|
||||
}
|
||||
} else {
|
||||
role = constants.DEFAULT_ROLE
|
||||
}
|
||||
|
||||
uuid := uuid.New()
|
||||
oauthStateString := uuid.String() + "___" + redirectURL
|
||||
oauthStateString := uuid.String() + "___" + redirectURL + "___" + role
|
||||
|
||||
provider := c.Param("oauth_provider")
|
||||
|
||||
|
@@ -50,15 +50,9 @@ func VerifyEmailHandler() gin.HandlerFunc {
|
||||
db.Mgr.DeleteToken(claim.Email)
|
||||
|
||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||
refreshToken, _, _ := utils.CreateAuthToken(utils.UserAuthInfo{
|
||||
ID: userIdStr,
|
||||
Email: user.Email,
|
||||
}, enum.RefreshToken)
|
||||
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, user.Roles)
|
||||
|
||||
accessToken, _, _ := utils.CreateAuthToken(utils.UserAuthInfo{
|
||||
ID: userIdStr,
|
||||
Email: user.Email,
|
||||
}, enum.AccessToken)
|
||||
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, user.Roles)
|
||||
|
||||
session.SetToken(userIdStr, refreshToken)
|
||||
utils.SetCookie(c, accessToken)
|
||||
|
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/authorizerdev/authorizer/server/handlers"
|
||||
"github.com/authorizerdev/authorizer/server/oauth"
|
||||
"github.com/authorizerdev/authorizer/server/session"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
"github.com/gin-contrib/location"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -50,6 +51,7 @@ func main() {
|
||||
db.InitDB()
|
||||
session.InitSession()
|
||||
oauth.InitOAuth()
|
||||
utils.InitServer()
|
||||
|
||||
r := gin.Default()
|
||||
r.Use(location.Default())
|
||||
|
127
server/resolvers/adminUpdateUser.go
Normal file
127
server/resolvers/adminUpdateUser.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package resolvers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/enum"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/session"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
)
|
||||
|
||||
func AdminUpdateUser(ctx context.Context, params model.AdminUpdateUserInput) (*model.User, error) {
|
||||
gc, err := utils.GinContextFromContext(ctx)
|
||||
var res *model.User
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
if !utils.IsSuperAdmin(gc) {
|
||||
return res, fmt.Errorf("unauthorized")
|
||||
}
|
||||
|
||||
if params.FirstName == nil && params.LastName == nil && params.Image == nil && params.Email == nil && params.Roles == nil {
|
||||
return res, fmt.Errorf("please enter atleast one param to update")
|
||||
}
|
||||
|
||||
user, err := db.Mgr.GetUserByID(params.ID)
|
||||
if err != nil {
|
||||
return res, fmt.Errorf(`User not found`)
|
||||
}
|
||||
|
||||
if params.FirstName != nil && user.FirstName != *params.FirstName {
|
||||
user.FirstName = *params.FirstName
|
||||
}
|
||||
|
||||
if params.LastName != nil && user.LastName != *params.LastName {
|
||||
user.LastName = *params.LastName
|
||||
}
|
||||
|
||||
if params.Image != nil && user.Image != *params.Image {
|
||||
user.Image = *params.Image
|
||||
}
|
||||
|
||||
if params.Email != nil && user.Email != *params.Email {
|
||||
// check if valid email
|
||||
if !utils.IsValidEmail(*params.Email) {
|
||||
return res, fmt.Errorf("invalid email address")
|
||||
}
|
||||
newEmail := strings.ToLower(*params.Email)
|
||||
// check if user with new email exists
|
||||
_, err = db.Mgr.GetUserByEmail(newEmail)
|
||||
// err = nil means user exists
|
||||
if err == nil {
|
||||
return res, fmt.Errorf("user with this email address already exists")
|
||||
}
|
||||
|
||||
session.DeleteToken(fmt.Sprintf("%v", user.ID))
|
||||
utils.DeleteCookie(gc)
|
||||
|
||||
user.Email = newEmail
|
||||
user.EmailVerifiedAt = 0
|
||||
// insert verification request
|
||||
verificationType := enum.UpdateEmail.String()
|
||||
token, err := utils.CreateVerificationToken(newEmail, verificationType)
|
||||
if err != nil {
|
||||
log.Println(`Error generating token`, err)
|
||||
}
|
||||
db.Mgr.AddVerification(db.VerificationRequest{
|
||||
Token: token,
|
||||
Identifier: verificationType,
|
||||
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
||||
Email: newEmail,
|
||||
})
|
||||
|
||||
// exec it as go routin so that we can reduce the api latency
|
||||
go func() {
|
||||
utils.SendVerificationMail(newEmail, token)
|
||||
}()
|
||||
}
|
||||
|
||||
rolesToSave := ""
|
||||
if params.Roles != nil && len(params.Roles) > 0 {
|
||||
currentRoles := strings.Split(user.Roles, ",")
|
||||
inputRoles := []string{}
|
||||
for _, item := range params.Roles {
|
||||
inputRoles = append(inputRoles, *item)
|
||||
}
|
||||
|
||||
if !utils.IsValidRolesArray(inputRoles) {
|
||||
return res, fmt.Errorf("invalid list of roles")
|
||||
}
|
||||
|
||||
if !utils.IsStringArrayEqual(inputRoles, currentRoles) {
|
||||
rolesToSave = strings.Join(inputRoles, ",")
|
||||
}
|
||||
|
||||
session.DeleteToken(fmt.Sprintf("%v", user.ID))
|
||||
utils.DeleteCookie(gc)
|
||||
}
|
||||
|
||||
if rolesToSave != "" {
|
||||
user.Roles = rolesToSave
|
||||
}
|
||||
|
||||
user, err = db.Mgr.UpdateUser(user)
|
||||
if err != nil {
|
||||
log.Println("Error updating user:", err)
|
||||
return res, err
|
||||
}
|
||||
|
||||
res = &model.User{
|
||||
ID: params.ID,
|
||||
Email: user.Email,
|
||||
Image: &user.Image,
|
||||
FirstName: &user.FirstName,
|
||||
LastName: &user.LastName,
|
||||
Roles: strings.Split(user.Roles, ","),
|
||||
CreatedAt: &user.CreatedAt,
|
||||
UpdatedAt: &user.UpdatedAt,
|
||||
}
|
||||
return res, nil
|
||||
}
|
@@ -46,16 +46,19 @@ func Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, e
|
||||
log.Println("Compare password error:", err)
|
||||
return res, fmt.Errorf(`invalid password`)
|
||||
}
|
||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||
refreshToken, _, _ := utils.CreateAuthToken(utils.UserAuthInfo{
|
||||
ID: userIdStr,
|
||||
Email: user.Email,
|
||||
}, enum.RefreshToken)
|
||||
role := constants.DEFAULT_ROLE
|
||||
if params.Role != nil {
|
||||
// validate role
|
||||
if !utils.IsValidRole(strings.Split(user.Roles, ","), *params.Role) {
|
||||
return res, fmt.Errorf(`invalid role`)
|
||||
}
|
||||
|
||||
accessToken, expiresAt, _ := utils.CreateAuthToken(utils.UserAuthInfo{
|
||||
ID: userIdStr,
|
||||
Email: user.Email,
|
||||
}, enum.AccessToken)
|
||||
role = *params.Role
|
||||
}
|
||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, role)
|
||||
|
||||
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, role)
|
||||
|
||||
session.SetToken(userIdStr, refreshToken)
|
||||
|
||||
@@ -71,6 +74,7 @@ func Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, e
|
||||
LastName: &user.LastName,
|
||||
SignupMethod: user.SignupMethod,
|
||||
EmailVerifiedAt: &user.EmailVerifiedAt,
|
||||
Roles: strings.Split(user.Roles, ","),
|
||||
CreatedAt: &user.CreatedAt,
|
||||
UpdatedAt: &user.UpdatedAt,
|
||||
},
|
||||
|
@@ -2,6 +2,7 @@ package resolvers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/session"
|
||||
@@ -25,7 +26,8 @@ func Logout(ctx context.Context) (*model.Response, error) {
|
||||
return res, err
|
||||
}
|
||||
|
||||
session.DeleteToken(claim.ID)
|
||||
userId := fmt.Sprintf("%v", claim["id"])
|
||||
session.DeleteToken(userId)
|
||||
res = &model.Response{
|
||||
Message: "Logged out successfully",
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@ package resolvers
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
@@ -27,13 +28,15 @@ func Profile(ctx context.Context) (*model.User, error) {
|
||||
return res, err
|
||||
}
|
||||
|
||||
sessionToken := session.GetToken(claim.ID)
|
||||
userID := fmt.Sprintf("%v", claim["id"])
|
||||
email := fmt.Sprintf("%v", claim["email"])
|
||||
sessionToken := session.GetToken(userID)
|
||||
|
||||
if sessionToken == "" {
|
||||
return res, fmt.Errorf(`unauthorized`)
|
||||
}
|
||||
|
||||
user, err := db.Mgr.GetUserByEmail(claim.Email)
|
||||
user, err := db.Mgr.GetUserByEmail(email)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
@@ -48,6 +51,7 @@ func Profile(ctx context.Context) (*model.User, error) {
|
||||
LastName: &user.LastName,
|
||||
SignupMethod: user.SignupMethod,
|
||||
EmailVerifiedAt: &user.EmailVerifiedAt,
|
||||
Roles: strings.Split(user.Roles, ","),
|
||||
CreatedAt: &user.CreatedAt,
|
||||
UpdatedAt: &user.UpdatedAt,
|
||||
}
|
||||
|
@@ -35,6 +35,20 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
|
||||
return res, fmt.Errorf(`invalid email address`)
|
||||
}
|
||||
|
||||
inputRoles := []string{}
|
||||
|
||||
if params.Roles != nil && len(params.Roles) > 0 {
|
||||
// check if roles exists
|
||||
for _, item := range params.Roles {
|
||||
inputRoles = append(inputRoles, *item)
|
||||
}
|
||||
if !utils.IsValidRolesArray(inputRoles) {
|
||||
return res, fmt.Errorf(`invalid roles`)
|
||||
}
|
||||
} else {
|
||||
inputRoles = []string{constants.DEFAULT_ROLE}
|
||||
}
|
||||
|
||||
// find user with email
|
||||
existingUser, err := db.Mgr.GetUserByEmail(params.Email)
|
||||
if err != nil {
|
||||
@@ -49,6 +63,8 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
|
||||
Email: params.Email,
|
||||
}
|
||||
|
||||
user.Roles = strings.Join(inputRoles, ",")
|
||||
|
||||
password, _ := utils.HashPassword(params.Password)
|
||||
user.Password = password
|
||||
|
||||
@@ -77,6 +93,7 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
|
||||
LastName: &user.LastName,
|
||||
SignupMethod: user.SignupMethod,
|
||||
EmailVerifiedAt: &user.EmailVerifiedAt,
|
||||
Roles: strings.Split(user.Roles, ","),
|
||||
CreatedAt: &user.CreatedAt,
|
||||
UpdatedAt: &user.UpdatedAt,
|
||||
}
|
||||
@@ -106,15 +123,9 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
|
||||
}
|
||||
} else {
|
||||
|
||||
refreshToken, _, _ := utils.CreateAuthToken(utils.UserAuthInfo{
|
||||
ID: userIdStr,
|
||||
Email: user.Email,
|
||||
}, enum.RefreshToken)
|
||||
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, constants.DEFAULT_ROLE)
|
||||
|
||||
accessToken, expiresAt, _ := utils.CreateAuthToken(utils.UserAuthInfo{
|
||||
ID: userIdStr,
|
||||
Email: user.Email,
|
||||
}, enum.AccessToken)
|
||||
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, constants.DEFAULT_ROLE)
|
||||
|
||||
session.SetToken(userIdStr, refreshToken)
|
||||
res = &model.AuthResponse{
|
||||
|
@@ -3,8 +3,10 @@ package resolvers
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/enum"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
@@ -12,7 +14,7 @@ import (
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
)
|
||||
|
||||
func Token(ctx context.Context) (*model.AuthResponse, error) {
|
||||
func Token(ctx context.Context, role *string) (*model.AuthResponse, error) {
|
||||
var res *model.AuthResponse
|
||||
|
||||
gc, err := utils.GinContextFromContext(ctx)
|
||||
@@ -25,13 +27,19 @@ func Token(ctx context.Context) (*model.AuthResponse, error) {
|
||||
}
|
||||
|
||||
claim, accessTokenErr := utils.VerifyAuthToken(token)
|
||||
expiresAt := claim.ExpiresAt
|
||||
expiresAt := claim["exp"].(int64)
|
||||
email := fmt.Sprintf("%v", claim["email"])
|
||||
|
||||
user, err := db.Mgr.GetUserByEmail(claim.Email)
|
||||
claimRole := fmt.Sprintf("%v", claim[constants.JWT_ROLE_CLAIM])
|
||||
user, err := db.Mgr.GetUserByEmail(email)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
if role != nil && *role != claimRole {
|
||||
return res, fmt.Errorf(`unauthorized`)
|
||||
}
|
||||
|
||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||
|
||||
sessionToken := session.GetToken(userIdStr)
|
||||
@@ -46,10 +54,7 @@ func Token(ctx context.Context) (*model.AuthResponse, error) {
|
||||
if accessTokenErr != nil || expiresTimeObj.Sub(currentTimeObj).Minutes() <= 5 {
|
||||
// if access token has expired and refresh/session token is valid
|
||||
// generate new accessToken
|
||||
token, expiresAt, _ = utils.CreateAuthToken(utils.UserAuthInfo{
|
||||
ID: userIdStr,
|
||||
Email: user.Email,
|
||||
}, enum.AccessToken)
|
||||
token, expiresAt, _ = utils.CreateAuthToken(user, enum.AccessToken, claimRole)
|
||||
}
|
||||
utils.SetCookie(gc, token)
|
||||
res = &model.AuthResponse{
|
||||
@@ -62,6 +67,7 @@ func Token(ctx context.Context) (*model.AuthResponse, error) {
|
||||
Image: &user.Image,
|
||||
FirstName: &user.FirstName,
|
||||
LastName: &user.LastName,
|
||||
Roles: strings.Split(user.Roles, ","),
|
||||
CreatedAt: &user.CreatedAt,
|
||||
UpdatedAt: &user.UpdatedAt,
|
||||
},
|
||||
|
@@ -32,7 +32,8 @@ func UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model
|
||||
return res, err
|
||||
}
|
||||
|
||||
sessionToken := session.GetToken(claim.ID)
|
||||
id := fmt.Sprintf("%v", claim["id"])
|
||||
sessionToken := session.GetToken(id)
|
||||
|
||||
if sessionToken == "" {
|
||||
return res, fmt.Errorf(`unauthorized`)
|
||||
@@ -43,7 +44,8 @@ func UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model
|
||||
return res, fmt.Errorf("please enter atleast one param to update")
|
||||
}
|
||||
|
||||
user, err := db.Mgr.GetUserByEmail(claim.Email)
|
||||
email := fmt.Sprintf("%v", claim["email"])
|
||||
user, err := db.Mgr.GetUserByEmail(email)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
@@ -120,9 +122,33 @@ func UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model
|
||||
go func() {
|
||||
utils.SendVerificationMail(newEmail, token)
|
||||
}()
|
||||
|
||||
}
|
||||
|
||||
// TODO this idea needs to be verified otherwise every user can make themselves super admin
|
||||
// rolesToSave := ""
|
||||
// if params.Roles != nil && len(params.Roles) > 0 {
|
||||
// currentRoles := strings.Split(user.Roles, ",")
|
||||
// inputRoles := []string{}
|
||||
// for _, item := range params.Roles {
|
||||
// inputRoles = append(inputRoles, *item)
|
||||
// }
|
||||
|
||||
// if !utils.IsValidRolesArray(inputRoles) {
|
||||
// return res, fmt.Errorf("invalid list of roles")
|
||||
// }
|
||||
|
||||
// if !utils.IsStringArrayEqual(inputRoles, currentRoles) {
|
||||
// rolesToSave = strings.Join(inputRoles, ",")
|
||||
// }
|
||||
|
||||
// session.DeleteToken(fmt.Sprintf("%v", user.ID))
|
||||
// utils.DeleteCookie(gc)
|
||||
// }
|
||||
|
||||
// if rolesToSave != "" {
|
||||
// user.Roles = rolesToSave
|
||||
// }
|
||||
|
||||
_, err = db.Mgr.UpdateUser(user)
|
||||
if err != nil {
|
||||
log.Println("Error updating user:", err)
|
||||
|
@@ -3,6 +3,7 @@ package resolvers
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
@@ -33,6 +34,7 @@ func Users(ctx context.Context) ([]*model.User, error) {
|
||||
FirstName: &users[i].FirstName,
|
||||
LastName: &users[i].LastName,
|
||||
EmailVerifiedAt: &users[i].EmailVerifiedAt,
|
||||
Roles: strings.Split(users[i].Roles, ","),
|
||||
CreatedAt: &users[i].CreatedAt,
|
||||
UpdatedAt: &users[i].UpdatedAt,
|
||||
})
|
||||
|
@@ -3,8 +3,10 @@ package resolvers
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/enum"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
@@ -41,15 +43,9 @@ func VerifyEmail(ctx context.Context, params model.VerifyEmailInput) (*model.Aut
|
||||
db.Mgr.DeleteToken(claim.Email)
|
||||
|
||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||
refreshToken, _, _ := utils.CreateAuthToken(utils.UserAuthInfo{
|
||||
ID: userIdStr,
|
||||
Email: user.Email,
|
||||
}, enum.RefreshToken)
|
||||
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, constants.DEFAULT_ROLE)
|
||||
|
||||
accessToken, expiresAt, _ := utils.CreateAuthToken(utils.UserAuthInfo{
|
||||
ID: userIdStr,
|
||||
Email: user.Email,
|
||||
}, enum.AccessToken)
|
||||
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, constants.DEFAULT_ROLE)
|
||||
|
||||
session.SetToken(userIdStr, refreshToken)
|
||||
|
||||
@@ -65,6 +61,7 @@ func VerifyEmail(ctx context.Context, params model.VerifyEmailInput) (*model.Aut
|
||||
LastName: &user.LastName,
|
||||
SignupMethod: user.SignupMethod,
|
||||
EmailVerifiedAt: &user.EmailVerifiedAt,
|
||||
Roles: strings.Split(user.Roles, ","),
|
||||
CreatedAt: &user.CreatedAt,
|
||||
UpdatedAt: &user.UpdatedAt,
|
||||
},
|
||||
|
@@ -1,29 +1,32 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/enum"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt"
|
||||
)
|
||||
|
||||
type UserAuthInfo struct {
|
||||
Email string `json:"email"`
|
||||
ID string `json:"id"`
|
||||
}
|
||||
// type UserAuthInfo struct {
|
||||
// Email string `json:"email"`
|
||||
// ID string `json:"id"`
|
||||
// }
|
||||
|
||||
type JWTCustomClaim map[string]interface{}
|
||||
|
||||
type UserAuthClaim struct {
|
||||
*jwt.StandardClaims
|
||||
TokenType string `json:"token_type"`
|
||||
UserAuthInfo
|
||||
*JWTCustomClaim `json:"authorizer"`
|
||||
}
|
||||
|
||||
func CreateAuthToken(user UserAuthInfo, tokenType enum.TokenType) (string, int64, error) {
|
||||
func CreateAuthToken(user db.User, tokenType enum.TokenType, role string) (string, int64, error) {
|
||||
t := jwt.New(jwt.GetSigningMethod(constants.JWT_TYPE))
|
||||
expiryBound := time.Hour
|
||||
if tokenType == enum.RefreshToken {
|
||||
@@ -33,12 +36,19 @@ func CreateAuthToken(user UserAuthInfo, tokenType enum.TokenType) (string, int64
|
||||
|
||||
expiresAt := time.Now().Add(expiryBound).Unix()
|
||||
|
||||
customClaims := JWTCustomClaim{
|
||||
"token_type": tokenType.String(),
|
||||
"email": user.Email,
|
||||
"id": user.ID,
|
||||
"allowed_roles": strings.Split(user.Roles, ","),
|
||||
constants.JWT_ROLE_CLAIM: role,
|
||||
}
|
||||
|
||||
t.Claims = &UserAuthClaim{
|
||||
&jwt.StandardClaims{
|
||||
ExpiresAt: expiresAt,
|
||||
},
|
||||
tokenType.String(),
|
||||
user,
|
||||
&customClaims,
|
||||
}
|
||||
|
||||
token, err := t.SignedString([]byte(constants.JWT_SECRET))
|
||||
@@ -63,14 +73,20 @@ func GetAuthToken(gc *gin.Context) (string, error) {
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func VerifyAuthToken(token string) (*UserAuthClaim, error) {
|
||||
func VerifyAuthToken(token string) (map[string]interface{}, error) {
|
||||
var res map[string]interface{}
|
||||
claims := &UserAuthClaim{}
|
||||
|
||||
_, err := jwt.ParseWithClaims(token, claims, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(constants.JWT_SECRET), nil
|
||||
})
|
||||
if err != nil {
|
||||
return claims, err
|
||||
return res, err
|
||||
}
|
||||
|
||||
return claims, nil
|
||||
data, _ := json.Marshal(claims.JWTCustomClaim)
|
||||
json.Unmarshal(data, &res)
|
||||
res["exp"] = claims.ExpiresAt
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
20
server/utils/common.go
Normal file
20
server/utils/common.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
func WriteToFile(filename string, data string) error {
|
||||
file, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
_, err = io.WriteString(file, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return file.Sync()
|
||||
}
|
@@ -16,17 +16,91 @@ func SendVerificationMail(toEmail, token string) error {
|
||||
|
||||
Subject := "Please verify your email"
|
||||
message := fmt.Sprintf(`
|
||||
<!DOCTYPE HTML PULBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html"; charset=ISO-8859-1">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Please verify your email by clicking on the link below </h1><br/>
|
||||
<a href="%s">Click here to verify</a>
|
||||
</body>
|
||||
</html>
|
||||
`, constants.AUTHORIZER_URL+"/verify_email"+"?token="+token)
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta content="width=device-width, initial-scale=1" name="viewport">
|
||||
<meta name="x-apple-disable-message-reformatting">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta content="telephone=no" name="format-detection">
|
||||
<title></title>
|
||||
<!--[if (mso 16)]>
|
||||
<style type="text/css">
|
||||
a {}
|
||||
</style>
|
||||
<![endif]-->
|
||||
<!--[if gte mso 9]><style>sup { font-size: 100%% !important; }</style><![endif]-->
|
||||
<!--[if gte mso 9]>
|
||||
<xml>
|
||||
<o:OfficeDocumentSettings>
|
||||
<o:AllowPNG></o:AllowPNG>
|
||||
<o:PixelsPerInch>96</o:PixelsPerInch>
|
||||
</o:OfficeDocumentSettings>
|
||||
</xml>
|
||||
<![endif]-->
|
||||
</head>
|
||||
|
||||
<body style="font-family: sans-serif;">
|
||||
<div class="es-wrapper-color">
|
||||
<!--[if gte mso 9]>
|
||||
<v:background xmlns:v="urn:schemas-microsoft-com:vml" fill="t">
|
||||
<v:fill type="tile" color="#ffffff"></v:fill>
|
||||
</v:background>
|
||||
<![endif]-->
|
||||
<table class="es-wrapper" width="100%%" cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-email-paddings" valign="top">
|
||||
<table class="es-content esd-footer-popover" cellspacing="0" cellpadding="0" align="center">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-stripe" align="center">
|
||||
<table class="es-content-body" style="border-left:1px solid transparent;border-right:1px solid transparent;border-top:1px solid transparent;border-bottom:1px solid transparent;padding:20px 0px;" width="600" cellspacing="0" cellpadding="0" bgcolor="#ffffff" align="center">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-structure es-p20t es-p40b es-p40r es-p40l" esd-custom-block-id="8537" align="left">
|
||||
<table width="100%%" cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-container-frame" width="518" align="left">
|
||||
<table width="100%%" cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-block-image es-m-txt-c es-p5b" style="font-size:0;padding:10px" align="center"><a target="_blank"><img src="%s" alt="icon" style="display: block;" title="icon" width="30"></a></td>
|
||||
</tr>
|
||||
|
||||
<tr style="background: rgb(249,250,251);padding: 10px;margin-bottom:10px;border-radius:5px;">
|
||||
<td class="esd-block-text es-m-txt-c es-p15t" align="center" style="padding:10px;padding-bottom:30px;">
|
||||
<p>Hey there 👋</p>
|
||||
<p>We received a request to sign-up for <b>%s</b>. If this is correct, please confirm your email address by clicking the button below.</p> <br/>
|
||||
<a href="%s" class="es-button" target="_blank" style="text-decoration: none;padding:10px 15px;background-color: rgba(59,130,246,1);color: #fff;font-size: 1em;border-radius:5px;">Confirm Email</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div style="position: absolute; left: -9999px; top: -9999px; margin: 0px;"></div>
|
||||
</body>
|
||||
</html>
|
||||
`, constants.ORGANIZATION_LOGO, constants.ORGANIZATION_NAME, constants.AUTHORIZER_URL+"/verify_email"+"?token="+token)
|
||||
bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message)
|
||||
|
||||
return sender.SendMail(Receiver, Subject, bodyMessage)
|
||||
@@ -46,17 +120,92 @@ func SendForgotPasswordMail(toEmail, token, host string) error {
|
||||
Subject := "Reset Password"
|
||||
|
||||
message := fmt.Sprintf(`
|
||||
<!DOCTYPE HTML PULBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html"; charset=ISO-8859-1">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Please use the link below to reset password </h1><br/>
|
||||
<a href="%s">Reset Password</a>
|
||||
</body>
|
||||
</html>
|
||||
`, constants.RESET_PASSWORD_URL+"?token="+token)
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta content="width=device-width, initial-scale=1" name="viewport">
|
||||
<meta name="x-apple-disable-message-reformatting">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta content="telephone=no" name="format-detection">
|
||||
<title></title>
|
||||
<!--[if (mso 16)]>
|
||||
<style type="text/css">
|
||||
a {}
|
||||
</style>
|
||||
<![endif]-->
|
||||
<!--[if gte mso 9]><style>sup { font-size: 100%% !important; }</style><![endif]-->
|
||||
<!--[if gte mso 9]>
|
||||
<xml>
|
||||
<o:OfficeDocumentSettings>
|
||||
<o:AllowPNG></o:AllowPNG>
|
||||
<o:PixelsPerInch>96</o:PixelsPerInch>
|
||||
</o:OfficeDocumentSettings>
|
||||
</xml>
|
||||
<![endif]-->
|
||||
</head>
|
||||
|
||||
<body style="font-family: sans-serif;">
|
||||
<div class="es-wrapper-color">
|
||||
<!--[if gte mso 9]>
|
||||
<v:background xmlns:v="urn:schemas-microsoft-com:vml" fill="t">
|
||||
<v:fill type="tile" color="#ffffff"></v:fill>
|
||||
</v:background>
|
||||
<![endif]-->
|
||||
<table class="es-wrapper" width="100%%" cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-email-paddings" valign="top">
|
||||
<table class="es-content esd-footer-popover" cellspacing="0" cellpadding="0" align="center">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-stripe" align="center">
|
||||
<table class="es-content-body" style="border-left:1px solid transparent;border-right:1px solid transparent;border-top:1px solid transparent;border-bottom:1px solid transparent;padding:20px 0px;" width="600" cellspacing="0" cellpadding="0" bgcolor="#ffffff" align="center">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-structure es-p20t es-p40b es-p40r es-p40l" esd-custom-block-id="8537" align="left">
|
||||
<table width="100%%" cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-container-frame" width="518" align="left">
|
||||
<table width="100%%" cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-block-image es-m-txt-c es-p5b" style="font-size:0;padding:10px" align="center"><a target="_blank"><img src="%s" alt="icon" style="display: block;" title="icon" width="30"></a></td>
|
||||
</tr>
|
||||
|
||||
<tr style="background: rgb(249,250,251);padding: 10px;margin-bottom:10px;border-radius:5px;">
|
||||
<td class="esd-block-text es-m-txt-c es-p15t" align="center" style="padding:10px;padding-bottom:30px;">
|
||||
<p>Hey there 👋</p>
|
||||
<p>We received a request to reset password for email: <b>%s</b>. If this is correct, please reset the password clicking the button below.</p> <br/>
|
||||
<a href="%s" class="es-button" target="_blank" style="text-decoration: none;padding:10px 15px;background-color: rgba(59,130,246,1);color: #fff;font-size: 1em;border-radius:5px;">Reset Password</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div style="position: absolute; left: -9999px; top: -9999px; margin: 0px;"></div>
|
||||
</body>
|
||||
</html>
|
||||
`, constants.ORGANIZATION_LOGO, toEmail, constants.RESET_PASSWORD_URL+"?token="+token)
|
||||
|
||||
bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message)
|
||||
|
||||
return sender.SendMail(Receiver, Subject, bodyMessage)
|
||||
|
25
server/utils/initServer.go
Normal file
25
server/utils/initServer.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
)
|
||||
|
||||
// any jobs that we want to run at start of server can be executed here
|
||||
|
||||
// 1. create roles table and add the roles list from env to table
|
||||
|
||||
func InitServer() {
|
||||
roles := []db.Role{}
|
||||
for _, val := range constants.ROLES {
|
||||
roles = append(roles, db.Role{
|
||||
Role: val,
|
||||
})
|
||||
}
|
||||
err := db.Mgr.SaveRoles(roles)
|
||||
if err != nil {
|
||||
log.Println(`Error saving roles`, err)
|
||||
}
|
||||
}
|
@@ -1,15 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func IsSuperAdmin(gc *gin.Context) bool {
|
||||
secret := gc.Request.Header.Get("x-authorizer-admin-secret")
|
||||
if secret == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
return secret == constants.ADMIN_SECRET
|
||||
}
|
@@ -5,6 +5,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func IsValidEmail(email string) bool {
|
||||
@@ -29,3 +30,52 @@ func IsValidRedirectURL(url string) bool {
|
||||
|
||||
return hasValidURL
|
||||
}
|
||||
|
||||
func IsSuperAdmin(gc *gin.Context) bool {
|
||||
secret := gc.Request.Header.Get("x-authorizer-admin-secret")
|
||||
if secret == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
return secret == constants.ADMIN_SECRET
|
||||
}
|
||||
|
||||
func IsValidRolesArray(roles []string) bool {
|
||||
valid := true
|
||||
currentRoleMap := map[string]bool{}
|
||||
|
||||
for _, currentRole := range constants.ROLES {
|
||||
currentRoleMap[currentRole] = true
|
||||
}
|
||||
for _, inputRole := range roles {
|
||||
if !currentRoleMap[inputRole] {
|
||||
valid = false
|
||||
break
|
||||
}
|
||||
}
|
||||
return valid
|
||||
}
|
||||
|
||||
func IsValidRole(userRoles []string, role string) bool {
|
||||
valid := false
|
||||
for _, currentRole := range userRoles {
|
||||
if role == currentRole {
|
||||
valid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return valid
|
||||
}
|
||||
|
||||
func IsStringArrayEqual(a, b []string) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i, v := range a {
|
||||
if v != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
Reference in New Issue
Block a user