Compare commits
21 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
8f7582e1ec | ||
![]() |
bdbbe4adee | ||
![]() |
65478296cb | ||
![]() |
2342f7c5c6 | ||
![]() |
8266c1cff5 | ||
![]() |
c662c625a0 | ||
![]() |
c989648327 | ||
![]() |
a933ac1118 | ||
![]() |
b8afe7abcc | ||
![]() |
3ab02cc4ff | ||
![]() |
bedc3d0b50 | ||
![]() |
1398762e1d | ||
![]() |
e0a77da773 | ||
![]() |
c3f4cd3bf9 | ||
![]() |
f110255310 | ||
![]() |
155d2e65c2 | ||
![]() |
4d341e9876 | ||
![]() |
1761f41691 | ||
![]() |
00565c8717 | ||
![]() |
74a551ae09 | ||
![]() |
cb5b02d777 |
4
.github/CONTRIBUTING.md
vendored
4
.github/CONTRIBUTING.md
vendored
@@ -10,7 +10,7 @@ We're so excited you're interested in helping with Authorizer! We are happy to h
|
||||
## Where to ask questions?
|
||||
|
||||
1. Check our [Github Issues](https://github.com/authorizerdev/authorizer/issues) to see if someone has already answered your question.
|
||||
2. Join our community on [Discord](https://discord.gg/WDvCxwkX) and feel free to ask us your questions
|
||||
2. Join our community on [Discord](https://discord.gg/Zv2D5h6kkK) and feel free to ask us your questions
|
||||
|
||||
As you gain experience with Authorizer, please help answer other people's questions! :pray:
|
||||
|
||||
@@ -19,7 +19,7 @@ As you gain experience with Authorizer, please help answer other people's questi
|
||||
You can get started by taking a look at our [Github issues](https://github.com/authorizerdev/authorizer/issues)
|
||||
If you find one that looks interesting and no one else is already working on it, comment on that issue and start contributing 🙂.
|
||||
|
||||
Please ask as many questions as you need, either directly in the issue or on [Discord](https://discord.gg/WDvCxwkX). We're happy to help!:raised_hands:
|
||||
Please ask as many questions as you need, either directly in the issue or on [Discord](https://discord.gg/Zv2D5h6kkK). We're happy to help!:raised_hands:
|
||||
|
||||
### Contributions that are ALWAYS welcome
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.16-alpine as builder
|
||||
FROM golang:1.17-alpine as builder
|
||||
WORKDIR /app
|
||||
COPY server server
|
||||
COPY Makefile .
|
||||
|
4
Makefile
4
Makefile
@@ -4,4 +4,6 @@ VERSION := $(or $(VERSION),$(DEFAULT_VERSION))
|
||||
cmd:
|
||||
cd server && go build -ldflags "-w -X main.Version=$(VERSION)" -o '../build/server'
|
||||
clean:
|
||||
rm -rf build
|
||||
rm -rf build
|
||||
test:
|
||||
cd server && go clean --testcache && go test -v ./...
|
24
README.md
24
README.md
@@ -7,7 +7,7 @@
|
||||
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.
|
||||
**Authorizer** is an open-source authentication and authorization solution for your applications. Bring your database and have complete control over the user information. You can self-host authorizer instances and connect to any database (Currently supports [Postgres](https://www.postgresql.org/), [MySQL](https://www.mysql.com/), [SQLite](https://www.sqlite.org/index.html), [SQLServer](https://www.microsoft.com/en-us/sql-server/), [MongoDB](https://mongodb.com/),[ArangoDB](https://www.arangodb.com/)).
|
||||
|
||||
## Table of contents
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
- [Getting Started](#getting-started)
|
||||
- [Contributing](https://github.com/authorizerdev/authorizer/blob/main/.github/CONTRIBUTING.md)
|
||||
- [Docs](http://docs.authorizer.dev/)
|
||||
- [Join Community](https://discord.gg/2fXUQN3E)
|
||||
- [Join Community](https://discord.gg/Zv2D5h6kkK)
|
||||
|
||||
# Introduction
|
||||
|
||||
@@ -66,6 +66,7 @@ This guide helps you practice using Authorizer to evaluate it before you use it
|
||||
- [Install using source code](#install-using-source-code)
|
||||
- [Install using binaries](#install-using-binaries)
|
||||
- [Install instance on heroku](#install-instance-on-Heroku)
|
||||
- [Install instance on railway.app](#install-instance-on-railway)
|
||||
|
||||
## Install using source code
|
||||
|
||||
@@ -134,12 +135,23 @@ Deploy Authorizer using [heroku](https://github.com/authorizerdev/authorizer-her
|
||||
<br/><br/>
|
||||
[](https://heroku.com/deploy?template=https://github.com/authorizerdev/authorizer-heroku)
|
||||
|
||||
# Install instance on railway
|
||||
|
||||
Deploy production ready Authorizer instance using [railway.app](https://github.com/authorizerdev/authorizer-railway) with postgres and redis for free and build with it in 30seconds
|
||||
<br/>
|
||||
|
||||
[](https://railway.app/new/template?template=https%3A%2F%2Fgithub.com%2Fauthorizerdev%2Fauthorizer-railway&plugins=postgresql%2Credis&envs=ENV%2CDATABASE_TYPE%2CADMIN_SECRET%2CCOOKIE_NAME%2CJWT_ROLE_CLAIM%2CJWT_TYPE%2CJWT_SECRET%2CFACEBOOK_CLIENT_ID%2CFACEBOOK_CLIENT_SECRET%2CGOOGLE_CLIENT_ID%2CGOOGLE_CLIENT_SECRET%2CGITHUB_CLIENT_ID%2CGITHUB_CLIENT_SECRET%2CALLOWED_ORIGINS%2CROLES%2CPROTECTED_ROLES%2CDEFAULT_ROLES&optionalEnvs=FACEBOOK_CLIENT_ID%2CFACEBOOK_CLIENT_SECRET%2CGOOGLE_CLIENT_ID%2CGOOGLE_CLIENT_SECRET%2CGITHUB_CLIENT_ID%2CGITHUB_CLIENT_SECRET%2CALLOWED_ORIGINS%2CROLES%2CPROTECTED_ROLES%2CDEFAULT_ROLES&ENVDesc=Deployment+environment&DATABASE_TYPEDesc=With+railway+we+are+deploying+postgres+db&ADMIN_SECRETDesc=Secret+to+access+the+admin+apis&COOKIE_NAMEDesc=Name+of+http+only+cookie+that+will+be+used+as+session&FACEBOOK_CLIENT_IDDesc=Facebook+client+ID+for+facebook+login&FACEBOOK_CLIENT_SECRETDesc=Facebook+client+secret+for+facebook+login&GOOGLE_CLIENT_IDDesc=Google+client+ID+for+google+login&GOOGLE_CLIENT_SECRETDesc=Google+client+secret+for+google+login&GITHUB_CLIENT_IDDesc=Github+client+ID+for+github+login&GITHUB_CLIENT_SECRETDesc=Github+client+secret+for+github+login&ALLOWED_ORIGINSDesc=Whitelist+the+URL+for+which+this+instance+of+authorizer+is+allowed&ROLESDesc=Comma+separated+list+of+roles+that+platform+supports.+Default+role+is+user&PROTECTED_ROLESDesc=Comma+separated+list+of+protected+roles+for+which+sign-up+is+disabled&DEFAULT_ROLESDesc=Default+role+that+should+be+assigned+to+user.+It+should+be+one+from+the+list+of+%60ROLES%60+env.+Default+role+is+user&JWT_ROLE_CLAIMDesc=JWT+key+to+be+used+to+validate+the+role+field.&JWT_TYPEDesc=JWT+encryption+type&JWT_SECRETDesc=Random+string+that+will+be+used+for+encrypting+the+JWT+token&ENVDefault=PRODUCTION&DATABASE_TYPEDefault=postgres&COOKIE_NAMEDefault=authorizer&JWT_TYPEDefault=HS256&JWT_ROLE_CLAIMDefault=role)
|
||||
|
||||
### Things to consider
|
||||
|
||||
- For social logins, you will need respective social platform key and secret
|
||||
- For having verified users, you will need an SMTP server with an email address and password using which system can send emails. The system will send a verification link to an email address. Once an email is verified then, only able to access it.
|
||||
> Note: One can always disable the email verification to allow open sign up, which is not recommended for production as anyone can use anyone's email address 😅
|
||||
- For persisting user sessions, you will need Redis URL. If you do not configure a Redis server, sessions will be persisted until the instance is up or not restarted. For better response time on authorization requests/middleware, we recommend deploying Redis on the same infra/network as your authorizer server.
|
||||
- For persisting user sessions, you will need Redis URL (not in case of railway.app). If you do not configure a Redis server, sessions will be persisted until the instance is up or not restarted. For better response time on authorization requests/middleware, we recommend deploying Redis on the same infra/network as your authorizer server.
|
||||
|
||||
## Testing
|
||||
|
||||
- Integration Test: This tests are written along with [authorizer-js](https://github.com/authorizerdev/authorizer-js/blob/main/__test__/index.test.js) lib.
|
||||
|
||||
## Integrating into your website
|
||||
|
||||
@@ -180,3 +192,9 @@ This example demonstrates how you can use [`@authorizerdev/authorizer-js`](/auth
|
||||
onLoad();
|
||||
</script>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Support my work
|
||||
|
||||
<a href="https://www.buymeacoffee.com/lakhansamani" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 60px !important;width: 217px !important;" ></a>
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
71
app/package-lock.json
generated
71
app/package-lock.json
generated
@@ -13,9 +13,9 @@
|
||||
}
|
||||
},
|
||||
"@authorizerdev/authorizer-react": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.1.0.tgz",
|
||||
"integrity": "sha512-+ro0CNKIvblEgRs5M0HevUYrXC6s433GinL7EubIL5XztN48GDBV2GI86lhl4WvjbYTZApZloXWczaZ51g8uig==",
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.2.0.tgz",
|
||||
"integrity": "sha512-1JICta0rpgDlncIcvilWcYKP8QNq2v0RlcYS6ZeYZGJ5bymFlF6NOdMjh6Hn2IFfSWtqEPQ6zU++kAQ8kei1AA==",
|
||||
"requires": {
|
||||
"@authorizerdev/authorizer-js": "^0.1.0",
|
||||
"final-form": "^4.20.2",
|
||||
@@ -32,9 +32,9 @@
|
||||
}
|
||||
},
|
||||
"@babel/generator": {
|
||||
"version": "7.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.0.tgz",
|
||||
"integrity": "sha512-RR8hUCfRQn9j9RPKEVXo9LiwoxLPYn6hNZlvUOR8tSnaxlD0p0+la00ZP9/SnRt6HchKr+X0fO2r8vrETiJGew==",
|
||||
"version": "7.16.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.5.tgz",
|
||||
"integrity": "sha512-kIvCdjZqcdKqoDbVVdt5R99icaRtrtYhYK/xux5qiWCBmfdvEYMFZ68QCrpE5cbFM1JsuArUNs1ZkuKtTtUcZA==",
|
||||
"requires": {
|
||||
"@babel/types": "^7.16.0",
|
||||
"jsesc": "^2.5.1",
|
||||
@@ -49,6 +49,14 @@
|
||||
"@babel/types": "^7.16.0"
|
||||
}
|
||||
},
|
||||
"@babel/helper-environment-visitor": {
|
||||
"version": "7.16.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.5.tgz",
|
||||
"integrity": "sha512-ODQyc5AnxmZWm/R2W7fzhamOk1ey8gSguo5SGvF0zcB3uUzRpTRmM/jmLSm9bDMyPlvbyJ+PwPEK0BWIoZ9wjg==",
|
||||
"requires": {
|
||||
"@babel/types": "^7.16.0"
|
||||
}
|
||||
},
|
||||
"@babel/helper-function-name": {
|
||||
"version": "7.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz",
|
||||
@@ -107,9 +115,9 @@
|
||||
}
|
||||
},
|
||||
"@babel/parser": {
|
||||
"version": "7.16.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.3.tgz",
|
||||
"integrity": "sha512-dcNwU1O4sx57ClvLBVFbEgx0UZWfd0JQX5X6fxFRCLHelFBGXFfSz6Y0FAq2PEwUqlqLkdVjVr4VASEOuUnLJw=="
|
||||
"version": "7.16.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.6.tgz",
|
||||
"integrity": "sha512-Gr86ujcNuPDnNOY8mi383Hvi8IYrJVJYuf3XcuBM/Dgd+bINn/7tHqsj+tKkoreMbmGsFLsltI/JJd8fOFWGDQ=="
|
||||
},
|
||||
"@babel/runtime": {
|
||||
"version": "7.14.8",
|
||||
@@ -130,16 +138,17 @@
|
||||
}
|
||||
},
|
||||
"@babel/traverse": {
|
||||
"version": "7.16.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.3.tgz",
|
||||
"integrity": "sha512-eolumr1vVMjqevCpwVO99yN/LoGL0EyHiLO5I043aYQvwOJ9eR5UsZSClHVCzfhBduMAsSzgA/6AyqPjNayJag==",
|
||||
"version": "7.16.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.5.tgz",
|
||||
"integrity": "sha512-FOCODAzqUMROikDYLYxl4nmwiLlu85rNqBML/A5hKRVXG2LV8d0iMqgPzdYTcIpjZEBB7D6UDU9vxRZiriASdQ==",
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.16.0",
|
||||
"@babel/generator": "^7.16.0",
|
||||
"@babel/generator": "^7.16.5",
|
||||
"@babel/helper-environment-visitor": "^7.16.5",
|
||||
"@babel/helper-function-name": "^7.16.0",
|
||||
"@babel/helper-hoist-variables": "^7.16.0",
|
||||
"@babel/helper-split-export-declaration": "^7.16.0",
|
||||
"@babel/parser": "^7.16.3",
|
||||
"@babel/parser": "^7.16.5",
|
||||
"@babel/types": "^7.16.0",
|
||||
"debug": "^4.1.0",
|
||||
"globals": "^11.1.0"
|
||||
@@ -241,12 +250,12 @@
|
||||
}
|
||||
},
|
||||
"babel-plugin-styled-components": {
|
||||
"version": "1.13.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-1.13.3.tgz",
|
||||
"integrity": "sha512-meGStRGv+VuKA/q0/jXxrPNWEm4LPfYIqxooDTdmh8kFsP/Ph7jJG5rUPwUPX3QHUvggwdbgdGpo88P/rRYsVw==",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.0.2.tgz",
|
||||
"integrity": "sha512-7eG5NE8rChnNTDxa6LQfynwgHTVOYYaHJbUYSlOhk8QBXIQiMBKq4gyfHBBKPrxUcVBXVJL61ihduCpCQbuNbw==",
|
||||
"requires": {
|
||||
"@babel/helper-annotate-as-pure": "^7.15.4",
|
||||
"@babel/helper-module-imports": "^7.15.4",
|
||||
"@babel/helper-annotate-as-pure": "^7.16.0",
|
||||
"@babel/helper-module-imports": "^7.16.0",
|
||||
"babel-plugin-syntax-jsx": "^6.18.0",
|
||||
"lodash": "^4.17.11"
|
||||
}
|
||||
@@ -305,9 +314,9 @@
|
||||
"integrity": "sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw=="
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
|
||||
"integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
|
||||
"integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
|
||||
"requires": {
|
||||
"ms": "2.1.2"
|
||||
}
|
||||
@@ -323,9 +332,9 @@
|
||||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
|
||||
},
|
||||
"final-form": {
|
||||
"version": "4.20.4",
|
||||
"resolved": "https://registry.npmjs.org/final-form/-/final-form-4.20.4.tgz",
|
||||
"integrity": "sha512-hyoOVVilPLpkTvgi+FSJkFZrh0Yhy4BhE6lk/NiBwrF4aRV8/ykKEyXYvQH/pfUbRkOosvpESYouFb+FscsLrw==",
|
||||
"version": "4.20.6",
|
||||
"resolved": "https://registry.npmjs.org/final-form/-/final-form-4.20.6.tgz",
|
||||
"integrity": "sha512-fCdwIj49KOaFfDRlXB57Eo+GghIMZQWrA9TakQI3C9uQxHwaFHXqZSNRlUdfnQmNNeySwGOaGPZCvjy58hyv4w==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.10.0"
|
||||
}
|
||||
@@ -432,9 +441,9 @@
|
||||
}
|
||||
},
|
||||
"postcss-value-parser": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz",
|
||||
"integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ=="
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
|
||||
},
|
||||
"prop-types": {
|
||||
"version": "15.7.2",
|
||||
@@ -481,9 +490,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.16.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.3.tgz",
|
||||
"integrity": "sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ==",
|
||||
"version": "7.16.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.5.tgz",
|
||||
"integrity": "sha512-TXWihFIS3Pyv5hzR7j6ihmeLkZfrXGxAr5UfSl8CHf+6q/wpiYDkUau0czckpYG8QmnCIuPpdLtuA9VmuGGyMA==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
|
@@ -10,7 +10,7 @@
|
||||
"author": "Lakhan Samani",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@authorizerdev/authorizer-react": "^0.1.0",
|
||||
"@authorizerdev/authorizer-react": "^0.2.0",
|
||||
"@types/react": "^17.0.15",
|
||||
"@types/react-dom": "^17.0.9",
|
||||
"esbuild": "^0.12.17",
|
||||
|
@@ -3,9 +3,11 @@ package constants
|
||||
var (
|
||||
ADMIN_SECRET = ""
|
||||
ENV = ""
|
||||
ENV_PATH = ""
|
||||
VERSION = ""
|
||||
DATABASE_TYPE = ""
|
||||
DATABASE_URL = ""
|
||||
DATABASE_NAME = ""
|
||||
SMTP_HOST = ""
|
||||
SMTP_PORT = ""
|
||||
SENDER_EMAIL = ""
|
||||
@@ -14,14 +16,15 @@ var (
|
||||
JWT_SECRET = ""
|
||||
ALLOWED_ORIGINS = []string{}
|
||||
AUTHORIZER_URL = ""
|
||||
APP_URL = ""
|
||||
PORT = "8080"
|
||||
REDIS_URL = ""
|
||||
IS_PROD = false
|
||||
COOKIE_NAME = ""
|
||||
RESET_PASSWORD_URL = ""
|
||||
DISABLE_EMAIL_VERIFICATION = "false"
|
||||
DISABLE_BASIC_AUTHENTICATION = "false"
|
||||
DISABLE_MAGIC_LOGIN = "false"
|
||||
DISABLE_EMAIL_VERIFICATION = false
|
||||
DISABLE_BASIC_AUTHENTICATION = false
|
||||
DISABLE_MAGIC_LOGIN = false
|
||||
|
||||
// ROLES
|
||||
ROLES = []string{}
|
||||
|
@@ -2,6 +2,7 @@ package constants
|
||||
|
||||
var (
|
||||
// Ref: https://github.com/qor/auth/blob/master/providers/google/google.go
|
||||
// deprecated and not used. instead we follow open id approach for google login
|
||||
GoogleUserInfoURL = "https://www.googleapis.com/oauth2/v3/userinfo"
|
||||
// Ref: https://github.com/qor/auth/blob/master/providers/facebook/facebook.go#L18
|
||||
FacebookUserInfoURL = "https://graph.facebook.com/me?fields=id,first_name,last_name,name,email,picture&access_token="
|
||||
|
112
server/db/arangodb.go
Normal file
112
server/db/arangodb.go
Normal file
@@ -0,0 +1,112 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
"github.com/arangodb/go-driver"
|
||||
arangoDriver "github.com/arangodb/go-driver"
|
||||
"github.com/arangodb/go-driver/http"
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
)
|
||||
|
||||
// for this we need arangodb instance up and running
|
||||
// for local testing we can use dockerized version of it
|
||||
// docker run -p 8529:8529 -e ARANGO_ROOT_PASSWORD=root arangodb/arangodb:3.8.4
|
||||
|
||||
func initArangodb() (arangoDriver.Database, error) {
|
||||
ctx := context.Background()
|
||||
conn, err := http.NewConnection(http.ConnectionConfig{
|
||||
Endpoints: []string{constants.DATABASE_URL},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
arangoClient, err := arangoDriver.NewClient(arangoDriver.ClientConfig{
|
||||
Connection: conn,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var arangodb driver.Database
|
||||
|
||||
arangodb_exists, err := arangoClient.DatabaseExists(nil, constants.DATABASE_NAME)
|
||||
|
||||
if arangodb_exists {
|
||||
log.Println(constants.DATABASE_NAME + " db exists already")
|
||||
|
||||
arangodb, err = arangoClient.Database(nil, constants.DATABASE_NAME)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
} else {
|
||||
arangodb, err = arangoClient.CreateDatabase(nil, constants.DATABASE_NAME, nil)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
userCollectionExists, err := arangodb.CollectionExists(ctx, Collections.User)
|
||||
if userCollectionExists {
|
||||
log.Println(Collections.User + " collection exists already")
|
||||
} else {
|
||||
_, err = arangodb.CreateCollection(ctx, Collections.User, nil)
|
||||
if err != nil {
|
||||
log.Println("error creating collection("+Collections.User+"):", err)
|
||||
}
|
||||
}
|
||||
userCollection, _ := arangodb.Collection(nil, Collections.User)
|
||||
userCollection.EnsureHashIndex(ctx, []string{"id"}, &arangoDriver.EnsureHashIndexOptions{
|
||||
Unique: true,
|
||||
Sparse: true,
|
||||
})
|
||||
userCollection.EnsureHashIndex(ctx, []string{"email"}, &arangoDriver.EnsureHashIndexOptions{
|
||||
Unique: true,
|
||||
Sparse: true,
|
||||
})
|
||||
|
||||
verificationRequestCollectionExists, err := arangodb.CollectionExists(ctx, Collections.VerificationRequest)
|
||||
if verificationRequestCollectionExists {
|
||||
log.Println(Collections.VerificationRequest + " collection exists already")
|
||||
} else {
|
||||
_, err = arangodb.CreateCollection(ctx, Collections.VerificationRequest, nil)
|
||||
if err != nil {
|
||||
log.Println("error creating collection("+Collections.VerificationRequest+"):", err)
|
||||
}
|
||||
}
|
||||
verificationRequestCollection, _ := arangodb.Collection(nil, Collections.VerificationRequest)
|
||||
verificationRequestCollection.EnsureHashIndex(ctx, []string{"id"}, &arangoDriver.EnsureHashIndexOptions{
|
||||
Unique: true,
|
||||
Sparse: true,
|
||||
})
|
||||
verificationRequestCollection.EnsureHashIndex(ctx, []string{"email", "identifier"}, &arangoDriver.EnsureHashIndexOptions{
|
||||
Unique: true,
|
||||
Sparse: true,
|
||||
})
|
||||
verificationRequestCollection.EnsureHashIndex(ctx, []string{"token"}, &arangoDriver.EnsureHashIndexOptions{
|
||||
Sparse: true,
|
||||
})
|
||||
|
||||
sessionCollectionExists, err := arangodb.CollectionExists(ctx, Collections.Session)
|
||||
if sessionCollectionExists {
|
||||
log.Println(Collections.Session + " collection exists already")
|
||||
} else {
|
||||
_, err = arangodb.CreateCollection(ctx, Collections.Session, nil)
|
||||
if err != nil {
|
||||
log.Println("error creating collection("+Collections.Session+"):", err)
|
||||
}
|
||||
}
|
||||
|
||||
sessionCollection, _ := arangodb.Collection(nil, Collections.Session)
|
||||
sessionCollection.EnsureHashIndex(ctx, []string{"id"}, &arangoDriver.EnsureHashIndexOptions{
|
||||
Unique: true,
|
||||
Sparse: true,
|
||||
})
|
||||
|
||||
return arangodb, err
|
||||
}
|
114
server/db/db.go
114
server/db/db.go
@@ -3,62 +3,126 @@ package db
|
||||
import (
|
||||
"log"
|
||||
|
||||
arangoDriver "github.com/arangodb/go-driver"
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/enum"
|
||||
"github.com/google/uuid"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/driver/sqlserver"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/schema"
|
||||
)
|
||||
|
||||
type Manager interface {
|
||||
SaveUser(user User) (User, error)
|
||||
AddUser(user User) (User, error)
|
||||
UpdateUser(user User) (User, error)
|
||||
DeleteUser(user User) error
|
||||
GetUsers() ([]User, error)
|
||||
GetUserByEmail(email string) (User, 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
|
||||
DeleteVerificationRequest(verificationRequest VerificationRequest) error
|
||||
GetVerificationRequests() ([]VerificationRequest, error)
|
||||
GetVerificationByEmail(email string) (VerificationRequest, error)
|
||||
DeleteUser(email string) error
|
||||
SaveRoles(roles []Role) error
|
||||
SaveSession(session Session) error
|
||||
AddSession(session Session) error
|
||||
}
|
||||
|
||||
type manager struct {
|
||||
db *gorm.DB
|
||||
sqlDB *gorm.DB
|
||||
arangodb arangoDriver.Database
|
||||
mongodb *mongo.Database
|
||||
}
|
||||
|
||||
var Mgr Manager
|
||||
// mainly used by nosql dbs
|
||||
type CollectionList struct {
|
||||
User string
|
||||
VerificationRequest string
|
||||
Session string
|
||||
}
|
||||
|
||||
var (
|
||||
IsORMSupported bool
|
||||
IsArangoDB bool
|
||||
IsMongoDB bool
|
||||
Mgr Manager
|
||||
Prefix = "authorizer_"
|
||||
Collections = CollectionList{
|
||||
User: Prefix + "users",
|
||||
VerificationRequest: Prefix + "verification_requests",
|
||||
Session: Prefix + "sessions",
|
||||
}
|
||||
)
|
||||
|
||||
func InitDB() {
|
||||
var db *gorm.DB
|
||||
var sqlDB *gorm.DB
|
||||
var err error
|
||||
|
||||
IsORMSupported = constants.DATABASE_TYPE != enum.Arangodb.String() && constants.DATABASE_TYPE != enum.Mongodb.String()
|
||||
IsArangoDB = constants.DATABASE_TYPE == enum.Arangodb.String()
|
||||
IsMongoDB = constants.DATABASE_TYPE == enum.Mongodb.String()
|
||||
|
||||
// sql db orm config
|
||||
ormConfig := &gorm.Config{
|
||||
NamingStrategy: schema.NamingStrategy{
|
||||
TablePrefix: "authorizer_",
|
||||
TablePrefix: Prefix,
|
||||
},
|
||||
}
|
||||
if constants.DATABASE_TYPE == enum.Postgres.String() {
|
||||
db, err = gorm.Open(postgres.Open(constants.DATABASE_URL), ormConfig)
|
||||
}
|
||||
if constants.DATABASE_TYPE == enum.Mysql.String() {
|
||||
db, err = gorm.Open(mysql.Open(constants.DATABASE_URL), ormConfig)
|
||||
}
|
||||
if constants.DATABASE_TYPE == enum.Sqlite.String() {
|
||||
db, err = gorm.Open(sqlite.Open(constants.DATABASE_URL), ormConfig)
|
||||
|
||||
log.Println("db type:", constants.DATABASE_TYPE)
|
||||
|
||||
switch constants.DATABASE_TYPE {
|
||||
case enum.Postgres.String():
|
||||
sqlDB, err = gorm.Open(postgres.Open(constants.DATABASE_URL), ormConfig)
|
||||
break
|
||||
case enum.Sqlite.String():
|
||||
sqlDB, err = gorm.Open(sqlite.Open(constants.DATABASE_URL), ormConfig)
|
||||
break
|
||||
case enum.Mysql.String():
|
||||
sqlDB, err = gorm.Open(mysql.Open(constants.DATABASE_URL), ormConfig)
|
||||
break
|
||||
case enum.SQLServer.String():
|
||||
sqlDB, err = gorm.Open(sqlserver.Open(constants.DATABASE_URL), ormConfig)
|
||||
break
|
||||
case enum.Arangodb.String():
|
||||
arangodb, err := initArangodb()
|
||||
if err != nil {
|
||||
log.Fatal("error initializing arangodb:", err)
|
||||
}
|
||||
|
||||
Mgr = &manager{
|
||||
sqlDB: nil,
|
||||
mongodb: nil,
|
||||
arangodb: arangodb,
|
||||
}
|
||||
|
||||
break
|
||||
case enum.Mongodb.String():
|
||||
mongodb, err := initMongodb()
|
||||
if err != nil {
|
||||
log.Fatal("error initializing mongodb connection:", err)
|
||||
}
|
||||
|
||||
Mgr = &manager{
|
||||
sqlDB: nil,
|
||||
arangodb: nil,
|
||||
mongodb: mongodb,
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Fatal("Failed to init db:", err)
|
||||
} else {
|
||||
db.AutoMigrate(&User{}, &VerificationRequest{}, &Role{}, &Session{})
|
||||
// common for all sql dbs that are configured via go-orm
|
||||
if IsORMSupported {
|
||||
if err != nil {
|
||||
log.Fatal("Failed to init sqlDB:", err)
|
||||
} else {
|
||||
sqlDB.AutoMigrate(&User{}, &VerificationRequest{}, &Session{})
|
||||
}
|
||||
Mgr = &manager{
|
||||
sqlDB: sqlDB,
|
||||
arangodb: nil,
|
||||
mongodb: nil,
|
||||
}
|
||||
}
|
||||
|
||||
Mgr = &manager{db: db}
|
||||
}
|
||||
|
81
server/db/mongodb.go
Normal file
81
server/db/mongodb.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"go.mongodb.org/mongo-driver/mongo/readpref"
|
||||
)
|
||||
|
||||
func initMongodb() (*mongo.Database, error) {
|
||||
mongodbOptions := options.Client().ApplyURI(constants.DATABASE_URL)
|
||||
maxWait := time.Duration(5 * time.Second)
|
||||
mongodbOptions.ConnectTimeout = &maxWait
|
||||
mongoClient, err := mongo.NewClient(mongodbOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx, _ := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
err = mongoClient.Connect(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = mongoClient.Ping(ctx, readpref.Primary())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mongodb := mongoClient.Database(constants.DATABASE_NAME, options.Database())
|
||||
|
||||
mongodb.CreateCollection(ctx, Collections.User, options.CreateCollection())
|
||||
userCollection := mongodb.Collection(Collections.User, options.Collection())
|
||||
userCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{
|
||||
mongo.IndexModel{
|
||||
Keys: bson.M{"id": 1},
|
||||
Options: options.Index().SetUnique(true).SetSparse(true),
|
||||
},
|
||||
}, options.CreateIndexes())
|
||||
userCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{
|
||||
mongo.IndexModel{
|
||||
Keys: bson.M{"email": 1},
|
||||
Options: options.Index().SetUnique(true).SetSparse(true),
|
||||
},
|
||||
}, options.CreateIndexes())
|
||||
|
||||
mongodb.CreateCollection(ctx, Collections.VerificationRequest, options.CreateCollection())
|
||||
verificationRequestCollection := mongodb.Collection(Collections.VerificationRequest, options.Collection())
|
||||
verificationRequestCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{
|
||||
mongo.IndexModel{
|
||||
Keys: bson.M{"id": 1},
|
||||
Options: options.Index().SetUnique(true).SetSparse(true),
|
||||
},
|
||||
}, options.CreateIndexes())
|
||||
verificationRequestCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{
|
||||
mongo.IndexModel{
|
||||
Keys: bson.M{"email": 1, "identifier": 1},
|
||||
Options: options.Index().SetUnique(true).SetSparse(true),
|
||||
},
|
||||
}, options.CreateIndexes())
|
||||
verificationRequestCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{
|
||||
mongo.IndexModel{
|
||||
Keys: bson.M{"token": 1},
|
||||
Options: options.Index().SetSparse(true),
|
||||
},
|
||||
}, options.CreateIndexes())
|
||||
|
||||
mongodb.CreateCollection(ctx, Collections.Session, options.CreateCollection())
|
||||
sessionCollection := mongodb.Collection(Collections.Session, options.Collection())
|
||||
sessionCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{
|
||||
mongo.IndexModel{
|
||||
Keys: bson.M{"id": 1},
|
||||
Options: options.Index().SetUnique(true).SetSparse(true),
|
||||
},
|
||||
}, options.CreateIndexes())
|
||||
|
||||
return mongodb, nil
|
||||
}
|
@@ -1,34 +0,0 @@
|
||||
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,37 +2,66 @@ package db
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
type Session struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;"`
|
||||
UserID uuid.UUID `gorm:"type:uuid;"`
|
||||
User User
|
||||
UserAgent string
|
||||
IP string
|
||||
CreatedAt int64 `gorm:"autoCreateTime"`
|
||||
UpdatedAt int64 `gorm:"autoUpdateTime"`
|
||||
Key string `json:"_key,omitempty" bson:"_key,omitempty"` // for arangodb
|
||||
ObjectID string `json:"_id,omitempty" bson:"_id"` // for arangodb & mongodb
|
||||
ID string `gorm:"primaryKey;type:char(36)" json:"id" bson:"id"`
|
||||
UserID string `gorm:"type:char(36)" json:"user_id" bson:"user_id"`
|
||||
User User `json:"-" bson:"-"`
|
||||
UserAgent string `json:"user_agent" bson:"user_agent"`
|
||||
IP string `json:"ip" bson:"ip"`
|
||||
CreatedAt int64 `gorm:"autoCreateTime" json:"created_at" bson:"created_at"`
|
||||
UpdatedAt int64 `gorm:"autoUpdateTime" json:"updated_at" bson:"updated_at"`
|
||||
}
|
||||
|
||||
func (r *Session) BeforeCreate(tx *gorm.DB) (err error) {
|
||||
r.ID = uuid.New()
|
||||
// AddSession function to save user sessiosn
|
||||
func (mgr *manager) AddSession(session Session) error {
|
||||
if session.ID == "" {
|
||||
session.ID = uuid.New().String()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
if IsORMSupported {
|
||||
session.Key = session.ID
|
||||
session.ObjectID = session.ID
|
||||
res := mgr.sqlDB.Clauses(
|
||||
clause.OnConflict{
|
||||
DoNothing: true,
|
||||
}).Create(&session)
|
||||
if res.Error != nil {
|
||||
log.Println(`error saving session`, res.Error)
|
||||
return res.Error
|
||||
}
|
||||
}
|
||||
|
||||
// SaveSession function to save user sessiosn
|
||||
func (mgr *manager) SaveSession(session Session) error {
|
||||
res := mgr.db.Clauses(
|
||||
clause.OnConflict{
|
||||
DoNothing: true,
|
||||
}).Create(&session)
|
||||
if res.Error != nil {
|
||||
log.Println(`Error saving session`, res.Error)
|
||||
return res.Error
|
||||
if IsArangoDB {
|
||||
session.CreatedAt = time.Now().Unix()
|
||||
session.UpdatedAt = time.Now().Unix()
|
||||
sessionCollection, _ := mgr.arangodb.Collection(nil, Collections.Session)
|
||||
_, err := sessionCollection.CreateDocument(nil, session)
|
||||
if err != nil {
|
||||
log.Println(`error saving session`, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if IsMongoDB {
|
||||
session.Key = session.ID
|
||||
session.ObjectID = session.ID
|
||||
session.CreatedAt = time.Now().Unix()
|
||||
session.UpdatedAt = time.Now().Unix()
|
||||
sessionCollection := mgr.mongodb.Collection(Collections.Session, options.Collection())
|
||||
_, err := sessionCollection.InsertOne(nil, session)
|
||||
if err != nil {
|
||||
log.Println(`error saving session`, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@@ -1,82 +1,224 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/arangodb/go-driver"
|
||||
arangoDriver "github.com/arangodb/go-driver"
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;"`
|
||||
FirstName string
|
||||
LastName string
|
||||
Email string `gorm:"unique"`
|
||||
Password string
|
||||
SignupMethod string
|
||||
EmailVerifiedAt int64
|
||||
CreatedAt int64 `gorm:"autoCreateTime"`
|
||||
UpdatedAt int64 `gorm:"autoUpdateTime"`
|
||||
Image string
|
||||
Roles string
|
||||
Key string `json:"_key,omitempty" bson:"_key"` // for arangodb
|
||||
ObjectID string `json:"_id,omitempty" bson:"_id"` // for arangodb & mongodb
|
||||
ID string `gorm:"primaryKey;type:char(36)" json:"id" bson:"id"`
|
||||
FirstName string `json:"first_name" bson:"first_name"`
|
||||
LastName string `json:"last_name" bson:"last_name"`
|
||||
Email string `gorm:"unique" json:"email" bson:"email"`
|
||||
Password string `gorm:"type:text" json:"password" bson:"password"`
|
||||
SignupMethod string `json:"signup_method" bson:"signup_method"`
|
||||
EmailVerifiedAt int64 `json:"email_verified_at" bson:"email_verified_at"`
|
||||
CreatedAt int64 `gorm:"autoCreateTime" json:"created_at" bson:"created_at"`
|
||||
UpdatedAt int64 `gorm:"autoUpdateTime" json:"updated_at" bson:"updated_at"`
|
||||
Image string `gorm:"type:text" json:"image" bson:"image"`
|
||||
Roles string `json:"roles" bson:"roles"`
|
||||
}
|
||||
|
||||
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
|
||||
u.ID = uuid.New()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SaveUser function to add user even with email conflict
|
||||
func (mgr *manager) SaveUser(user User) (User, error) {
|
||||
result := mgr.db.Clauses(
|
||||
clause.OnConflict{
|
||||
UpdateAll: true,
|
||||
Columns: []clause.Column{{Name: "email"}},
|
||||
}).Create(&user)
|
||||
|
||||
if result.Error != nil {
|
||||
log.Println(result.Error)
|
||||
return user, result.Error
|
||||
// AddUser function to add user even with email conflict
|
||||
func (mgr *manager) AddUser(user User) (User, error) {
|
||||
if user.ID == "" {
|
||||
user.ID = uuid.New().String()
|
||||
}
|
||||
|
||||
if IsORMSupported {
|
||||
// copy id as value for fields required for mongodb & arangodb
|
||||
user.Key = user.ID
|
||||
user.ObjectID = user.ID
|
||||
result := mgr.sqlDB.Clauses(
|
||||
clause.OnConflict{
|
||||
UpdateAll: true,
|
||||
Columns: []clause.Column{{Name: "email"}},
|
||||
}).Create(&user)
|
||||
|
||||
if result.Error != nil {
|
||||
log.Println("error adding user:", result.Error)
|
||||
return user, result.Error
|
||||
}
|
||||
}
|
||||
|
||||
if IsArangoDB {
|
||||
user.CreatedAt = time.Now().Unix()
|
||||
user.UpdatedAt = time.Now().Unix()
|
||||
userCollection, _ := mgr.arangodb.Collection(nil, Collections.User)
|
||||
meta, err := userCollection.CreateDocument(arangoDriver.WithOverwrite(nil), user)
|
||||
if err != nil {
|
||||
log.Println("error adding user:", err)
|
||||
return user, err
|
||||
}
|
||||
user.Key = meta.Key
|
||||
user.ObjectID = meta.ID.String()
|
||||
}
|
||||
|
||||
if IsMongoDB {
|
||||
user.CreatedAt = time.Now().Unix()
|
||||
user.UpdatedAt = time.Now().Unix()
|
||||
user.Key = user.ID
|
||||
user.ObjectID = user.ID
|
||||
userCollection := mgr.mongodb.Collection(Collections.User, options.Collection())
|
||||
_, err := userCollection.InsertOne(nil, user)
|
||||
if err != nil {
|
||||
log.Println("error adding user:", err)
|
||||
return user, err
|
||||
}
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// 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: "email"}},
|
||||
}).Create(&user)
|
||||
|
||||
if result.Error != nil {
|
||||
log.Println(result.Error)
|
||||
return user, result.Error
|
||||
if IsORMSupported {
|
||||
result := mgr.sqlDB.Save(&user)
|
||||
|
||||
if result.Error != nil {
|
||||
log.Println("error updating user:", result.Error)
|
||||
return user, result.Error
|
||||
}
|
||||
}
|
||||
|
||||
if IsArangoDB {
|
||||
collection, _ := mgr.arangodb.Collection(nil, Collections.User)
|
||||
meta, err := collection.UpdateDocument(nil, user.Key, user)
|
||||
if err != nil {
|
||||
log.Println("error updating user:", err)
|
||||
return user, err
|
||||
}
|
||||
|
||||
user.Key = meta.Key
|
||||
user.ObjectID = meta.ID.String()
|
||||
}
|
||||
|
||||
if IsMongoDB {
|
||||
userCollection := mgr.mongodb.Collection(Collections.User, options.Collection())
|
||||
_, err := userCollection.UpdateOne(nil, bson.M{"id": bson.M{"$eq": user.ID}}, bson.M{"$set": user}, options.MergeUpdateOptions())
|
||||
if err != nil {
|
||||
log.Println("error updating user:", err)
|
||||
return user, err
|
||||
}
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// GetUsers function to get all users
|
||||
func (mgr *manager) GetUsers() ([]User, error) {
|
||||
var users []User
|
||||
result := mgr.db.Find(&users)
|
||||
if result.Error != nil {
|
||||
log.Println(result.Error)
|
||||
return users, result.Error
|
||||
|
||||
if IsORMSupported {
|
||||
result := mgr.sqlDB.Find(&users)
|
||||
if result.Error != nil {
|
||||
log.Println("error getting users:", result.Error)
|
||||
return users, result.Error
|
||||
}
|
||||
}
|
||||
|
||||
if IsArangoDB {
|
||||
query := fmt.Sprintf("FOR d in %s RETURN d", Collections.User)
|
||||
|
||||
cursor, err := mgr.arangodb.Query(nil, query, nil)
|
||||
if err != nil {
|
||||
return users, err
|
||||
}
|
||||
defer cursor.Close()
|
||||
|
||||
for {
|
||||
var user User
|
||||
meta, err := cursor.ReadDocument(nil, &user)
|
||||
|
||||
if driver.IsNoMoreDocuments(err) {
|
||||
break
|
||||
} else if err != nil {
|
||||
return users, err
|
||||
}
|
||||
|
||||
if meta.Key != "" {
|
||||
users = append(users, user)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if IsMongoDB {
|
||||
userCollection := mgr.mongodb.Collection(Collections.User, options.Collection())
|
||||
cursor, err := userCollection.Find(nil, bson.M{}, options.Find())
|
||||
if err != nil {
|
||||
log.Println("error getting users:", err)
|
||||
return users, err
|
||||
}
|
||||
defer cursor.Close(nil)
|
||||
|
||||
for cursor.Next(nil) {
|
||||
var user User
|
||||
err := cursor.Decode(&user)
|
||||
if err != nil {
|
||||
return users, err
|
||||
}
|
||||
users = append(users, user)
|
||||
}
|
||||
}
|
||||
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func (mgr *manager) GetUserByEmail(email string) (User, error) {
|
||||
var user User
|
||||
result := mgr.db.Where("email = ?", email).First(&user)
|
||||
|
||||
if result.Error != nil {
|
||||
return user, result.Error
|
||||
if IsORMSupported {
|
||||
result := mgr.sqlDB.Where("email = ?", email).First(&user)
|
||||
|
||||
if result.Error != nil {
|
||||
return user, result.Error
|
||||
}
|
||||
}
|
||||
|
||||
if IsArangoDB {
|
||||
query := fmt.Sprintf("FOR d in %s FILTER d.email == @email RETURN d", Collections.User)
|
||||
bindVars := map[string]interface{}{
|
||||
"email": email,
|
||||
}
|
||||
|
||||
cursor, err := mgr.arangodb.Query(nil, query, bindVars)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
defer cursor.Close()
|
||||
|
||||
for {
|
||||
if !cursor.HasMore() {
|
||||
if user.Key == "" {
|
||||
return user, fmt.Errorf("user not found")
|
||||
}
|
||||
break
|
||||
}
|
||||
_, err := cursor.ReadDocument(nil, &user)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if IsMongoDB {
|
||||
userCollection := mgr.mongodb.Collection(Collections.User, options.Collection())
|
||||
err := userCollection.FindOne(nil, bson.M{"email": email}).Decode(&user)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
}
|
||||
|
||||
return user, nil
|
||||
@@ -84,35 +226,78 @@ func (mgr *manager) GetUserByEmail(email string) (User, 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
|
||||
if IsORMSupported {
|
||||
result := mgr.sqlDB.Where("id = ?", id).First(&user)
|
||||
|
||||
if result.Error != nil {
|
||||
return user, result.Error
|
||||
}
|
||||
}
|
||||
|
||||
if IsArangoDB {
|
||||
query := fmt.Sprintf("FOR d in %s FILTER d.id == @id LIMIT 1 RETURN d", Collections.User)
|
||||
bindVars := map[string]interface{}{
|
||||
"id": id,
|
||||
}
|
||||
|
||||
cursor, err := mgr.arangodb.Query(nil, query, bindVars)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
defer cursor.Close()
|
||||
|
||||
for {
|
||||
if !cursor.HasMore() {
|
||||
if user.Key == "" {
|
||||
return user, fmt.Errorf("user not found")
|
||||
}
|
||||
break
|
||||
}
|
||||
_, err := cursor.ReadDocument(nil, &user)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if IsMongoDB {
|
||||
userCollection := mgr.mongodb.Collection(Collections.User, options.Collection())
|
||||
err := userCollection.FindOne(nil, bson.M{"id": id}).Decode(&user)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (mgr *manager) UpdateVerificationTime(verifiedAt int64, id uuid.UUID) error {
|
||||
user := &User{
|
||||
ID: id,
|
||||
}
|
||||
result := mgr.db.Model(&user).Where("id = ?", id).Update("email_verified_at", verifiedAt)
|
||||
func (mgr *manager) DeleteUser(user User) error {
|
||||
if IsORMSupported {
|
||||
result := mgr.sqlDB.Delete(&user)
|
||||
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mgr *manager) DeleteUser(email string) error {
|
||||
var user User
|
||||
result := mgr.db.Where("email = ?", email).Delete(&user)
|
||||
|
||||
if result.Error != nil {
|
||||
log.Println(`Error deleting user:`, result.Error)
|
||||
return result.Error
|
||||
if result.Error != nil {
|
||||
log.Println(`error deleting user:`, result.Error)
|
||||
return result.Error
|
||||
}
|
||||
}
|
||||
|
||||
if IsArangoDB {
|
||||
collection, _ := mgr.arangodb.Collection(nil, Collections.User)
|
||||
_, err := collection.RemoveDocument(nil, user.Key)
|
||||
if err != nil {
|
||||
log.Println(`error deleting user:`, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if IsMongoDB {
|
||||
userCollection := mgr.mongodb.Collection(Collections.User, options.Collection())
|
||||
_, err := userCollection.DeleteOne(nil, bson.M{"id": user.ID}, options.Delete())
|
||||
if err != nil {
|
||||
log.Println("error deleting user:", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@@ -1,50 +1,182 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/arangodb/go-driver"
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
type VerificationRequest struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;"`
|
||||
Token string `gorm:"index"`
|
||||
Identifier string
|
||||
ExpiresAt int64
|
||||
CreatedAt int64 `gorm:"autoCreateTime"`
|
||||
UpdatedAt int64 `gorm:"autoUpdateTime"`
|
||||
Email string `gorm:"unique"`
|
||||
}
|
||||
|
||||
func (v *VerificationRequest) BeforeCreate(tx *gorm.DB) (err error) {
|
||||
v.ID = uuid.New()
|
||||
|
||||
return
|
||||
Key string `json:"_key,omitempty" bson:"_key"` // for arangodb
|
||||
ObjectID string `json:"_id,omitempty" bson:"_id"` // for arangodb & mongodb
|
||||
ID string `gorm:"primaryKey;type:char(36)" json:"id" bson:"id"`
|
||||
Token string `gorm:"type:text" json:"token" bson:"token"`
|
||||
Identifier string `gorm:"uniqueIndex:idx_email_identifier" json:"identifier" bson:"identifier"`
|
||||
ExpiresAt int64 `json:"expires_at" bson:"expires_at"`
|
||||
CreatedAt int64 `gorm:"autoCreateTime" json:"created_at" bson:"created_at"`
|
||||
UpdatedAt int64 `gorm:"autoUpdateTime" json:"updated_at" bson:"updated_at"`
|
||||
Email string `gorm:"uniqueIndex:idx_email_identifier" json:"email" bson:"email"`
|
||||
}
|
||||
|
||||
// AddVerification function to add verification record
|
||||
func (mgr *manager) AddVerification(verification VerificationRequest) (VerificationRequest, error) {
|
||||
result := mgr.db.Clauses(clause.OnConflict{
|
||||
Columns: []clause.Column{{Name: "email"}},
|
||||
DoUpdates: clause.AssignmentColumns([]string{"token", "identifier", "expires_at"}),
|
||||
}).Create(&verification)
|
||||
|
||||
if result.Error != nil {
|
||||
log.Println(`Error saving verification record`, result.Error)
|
||||
return verification, result.Error
|
||||
if verification.ID == "" {
|
||||
verification.ID = uuid.New().String()
|
||||
}
|
||||
if IsORMSupported {
|
||||
// copy id as value for fields required for mongodb & arangodb
|
||||
verification.Key = verification.ID
|
||||
verification.ObjectID = verification.ID
|
||||
result := mgr.sqlDB.Clauses(clause.OnConflict{
|
||||
Columns: []clause.Column{{Name: "email"}, {Name: "identifier"}},
|
||||
DoUpdates: clause.AssignmentColumns([]string{"token", "expires_at"}),
|
||||
}).Create(&verification)
|
||||
|
||||
if result.Error != nil {
|
||||
log.Println(`error saving verification record`, result.Error)
|
||||
return verification, result.Error
|
||||
}
|
||||
}
|
||||
|
||||
if IsArangoDB {
|
||||
verification.CreatedAt = time.Now().Unix()
|
||||
verification.UpdatedAt = time.Now().Unix()
|
||||
verificationRequestCollection, _ := mgr.arangodb.Collection(nil, Collections.VerificationRequest)
|
||||
meta, err := verificationRequestCollection.CreateDocument(nil, verification)
|
||||
if err != nil {
|
||||
log.Println("error saving verification record:", err)
|
||||
return verification, err
|
||||
}
|
||||
verification.Key = meta.Key
|
||||
verification.ObjectID = meta.ID.String()
|
||||
}
|
||||
|
||||
if IsMongoDB {
|
||||
verification.CreatedAt = time.Now().Unix()
|
||||
verification.UpdatedAt = time.Now().Unix()
|
||||
verification.Key = verification.ID
|
||||
verification.ObjectID = verification.ID
|
||||
verificationRequestCollection := mgr.mongodb.Collection(Collections.VerificationRequest, options.Collection())
|
||||
_, err := verificationRequestCollection.InsertOne(nil, verification)
|
||||
if err != nil {
|
||||
log.Println("error saving verification record:", err)
|
||||
return verification, err
|
||||
}
|
||||
}
|
||||
|
||||
return verification, nil
|
||||
}
|
||||
|
||||
// GetVerificationRequests function to get all verification requests
|
||||
func (mgr *manager) GetVerificationRequests() ([]VerificationRequest, error) {
|
||||
var verificationRequests []VerificationRequest
|
||||
|
||||
if IsORMSupported {
|
||||
result := mgr.sqlDB.Find(&verificationRequests)
|
||||
if result.Error != nil {
|
||||
log.Println("error getting verification requests:", result.Error)
|
||||
return verificationRequests, result.Error
|
||||
}
|
||||
}
|
||||
|
||||
if IsArangoDB {
|
||||
query := fmt.Sprintf("FOR d in %s RETURN d", Collections.VerificationRequest)
|
||||
|
||||
cursor, err := mgr.arangodb.Query(nil, query, nil)
|
||||
if err != nil {
|
||||
return verificationRequests, err
|
||||
}
|
||||
defer cursor.Close()
|
||||
|
||||
for {
|
||||
var verificationRequest VerificationRequest
|
||||
meta, err := cursor.ReadDocument(nil, &verificationRequest)
|
||||
|
||||
if driver.IsNoMoreDocuments(err) {
|
||||
break
|
||||
} else if err != nil {
|
||||
return verificationRequests, err
|
||||
}
|
||||
|
||||
if meta.Key != "" {
|
||||
verificationRequests = append(verificationRequests, verificationRequest)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if IsMongoDB {
|
||||
verificationRequestCollection := mgr.mongodb.Collection(Collections.VerificationRequest, options.Collection())
|
||||
cursor, err := verificationRequestCollection.Find(nil, bson.M{}, options.Find())
|
||||
if err != nil {
|
||||
log.Println("error getting verification requests:", err)
|
||||
return verificationRequests, err
|
||||
}
|
||||
defer cursor.Close(nil)
|
||||
|
||||
for cursor.Next(nil) {
|
||||
var verificationRequest VerificationRequest
|
||||
err := cursor.Decode(&verificationRequest)
|
||||
if err != nil {
|
||||
return verificationRequests, err
|
||||
}
|
||||
verificationRequests = append(verificationRequests, verificationRequest)
|
||||
}
|
||||
}
|
||||
|
||||
return verificationRequests, nil
|
||||
}
|
||||
|
||||
func (mgr *manager) GetVerificationByToken(token string) (VerificationRequest, error) {
|
||||
var verification VerificationRequest
|
||||
result := mgr.db.Where("token = ?", token).First(&verification)
|
||||
|
||||
if result.Error != nil {
|
||||
log.Println(`Error getting verification token:`, result.Error)
|
||||
return verification, result.Error
|
||||
if IsORMSupported {
|
||||
result := mgr.sqlDB.Where("token = ?", token).First(&verification)
|
||||
|
||||
if result.Error != nil {
|
||||
log.Println(`error getting verification request:`, result.Error)
|
||||
return verification, result.Error
|
||||
}
|
||||
}
|
||||
|
||||
if IsArangoDB {
|
||||
query := fmt.Sprintf("FOR d in %s FILTER d.token == @token LIMIT 1 RETURN d", Collections.VerificationRequest)
|
||||
bindVars := map[string]interface{}{
|
||||
"token": token,
|
||||
}
|
||||
|
||||
cursor, err := mgr.arangodb.Query(nil, query, bindVars)
|
||||
if err != nil {
|
||||
return verification, err
|
||||
}
|
||||
defer cursor.Close()
|
||||
|
||||
for {
|
||||
if !cursor.HasMore() {
|
||||
if verification.Key == "" {
|
||||
return verification, fmt.Errorf("verification request not found")
|
||||
}
|
||||
break
|
||||
}
|
||||
_, err := cursor.ReadDocument(nil, &verification)
|
||||
if err != nil {
|
||||
return verification, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if IsMongoDB {
|
||||
verificationRequestCollection := mgr.mongodb.Collection(Collections.VerificationRequest, options.Collection())
|
||||
err := verificationRequestCollection.FindOne(nil, bson.M{"token": token}).Decode(&verification)
|
||||
if err != nil {
|
||||
return verification, err
|
||||
}
|
||||
}
|
||||
|
||||
return verification, nil
|
||||
@@ -52,35 +184,79 @@ func (mgr *manager) GetVerificationByToken(token string) (VerificationRequest, e
|
||||
|
||||
func (mgr *manager) GetVerificationByEmail(email string) (VerificationRequest, error) {
|
||||
var verification VerificationRequest
|
||||
result := mgr.db.Where("email = ?", email).First(&verification)
|
||||
if IsORMSupported {
|
||||
result := mgr.sqlDB.Where("email = ?", email).First(&verification)
|
||||
|
||||
if result.Error != nil {
|
||||
log.Println(`Error getting verification token:`, result.Error)
|
||||
return verification, result.Error
|
||||
if result.Error != nil {
|
||||
log.Println(`error getting verification token:`, result.Error)
|
||||
return verification, result.Error
|
||||
}
|
||||
}
|
||||
|
||||
if IsArangoDB {
|
||||
query := fmt.Sprintf("FOR d in %s FILTER d.email == @email LIMIT 1 RETURN d", Collections.VerificationRequest)
|
||||
bindVars := map[string]interface{}{
|
||||
"email": email,
|
||||
}
|
||||
|
||||
cursor, err := mgr.arangodb.Query(nil, query, bindVars)
|
||||
if err != nil {
|
||||
return verification, err
|
||||
}
|
||||
defer cursor.Close()
|
||||
|
||||
for {
|
||||
if !cursor.HasMore() {
|
||||
if verification.Key == "" {
|
||||
return verification, fmt.Errorf("verification request not found")
|
||||
}
|
||||
break
|
||||
}
|
||||
_, err := cursor.ReadDocument(nil, &verification)
|
||||
if err != nil {
|
||||
return verification, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if IsMongoDB {
|
||||
verificationRequestCollection := mgr.mongodb.Collection(Collections.VerificationRequest, options.Collection())
|
||||
err := verificationRequestCollection.FindOne(nil, bson.M{"email": email}).Decode(&verification)
|
||||
if err != nil {
|
||||
return verification, err
|
||||
}
|
||||
}
|
||||
|
||||
return verification, nil
|
||||
}
|
||||
|
||||
func (mgr *manager) DeleteToken(email string) error {
|
||||
var verification VerificationRequest
|
||||
result := mgr.db.Where("email = ?", email).Delete(&verification)
|
||||
func (mgr *manager) DeleteVerificationRequest(verificationRequest VerificationRequest) error {
|
||||
if IsORMSupported {
|
||||
result := mgr.sqlDB.Delete(&verificationRequest)
|
||||
|
||||
if result.Error != nil {
|
||||
log.Println(`Error deleting token:`, result.Error)
|
||||
return result.Error
|
||||
if result.Error != nil {
|
||||
log.Println(`error deleting verification request:`, result.Error)
|
||||
return result.Error
|
||||
}
|
||||
}
|
||||
|
||||
if IsArangoDB {
|
||||
collection, _ := mgr.arangodb.Collection(nil, Collections.VerificationRequest)
|
||||
_, err := collection.RemoveDocument(nil, verificationRequest.Key)
|
||||
if err != nil {
|
||||
log.Println(`error deleting verification request:`, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if IsMongoDB {
|
||||
verificationRequestCollection := mgr.mongodb.Collection(Collections.VerificationRequest, options.Collection())
|
||||
_, err := verificationRequestCollection.DeleteOne(nil, bson.M{"id": verificationRequest.ID}, options.Delete())
|
||||
if err != nil {
|
||||
log.Println("error deleting verification request::", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetUsers function to get all users
|
||||
func (mgr *manager) GetVerificationRequests() ([]VerificationRequest, error) {
|
||||
var verificationRequests []VerificationRequest
|
||||
result := mgr.db.Find(&verificationRequests)
|
||||
if result.Error != nil {
|
||||
log.Println(result.Error)
|
||||
return verificationRequests, result.Error
|
||||
}
|
||||
return verificationRequests, nil
|
||||
}
|
||||
|
@@ -6,6 +6,9 @@ const (
|
||||
Postgres DbType = iota
|
||||
Sqlite
|
||||
Mysql
|
||||
SQLServer
|
||||
Arangodb
|
||||
Mongodb
|
||||
)
|
||||
|
||||
func (d DbType) String() string {
|
||||
@@ -13,5 +16,8 @@ func (d DbType) String() string {
|
||||
"postgres",
|
||||
"sqlite",
|
||||
"mysql",
|
||||
"sqlserver",
|
||||
"arangodb",
|
||||
"mongodb",
|
||||
}[d]
|
||||
}
|
||||
|
61
server/env.go → server/env/env.go
vendored
61
server/env.go → server/env/env.go
vendored
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package env
|
||||
|
||||
import (
|
||||
"flag"
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
|
||||
// build variables
|
||||
var (
|
||||
Version string
|
||||
VERSION string
|
||||
ARG_DB_URL *string
|
||||
ARG_DB_TYPE *string
|
||||
ARG_AUTHORIZER_URL *string
|
||||
@@ -22,7 +22,9 @@ var (
|
||||
|
||||
// InitEnv -> to initialize env and through error if required env are not present
|
||||
func InitEnv() {
|
||||
envPath := `.env`
|
||||
if constants.ENV_PATH == "" {
|
||||
constants.ENV_PATH = `.env`
|
||||
}
|
||||
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")
|
||||
@@ -30,19 +32,20 @@ func InitEnv() {
|
||||
|
||||
flag.Parse()
|
||||
if *ARG_ENV_FILE != "" {
|
||||
envPath = *ARG_ENV_FILE
|
||||
constants.ENV_PATH = *ARG_ENV_FILE
|
||||
}
|
||||
|
||||
err := godotenv.Load(envPath)
|
||||
err := godotenv.Load(constants.ENV_PATH)
|
||||
if err != nil {
|
||||
log.Println("Error loading .env file")
|
||||
log.Printf("error loading %s file", constants.ENV_PATH)
|
||||
}
|
||||
|
||||
constants.VERSION = Version
|
||||
constants.VERSION = VERSION
|
||||
constants.ADMIN_SECRET = os.Getenv("ADMIN_SECRET")
|
||||
constants.ENV = os.Getenv("ENV")
|
||||
constants.DATABASE_TYPE = os.Getenv("DATABASE_TYPE")
|
||||
constants.DATABASE_URL = os.Getenv("DATABASE_URL")
|
||||
constants.DATABASE_NAME = os.Getenv("DATABASE_NAME")
|
||||
constants.SMTP_HOST = os.Getenv("SMTP_HOST")
|
||||
constants.SMTP_PORT = os.Getenv("SMTP_PORT")
|
||||
constants.SENDER_EMAIL = os.Getenv("SENDER_EMAIL")
|
||||
@@ -62,9 +65,9 @@ func InitEnv() {
|
||||
constants.TWITTER_CLIENT_ID = os.Getenv("TWITTER_CLIENT_ID")
|
||||
constants.TWITTER_CLIENT_SECRET = os.Getenv("TWITTER_CLIENT_SECRET")
|
||||
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.DISABLE_MAGIC_LOGIN = os.Getenv("DISABLE_MAGIC_LOGIN")
|
||||
constants.DISABLE_BASIC_AUTHENTICATION = os.Getenv("DISABLE_BASIC_AUTHENTICATION") == "true"
|
||||
constants.DISABLE_EMAIL_VERIFICATION = os.Getenv("DISABLE_EMAIL_VERIFICATION") == "true"
|
||||
constants.DISABLE_MAGIC_LOGIN = os.Getenv("DISABLE_MAGIC_LOGIN") == "true"
|
||||
constants.JWT_ROLE_CLAIM = os.Getenv("JWT_ROLE_CLAIM")
|
||||
|
||||
if constants.ADMIN_SECRET == "" {
|
||||
@@ -84,15 +87,30 @@ func InitEnv() {
|
||||
|
||||
allowedOriginsSplit := strings.Split(os.Getenv("ALLOWED_ORIGINS"), ",")
|
||||
allowedOrigins := []string{}
|
||||
hasWildCard := false
|
||||
|
||||
for _, val := range allowedOriginsSplit {
|
||||
trimVal := strings.TrimSpace(val)
|
||||
if trimVal != "" {
|
||||
allowedOrigins = append(allowedOrigins, trimVal)
|
||||
if trimVal != "*" {
|
||||
host, port := utils.GetHostParts(trimVal)
|
||||
allowedOrigins = append(allowedOrigins, host+":"+port)
|
||||
} else {
|
||||
hasWildCard = true
|
||||
allowedOrigins = append(allowedOrigins, trimVal)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(allowedOrigins) > 1 && hasWildCard {
|
||||
allowedOrigins = []string{"*"}
|
||||
}
|
||||
|
||||
if len(allowedOrigins) == 0 {
|
||||
allowedOrigins = []string{"*"}
|
||||
}
|
||||
|
||||
constants.ALLOWED_ORIGINS = allowedOrigins
|
||||
|
||||
if *ARG_AUTHORIZER_URL != "" {
|
||||
@@ -115,6 +133,10 @@ func InitEnv() {
|
||||
panic("Database type is required")
|
||||
}
|
||||
|
||||
if constants.DATABASE_NAME == "" {
|
||||
constants.DATABASE_NAME = "authorizer"
|
||||
}
|
||||
|
||||
if constants.JWT_TYPE == "" {
|
||||
constants.JWT_TYPE = "HS256"
|
||||
}
|
||||
@@ -123,21 +145,14 @@ func InitEnv() {
|
||||
constants.COOKIE_NAME = "authorizer"
|
||||
}
|
||||
|
||||
if constants.DISABLE_BASIC_AUTHENTICATION == "" {
|
||||
constants.DISABLE_BASIC_AUTHENTICATION = "false"
|
||||
}
|
||||
|
||||
if constants.DISABLE_MAGIC_LOGIN == "" {
|
||||
constants.DISABLE_MAGIC_LOGIN = "false"
|
||||
}
|
||||
|
||||
if constants.SMTP_HOST == "" || constants.SENDER_EMAIL == "" || constants.SENDER_PASSWORD == "" {
|
||||
constants.DISABLE_EMAIL_VERIFICATION = "true"
|
||||
} else if constants.DISABLE_EMAIL_VERIFICATION == "" {
|
||||
constants.DISABLE_EMAIL_VERIFICATION = "false"
|
||||
constants.DISABLE_EMAIL_VERIFICATION = true
|
||||
constants.DISABLE_MAGIC_LOGIN = true
|
||||
}
|
||||
|
||||
log.Println("=> disable email verification:", constants.DISABLE_EMAIL_VERIFICATION)
|
||||
if constants.DISABLE_EMAIL_VERIFICATION {
|
||||
constants.DISABLE_MAGIC_LOGIN = true
|
||||
}
|
||||
|
||||
rolesSplit := strings.Split(os.Getenv("ROLES"), ",")
|
||||
roles := []string{}
|
29
server/env/env_test.go
vendored
Normal file
29
server/env/env_test.go
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/enum"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestEnvs(t *testing.T) {
|
||||
constants.ENV_PATH = "../../.env.sample"
|
||||
InitEnv()
|
||||
|
||||
assert.Equal(t, constants.ADMIN_SECRET, "admin")
|
||||
assert.Equal(t, constants.ENV, "production")
|
||||
assert.Equal(t, constants.DATABASE_URL, "data.db")
|
||||
assert.Equal(t, constants.DATABASE_TYPE, enum.Sqlite.String())
|
||||
assert.True(t, constants.DISABLE_EMAIL_VERIFICATION)
|
||||
assert.True(t, constants.DISABLE_MAGIC_LOGIN)
|
||||
assert.False(t, constants.DISABLE_BASIC_AUTHENTICATION)
|
||||
assert.Equal(t, constants.JWT_TYPE, "HS256")
|
||||
assert.Equal(t, constants.JWT_SECRET, "random_string")
|
||||
assert.Equal(t, constants.JWT_ROLE_CLAIM, "role")
|
||||
assert.EqualValues(t, constants.ROLES, []string{"user"})
|
||||
assert.EqualValues(t, constants.DEFAULT_ROLES, []string{"user"})
|
||||
assert.EqualValues(t, constants.PROTECTED_ROLES, []string{"admin"})
|
||||
assert.EqualValues(t, constants.ALLOWED_ORIGINS, []string{"*"})
|
||||
}
|
@@ -4,30 +4,35 @@ go 1.16
|
||||
|
||||
require (
|
||||
github.com/99designs/gqlgen v0.13.0
|
||||
github.com/coreos/go-oidc/v3 v3.1.0 // indirect
|
||||
github.com/gin-contrib/location v0.0.2 // indirect
|
||||
github.com/arangodb/go-driver v1.2.1
|
||||
github.com/coreos/go-oidc/v3 v3.1.0
|
||||
github.com/gin-contrib/location v0.0.2
|
||||
github.com/gin-gonic/gin v1.7.2
|
||||
github.com/go-playground/validator/v10 v10.8.0 // indirect
|
||||
github.com/go-redis/redis/v8 v8.11.0
|
||||
github.com/golang-jwt/jwt v3.2.1+incompatible
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/jackc/pgproto3/v2 v2.1.0 // indirect
|
||||
github.com/joho/godotenv v1.3.0
|
||||
github.com/json-iterator/go v1.1.11 // indirect
|
||||
github.com/mattn/go-isatty v0.0.13 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.7 // indirect
|
||||
github.com/robertkrimen/otto v0.0.0-20211024170158-b87d35c0b86f // indirect
|
||||
github.com/mitchellh/mapstructure v1.1.2 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||
github.com/robertkrimen/otto v0.0.0-20211024170158-b87d35c0b86f
|
||||
github.com/stretchr/testify v1.7.0 // indirect
|
||||
github.com/ugorji/go v1.2.6 // indirect
|
||||
github.com/vektah/gqlparser/v2 v2.1.0
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
|
||||
go.mongodb.org/mongo-driver v1.8.1 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gorm.io/driver/mysql v1.1.1
|
||||
gorm.io/driver/postgres v1.1.0
|
||||
gorm.io/driver/sqlite v1.1.4
|
||||
gorm.io/gorm v1.21.11
|
||||
gorm.io/driver/mysql v1.2.1
|
||||
gorm.io/driver/postgres v1.2.3
|
||||
gorm.io/driver/sqlite v1.2.6
|
||||
gorm.io/driver/sqlserver v1.2.1
|
||||
gorm.io/gorm v1.22.4
|
||||
)
|
||||
|
570
server/go.sum
570
server/go.sum
File diff suppressed because it is too large
Load Diff
@@ -49,7 +49,7 @@ func AppHandler() gin.HandlerFunc {
|
||||
stateObj.RedirectURL = strings.TrimSuffix(stateObj.RedirectURL, "/")
|
||||
|
||||
// validate redirect url with allowed origins
|
||||
if !utils.IsValidRedirectURL(stateObj.RedirectURL) {
|
||||
if !utils.IsValidOrigin(stateObj.RedirectURL) {
|
||||
c.JSON(400, gin.H{"error": "invalid redirect url"})
|
||||
return
|
||||
}
|
||||
|
@@ -40,7 +40,7 @@ func processGoogleUserInfo(code string) (db.User, error) {
|
||||
// Parse and verify ID Token payload.
|
||||
idToken, err := verifier.Verify(ctx, rawIDToken)
|
||||
if err != nil {
|
||||
return user, fmt.Errorf("unable to verify id_token:", err.Error())
|
||||
return user, fmt.Errorf("unable to verify id_token: %s", err.Error())
|
||||
}
|
||||
|
||||
// Extract custom claims
|
||||
@@ -129,7 +129,7 @@ func processFacebookUserInfo(code string) (db.User, error) {
|
||||
|
||||
response, err := client.Do(req)
|
||||
if err != nil {
|
||||
log.Println("err:", err)
|
||||
log.Println("error processing facebook user info:", err)
|
||||
return user, err
|
||||
}
|
||||
|
||||
@@ -217,6 +217,7 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
||||
}
|
||||
|
||||
user.Roles = strings.Join(inputRoles, ",")
|
||||
user, _ = db.Mgr.AddUser(user)
|
||||
} else {
|
||||
// user exists in db, check if method was google
|
||||
// if not append google to existing signup method and save it
|
||||
@@ -260,9 +261,12 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
||||
} else {
|
||||
user.Roles = existingUser.Roles
|
||||
}
|
||||
user.Key = existingUser.Key
|
||||
user.ObjectID = existingUser.ObjectID
|
||||
user.ID = existingUser.ID
|
||||
user, err = db.Mgr.UpdateUser(user)
|
||||
}
|
||||
|
||||
user, _ = db.Mgr.SaveUser(user)
|
||||
user, _ = db.Mgr.GetUserByEmail(user.Email)
|
||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, inputRoles)
|
||||
@@ -277,7 +281,7 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
||||
IP: utils.GetIP(c.Request),
|
||||
}
|
||||
|
||||
db.Mgr.SaveSession(sessionData)
|
||||
db.Mgr.AddSession(sessionData)
|
||||
}()
|
||||
|
||||
c.Redirect(http.StatusTemporaryRedirect, redirectURL)
|
||||
|
@@ -24,7 +24,7 @@ func VerifyEmailHandler() gin.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
_, err := db.Mgr.GetVerificationByToken(token)
|
||||
verificationRequest, err := db.Mgr.GetVerificationByToken(token)
|
||||
if err != nil {
|
||||
c.JSON(400, errorRes)
|
||||
return
|
||||
@@ -47,10 +47,11 @@ func VerifyEmailHandler() gin.HandlerFunc {
|
||||
|
||||
// update email_verified_at in users table
|
||||
if user.EmailVerifiedAt <= 0 {
|
||||
db.Mgr.UpdateVerificationTime(time.Now().Unix(), user.ID)
|
||||
user.EmailVerifiedAt = time.Now().Unix()
|
||||
db.Mgr.UpdateUser(user)
|
||||
}
|
||||
// delete from verification table
|
||||
db.Mgr.DeleteToken(claim.Email)
|
||||
db.Mgr.DeleteVerificationRequest(verificationRequest)
|
||||
|
||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||
roles := strings.Split(user.Roles, ",")
|
||||
@@ -66,9 +67,9 @@ func VerifyEmailHandler() gin.HandlerFunc {
|
||||
IP: utils.GetIP(c.Request),
|
||||
}
|
||||
|
||||
db.Mgr.SaveSession(sessionData)
|
||||
db.Mgr.AddSession(sessionData)
|
||||
}()
|
||||
utils.SetCookie(c, accessToken)
|
||||
c.Redirect(http.StatusTemporaryRedirect, claim.Host)
|
||||
c.Redirect(http.StatusTemporaryRedirect, claim.RedirectURL)
|
||||
}
|
||||
}
|
||||
|
44
server/integration_test/cors_test.go
Normal file
44
server/integration_test/cors_test.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package integration_test
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/env"
|
||||
"github.com/authorizerdev/authorizer/server/middlewares"
|
||||
"github.com/gin-contrib/location"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCors(t *testing.T) {
|
||||
constants.ENV_PATH = "../../.env.local"
|
||||
env.InitEnv()
|
||||
r := gin.Default()
|
||||
r.Use(location.Default())
|
||||
r.Use(middlewares.GinContextToContextMiddleware())
|
||||
r.Use(middlewares.CORSMiddleware())
|
||||
allowedOrigin := "http://localhost:8080" // The allowed origin that you want to check
|
||||
notAllowedOrigin := "http://myapp.com"
|
||||
|
||||
server := httptest.NewServer(r)
|
||||
defer server.Close()
|
||||
|
||||
client := &http.Client{}
|
||||
req, _ := http.NewRequest(
|
||||
"GET",
|
||||
"http://"+server.Listener.Addr().String()+"/api",
|
||||
nil,
|
||||
)
|
||||
req.Header.Add("Origin", allowedOrigin)
|
||||
|
||||
get, _ := client.Do(req)
|
||||
|
||||
// You should get your origin (or a * depending on your config) if the
|
||||
// passed origin is allowed.
|
||||
o := get.Header.Get("Access-Control-Allow-Origin")
|
||||
assert.NotEqual(t, o, notAllowedOrigin, "Origins should not match")
|
||||
assert.Equal(t, o, allowedOrigin, "Origins don't match")
|
||||
}
|
@@ -1,12 +1,10 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/env"
|
||||
"github.com/authorizerdev/authorizer/server/handlers"
|
||||
"github.com/authorizerdev/authorizer/server/middlewares"
|
||||
"github.com/authorizerdev/authorizer/server/oauth"
|
||||
"github.com/authorizerdev/authorizer/server/session"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
@@ -14,40 +12,8 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func GinContextToContextMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if constants.AUTHORIZER_URL == "" {
|
||||
url := location.Get(c)
|
||||
constants.AUTHORIZER_URL = url.Scheme + "://" + c.Request.Host
|
||||
log.Println("=> setting url:", constants.AUTHORIZER_URL)
|
||||
}
|
||||
ctx := context.WithValue(c.Request.Context(), "GinContextKey", c)
|
||||
c.Request = c.Request.WithContext(ctx)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO use allowed origins for cors origin
|
||||
// TODO throw error if url is not allowed
|
||||
func CORSMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
origin := c.Request.Header.Get("Origin")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
|
||||
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT")
|
||||
|
||||
if c.Request.Method == "OPTIONS" {
|
||||
c.AbortWithStatus(204)
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
InitEnv()
|
||||
env.InitEnv()
|
||||
db.InitDB()
|
||||
session.InitSession()
|
||||
oauth.InitOAuth()
|
||||
@@ -55,8 +21,8 @@ func main() {
|
||||
|
||||
r := gin.Default()
|
||||
r.Use(location.Default())
|
||||
r.Use(GinContextToContextMiddleware())
|
||||
r.Use(CORSMiddleware())
|
||||
r.Use(middlewares.GinContextToContextMiddleware())
|
||||
r.Use(middlewares.CORSMiddleware())
|
||||
|
||||
r.GET("/", handlers.PlaygroundHandler())
|
||||
r.POST("/graphql", handlers.GraphqlHandler())
|
||||
|
23
server/middlewares/context.go
Normal file
23
server/middlewares/context.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/gin-contrib/location"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func GinContextToContextMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if constants.AUTHORIZER_URL == "" {
|
||||
url := location.Get(c)
|
||||
constants.AUTHORIZER_URL = url.Scheme + "://" + c.Request.Host
|
||||
log.Println("=> authorizer url:", constants.AUTHORIZER_URL)
|
||||
}
|
||||
ctx := context.WithValue(c.Request.Context(), "GinContextKey", c)
|
||||
c.Request = c.Request.WithContext(ctx)
|
||||
c.Next()
|
||||
}
|
||||
}
|
29
server/middlewares/cors.go
Normal file
29
server/middlewares/cors.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func CORSMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
origin := c.Request.Header.Get("Origin")
|
||||
constants.APP_URL = origin
|
||||
|
||||
if utils.IsValidOrigin(origin) {
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
|
||||
}
|
||||
|
||||
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT")
|
||||
|
||||
if c.Request.Method == "OPTIONS" {
|
||||
c.AbortWithStatus(204)
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
@@ -69,7 +69,7 @@ func AdminUpdateUser(ctx context.Context, params model.AdminUpdateUserInput) (*m
|
||||
verificationType := enum.UpdateEmail.String()
|
||||
token, err := utils.CreateVerificationToken(newEmail, verificationType)
|
||||
if err != nil {
|
||||
log.Println(`Error generating token`, err)
|
||||
log.Println(`error generating token`, err)
|
||||
}
|
||||
db.Mgr.AddVerification(db.VerificationRequest{
|
||||
Token: token,
|
||||
@@ -110,7 +110,7 @@ func AdminUpdateUser(ctx context.Context, params model.AdminUpdateUserInput) (*m
|
||||
|
||||
user, err = db.Mgr.UpdateUser(user)
|
||||
if err != nil {
|
||||
log.Println("Error updating user:", err)
|
||||
log.Println("error updating user:", err)
|
||||
return res, err
|
||||
}
|
||||
|
||||
|
@@ -29,9 +29,9 @@ func DeleteUser(ctx context.Context, params model.DeleteUserInput) (*model.Respo
|
||||
|
||||
session.DeleteUserSession(fmt.Sprintf("%x", user.ID))
|
||||
|
||||
err = db.Mgr.DeleteUser(params.Email)
|
||||
err = db.Mgr.DeleteUser(user)
|
||||
if err != nil {
|
||||
log.Println("Err:", err)
|
||||
log.Println("error deleting user:", err)
|
||||
return res, err
|
||||
}
|
||||
|
||||
|
@@ -20,7 +20,7 @@ func ForgotPassword(ctx context.Context, params model.ForgotPasswordInput) (*mod
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
if constants.DISABLE_BASIC_AUTHENTICATION == "true" {
|
||||
if constants.DISABLE_BASIC_AUTHENTICATION {
|
||||
return res, fmt.Errorf(`basic authentication is disabled for this instance`)
|
||||
}
|
||||
host := gc.Request.Host
|
||||
@@ -37,7 +37,7 @@ func ForgotPassword(ctx context.Context, params model.ForgotPasswordInput) (*mod
|
||||
|
||||
token, err := utils.CreateVerificationToken(params.Email, enum.ForgotPassword.String())
|
||||
if err != nil {
|
||||
log.Println(`Error generating token`, err)
|
||||
log.Println(`error generating token`, err)
|
||||
}
|
||||
db.Mgr.AddVerification(db.VerificationRequest{
|
||||
Token: token,
|
||||
|
@@ -22,7 +22,7 @@ func Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, e
|
||||
return res, err
|
||||
}
|
||||
|
||||
if constants.DISABLE_BASIC_AUTHENTICATION == "true" {
|
||||
if constants.DISABLE_BASIC_AUTHENTICATION {
|
||||
return res, fmt.Errorf(`basic authentication is disabled for this instance`)
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ func Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, e
|
||||
err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(params.Password))
|
||||
|
||||
if err != nil {
|
||||
log.Println("Compare password error:", err)
|
||||
log.Println("compare password error:", err)
|
||||
return res, fmt.Errorf(`invalid password`)
|
||||
}
|
||||
roles := constants.DEFAULT_ROLES
|
||||
@@ -68,7 +68,7 @@ func Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, e
|
||||
IP: utils.GetIP(gc.Request),
|
||||
}
|
||||
|
||||
db.Mgr.SaveSession(sessionData)
|
||||
db.Mgr.AddSession(sessionData)
|
||||
}()
|
||||
|
||||
res = &model.AuthResponse{
|
||||
|
@@ -27,7 +27,7 @@ func Logout(ctx context.Context) (*model.Response, error) {
|
||||
}
|
||||
|
||||
userId := fmt.Sprintf("%v", claim["id"])
|
||||
session.DeleteToken(userId, token)
|
||||
session.DeleteVerificationRequest(userId, token)
|
||||
res = &model.Response{
|
||||
Message: "Logged out successfully",
|
||||
}
|
||||
|
@@ -17,7 +17,7 @@ import (
|
||||
func MagicLogin(ctx context.Context, params model.MagicLoginInput) (*model.Response, error) {
|
||||
var res *model.Response
|
||||
|
||||
if constants.DISABLE_MAGIC_LOGIN == "true" {
|
||||
if constants.DISABLE_MAGIC_LOGIN {
|
||||
return res, fmt.Errorf(`magic link login is disabled for this instance`)
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ func MagicLogin(ctx context.Context, params model.MagicLoginInput) (*model.Respo
|
||||
|
||||
// find user with email
|
||||
existingUser, err := db.Mgr.GetUserByEmail(params.Email)
|
||||
|
||||
if err != nil {
|
||||
user.SignupMethod = enum.MagicLink.String()
|
||||
// define roles for new user
|
||||
@@ -50,6 +51,7 @@ func MagicLogin(ctx context.Context, params model.MagicLoginInput) (*model.Respo
|
||||
}
|
||||
|
||||
user.Roles = strings.Join(inputRoles, ",")
|
||||
user, _ = db.Mgr.AddUser(user)
|
||||
} else {
|
||||
user = existingUser
|
||||
// There multiple scenarios with roles here in magic link login
|
||||
@@ -90,16 +92,18 @@ func MagicLogin(ctx context.Context, params model.MagicLoginInput) (*model.Respo
|
||||
}
|
||||
|
||||
user.SignupMethod = signupMethod
|
||||
user, _ = db.Mgr.UpdateUser(user)
|
||||
if err != nil {
|
||||
log.Println("error updating user:", err)
|
||||
}
|
||||
}
|
||||
|
||||
user, _ = db.Mgr.SaveUser(user)
|
||||
|
||||
if constants.DISABLE_EMAIL_VERIFICATION != "true" {
|
||||
if !constants.DISABLE_EMAIL_VERIFICATION {
|
||||
// insert verification request
|
||||
verificationType := enum.MagicLink.String()
|
||||
token, err := utils.CreateVerificationToken(params.Email, verificationType)
|
||||
if err != nil {
|
||||
log.Println(`Error generating token`, err)
|
||||
log.Println(`error generating token`, err)
|
||||
}
|
||||
db.Mgr.AddVerification(db.VerificationRequest{
|
||||
Token: token,
|
||||
|
@@ -27,7 +27,7 @@ func ResendVerifyEmail(ctx context.Context, params model.ResendVerifyEmailInput)
|
||||
|
||||
token, err := utils.CreateVerificationToken(params.Email, verificationRequest.Identifier)
|
||||
if err != nil {
|
||||
log.Println(`Error generating token`, err)
|
||||
log.Println(`error generating token`, err)
|
||||
}
|
||||
db.Mgr.AddVerification(db.VerificationRequest{
|
||||
Token: token,
|
||||
|
@@ -14,11 +14,11 @@ import (
|
||||
|
||||
func ResetPassword(ctx context.Context, params model.ResetPasswordInput) (*model.Response, error) {
|
||||
var res *model.Response
|
||||
if constants.DISABLE_BASIC_AUTHENTICATION == "true" {
|
||||
if constants.DISABLE_BASIC_AUTHENTICATION {
|
||||
return res, fmt.Errorf(`basic authentication is disabled for this instance`)
|
||||
}
|
||||
|
||||
_, err := db.Mgr.GetVerificationByToken(params.Token)
|
||||
verificationRequest, err := db.Mgr.GetVerificationByToken(params.Token)
|
||||
if err != nil {
|
||||
return res, fmt.Errorf(`invalid token`)
|
||||
}
|
||||
@@ -48,7 +48,7 @@ func ResetPassword(ctx context.Context, params model.ResetPasswordInput) (*model
|
||||
user.SignupMethod = signupMethod
|
||||
|
||||
// delete from verification table
|
||||
db.Mgr.DeleteToken(claim.Email)
|
||||
db.Mgr.DeleteVerificationRequest(verificationRequest)
|
||||
db.Mgr.UpdateUser(user)
|
||||
|
||||
res = &model.Response{
|
||||
|
@@ -22,11 +22,11 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
|
||||
return res, err
|
||||
}
|
||||
|
||||
if constants.DISABLE_BASIC_AUTHENTICATION == "true" {
|
||||
if constants.DISABLE_BASIC_AUTHENTICATION {
|
||||
return res, fmt.Errorf(`basic authentication is disabled for this instance`)
|
||||
}
|
||||
if params.ConfirmPassword != params.Password {
|
||||
return res, fmt.Errorf(`passowrd and confirm password does not match`)
|
||||
return res, fmt.Errorf(`password and confirm password does not match`)
|
||||
}
|
||||
|
||||
params.Email = strings.ToLower(params.Email)
|
||||
@@ -51,13 +51,16 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
|
||||
// find user with email
|
||||
existingUser, err := db.Mgr.GetUserByEmail(params.Email)
|
||||
if err != nil {
|
||||
log.Println("User with email " + params.Email + " not found")
|
||||
log.Println("user with email " + params.Email + " not found")
|
||||
}
|
||||
|
||||
if existingUser.EmailVerifiedAt > 0 {
|
||||
// email is verified
|
||||
return res, fmt.Errorf(`you have already signed up. Please login`)
|
||||
return res, fmt.Errorf(`%s has already signed up`, params.Email)
|
||||
} else if existingUser.ID != "" && existingUser.EmailVerifiedAt <= 0 {
|
||||
return res, fmt.Errorf("%s has already signed up. please complete the email verification process or reset the password", params.Email)
|
||||
}
|
||||
|
||||
user := db.User{
|
||||
Email: params.Email,
|
||||
}
|
||||
@@ -76,10 +79,10 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
|
||||
}
|
||||
|
||||
user.SignupMethod = enum.BasicAuth.String()
|
||||
if constants.DISABLE_EMAIL_VERIFICATION == "true" {
|
||||
if constants.DISABLE_EMAIL_VERIFICATION {
|
||||
user.EmailVerifiedAt = time.Now().Unix()
|
||||
}
|
||||
user, err = db.Mgr.SaveUser(user)
|
||||
user, err = db.Mgr.AddUser(user)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
@@ -98,12 +101,12 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
|
||||
UpdatedAt: &user.UpdatedAt,
|
||||
}
|
||||
|
||||
if constants.DISABLE_EMAIL_VERIFICATION != "true" {
|
||||
if !constants.DISABLE_EMAIL_VERIFICATION {
|
||||
// insert verification request
|
||||
verificationType := enum.BasicAuthSignup.String()
|
||||
token, err := utils.CreateVerificationToken(params.Email, verificationType)
|
||||
if err != nil {
|
||||
log.Println(`Error generating token`, err)
|
||||
log.Println(`error generating token`, err)
|
||||
}
|
||||
db.Mgr.AddVerification(db.VerificationRequest{
|
||||
Token: token,
|
||||
@@ -135,7 +138,7 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
|
||||
IP: utils.GetIP(gc.Request),
|
||||
}
|
||||
|
||||
db.Mgr.SaveSession(sessionData)
|
||||
db.Mgr.AddSession(sessionData)
|
||||
}()
|
||||
res = &model.AuthResponse{
|
||||
Message: `Signed up successfully.`,
|
||||
|
@@ -64,7 +64,7 @@ func Token(ctx context.Context, roles []string) (*model.AuthResponse, error) {
|
||||
// if access token has expired and refresh/session token is valid
|
||||
// generate new accessToken
|
||||
currentRefreshToken := session.GetToken(userIdStr, token)
|
||||
session.DeleteToken(userIdStr, token)
|
||||
session.DeleteVerificationRequest(userIdStr, token)
|
||||
token, expiresAt, _ = utils.CreateAuthToken(user, enum.AccessToken, claimRoles)
|
||||
session.SetToken(userIdStr, token, currentRefreshToken)
|
||||
go func() {
|
||||
@@ -74,7 +74,7 @@ func Token(ctx context.Context, roles []string) (*model.AuthResponse, error) {
|
||||
IP: utils.GetIP(gc.Request),
|
||||
}
|
||||
|
||||
db.Mgr.SaveSession(sessionData)
|
||||
db.Mgr.AddSession(sessionData)
|
||||
}()
|
||||
}
|
||||
|
||||
|
@@ -109,7 +109,7 @@ func UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model
|
||||
verificationType := enum.UpdateEmail.String()
|
||||
token, err := utils.CreateVerificationToken(newEmail, verificationType)
|
||||
if err != nil {
|
||||
log.Println(`Error generating token`, err)
|
||||
log.Println(`error generating token`, err)
|
||||
}
|
||||
db.Mgr.AddVerification(db.VerificationRequest{
|
||||
Token: token,
|
||||
@@ -126,7 +126,7 @@ func UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model
|
||||
|
||||
_, err = db.Mgr.UpdateUser(user)
|
||||
if err != nil {
|
||||
log.Println("Error updating user:", err)
|
||||
log.Println("error updating user:", err)
|
||||
return res, err
|
||||
}
|
||||
message := `Profile details updated successfully.`
|
||||
|
@@ -20,7 +20,7 @@ func VerifyEmail(ctx context.Context, params model.VerifyEmailInput) (*model.Aut
|
||||
return res, err
|
||||
}
|
||||
|
||||
_, err = db.Mgr.GetVerificationByToken(params.Token)
|
||||
verificationRequest, err := db.Mgr.GetVerificationByToken(params.Token)
|
||||
if err != nil {
|
||||
return res, fmt.Errorf(`invalid token`)
|
||||
}
|
||||
@@ -37,9 +37,10 @@ func VerifyEmail(ctx context.Context, params model.VerifyEmailInput) (*model.Aut
|
||||
}
|
||||
|
||||
// update email_verified_at in users table
|
||||
db.Mgr.UpdateVerificationTime(time.Now().Unix(), user.ID)
|
||||
user.EmailVerifiedAt = time.Now().Unix()
|
||||
db.Mgr.UpdateUser(user)
|
||||
// delete from verification table
|
||||
db.Mgr.DeleteToken(claim.Email)
|
||||
db.Mgr.DeleteVerificationRequest(verificationRequest)
|
||||
|
||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||
roles := strings.Split(user.Roles, ",")
|
||||
@@ -55,7 +56,7 @@ func VerifyEmail(ctx context.Context, params model.VerifyEmailInput) (*model.Aut
|
||||
IP: utils.GetIP(gc.Request),
|
||||
}
|
||||
|
||||
db.Mgr.SaveSession(sessionData)
|
||||
db.Mgr.AddSession(sessionData)
|
||||
}()
|
||||
|
||||
res = &model.AuthResponse{
|
||||
|
@@ -1,7 +1,6 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sync"
|
||||
)
|
||||
|
||||
@@ -30,8 +29,6 @@ func (c *InMemoryStore) AddToken(userId, accessToken, refreshToken string) {
|
||||
c.store[userId] = tempMap
|
||||
}
|
||||
|
||||
log.Println(c.store)
|
||||
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
@@ -41,7 +38,7 @@ func (c *InMemoryStore) DeleteUserSession(userId string) {
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
func (c *InMemoryStore) DeleteToken(userId, accessToken string) {
|
||||
func (c *InMemoryStore) DeleteVerificationRequest(userId, accessToken string) {
|
||||
c.mu.Lock()
|
||||
delete(c.store[userId], accessToken)
|
||||
c.mu.Unlock()
|
||||
|
@@ -29,7 +29,7 @@ func (c *RedisStore) DeleteUserSession(userId string) {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RedisStore) DeleteToken(userId, accessToken string) {
|
||||
func (c *RedisStore) DeleteVerificationRequest(userId, accessToken string) {
|
||||
err := c.store.HDel(c.ctx, "authorizer_"+userId, accessToken).Err()
|
||||
if err != nil {
|
||||
log.Fatalln("Error deleting redis token:", err)
|
||||
@@ -47,7 +47,7 @@ func (c *RedisStore) GetToken(userId, accessToken string) string {
|
||||
token := ""
|
||||
res, err := c.store.HMGet(c.ctx, "authorizer_"+userId, accessToken).Result()
|
||||
if err != nil {
|
||||
log.Println("Error getting token from redis store:", err)
|
||||
log.Println("error getting token from redis store:", err)
|
||||
}
|
||||
if len(res) > 0 && res[0] != nil {
|
||||
token = fmt.Sprintf("%v", res[0])
|
||||
@@ -66,7 +66,7 @@ func (c *RedisStore) GetSocialLoginState(key string) string {
|
||||
state := ""
|
||||
state, err := c.store.Get(c.ctx, key).Result()
|
||||
if err != nil {
|
||||
log.Println("Error getting token from redis store:", err)
|
||||
log.Println("error getting token from redis store:", err)
|
||||
}
|
||||
|
||||
return state
|
||||
|
@@ -27,12 +27,12 @@ func SetToken(userId, accessToken, refreshToken string) {
|
||||
}
|
||||
}
|
||||
|
||||
func DeleteToken(userId, accessToken string) {
|
||||
func DeleteVerificationRequest(userId, accessToken string) {
|
||||
if SessionStoreObj.RedisMemoryStoreObj != nil {
|
||||
SessionStoreObj.RedisMemoryStoreObj.DeleteToken(userId, accessToken)
|
||||
SessionStoreObj.RedisMemoryStoreObj.DeleteVerificationRequest(userId, accessToken)
|
||||
}
|
||||
if SessionStoreObj.InMemoryStoreObj != nil {
|
||||
SessionStoreObj.InMemoryStoreObj.DeleteToken(userId, accessToken)
|
||||
SessionStoreObj.InMemoryStoreObj.DeleteVerificationRequest(userId, accessToken)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ func RemoveSocialLoginState(key string) {
|
||||
|
||||
func InitSession() {
|
||||
if constants.REDIS_URL != "" {
|
||||
log.Println("Using redis store to save sessions")
|
||||
log.Println("using redis store to save sessions")
|
||||
opt, err := redis.ParseURL(constants.REDIS_URL)
|
||||
if err != nil {
|
||||
log.Fatalln("Error parsing redis url:", err)
|
||||
@@ -114,7 +114,7 @@ func InitSession() {
|
||||
}
|
||||
|
||||
} else {
|
||||
log.Println("Using in memory store to save sessions")
|
||||
log.Println("using in memory store to save sessions")
|
||||
SessionStoreObj.InMemoryStoreObj = &InMemoryStore{
|
||||
store: map[string]map[string]string{},
|
||||
socialLoginState: map[string]string{},
|
||||
|
@@ -64,12 +64,12 @@ func CreateAuthToken(user db.User, tokenType enum.TokenType, roles []string) (st
|
||||
val, err := vm.Get("functionRes")
|
||||
|
||||
if err != nil {
|
||||
log.Println("=> err custom access token script:", err)
|
||||
log.Println("error getting custom access token script:", err)
|
||||
} else {
|
||||
extraPayload := make(map[string]interface{})
|
||||
err = json.Unmarshal([]byte(fmt.Sprintf("%s", val)), &extraPayload)
|
||||
if err != nil {
|
||||
log.Println("Error converting accessTokenScript response to map:", err)
|
||||
log.Println("error converting accessTokenScript response to map:", err)
|
||||
} else {
|
||||
for k, v := range extraPayload {
|
||||
customClaims[k] = v
|
||||
|
@@ -1,7 +1,6 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
@@ -11,17 +10,24 @@ import (
|
||||
func SetCookie(gc *gin.Context, token string) {
|
||||
secure := true
|
||||
httpOnly := true
|
||||
host, _ := GetHostParts(constants.AUTHORIZER_URL)
|
||||
domain := GetDomainName(constants.AUTHORIZER_URL)
|
||||
if domain != "localhost" {
|
||||
domain = "." + domain
|
||||
}
|
||||
|
||||
host := GetHostName(constants.AUTHORIZER_URL)
|
||||
log.Println("=> cookie host", host)
|
||||
gc.SetSameSite(http.SameSiteNoneMode)
|
||||
gc.SetCookie(constants.COOKIE_NAME, token, 3600, "/", host, secure, httpOnly)
|
||||
gc.SetCookie(constants.COOKIE_NAME+"-client", token, 3600, "/", domain, secure, httpOnly)
|
||||
}
|
||||
|
||||
func GetCookie(gc *gin.Context) (string, error) {
|
||||
cookie, err := gc.Request.Cookie(constants.COOKIE_NAME)
|
||||
if err != nil {
|
||||
return "", err
|
||||
cookie, err = gc.Request.Cookie(constants.COOKIE_NAME + "-client")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
return cookie.Value, nil
|
||||
@@ -31,11 +37,13 @@ func DeleteCookie(gc *gin.Context) {
|
||||
secure := true
|
||||
httpOnly := true
|
||||
|
||||
if !constants.IS_PROD {
|
||||
secure = false
|
||||
host, _ := GetHostParts(constants.AUTHORIZER_URL)
|
||||
domain := GetDomainName(constants.AUTHORIZER_URL)
|
||||
if domain != "localhost" {
|
||||
domain = "." + domain
|
||||
}
|
||||
|
||||
host := GetHostName(constants.AUTHORIZER_URL)
|
||||
gc.SetSameSite(http.SameSiteNoneMode)
|
||||
gc.SetCookie(constants.COOKIE_NAME, "", -1, "/", host, secure, httpOnly)
|
||||
gc.SetCookie(constants.COOKIE_NAME+"-client", "", -1, "/", domain, secure, httpOnly)
|
||||
}
|
||||
|
@@ -1,30 +1,6 @@
|
||||
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,
|
||||
})
|
||||
}
|
||||
for _, val := range constants.PROTECTED_ROLES {
|
||||
roles = append(roles, db.Role{
|
||||
Role: val,
|
||||
})
|
||||
}
|
||||
err := db.Mgr.SaveRoles(roles)
|
||||
if err != nil {
|
||||
log.Println(`Error saving roles`, err)
|
||||
}
|
||||
}
|
||||
|
@@ -14,8 +14,8 @@ func GetMetaInfo() model.Meta {
|
||||
IsGithubLoginEnabled: constants.GITHUB_CLIENT_ID != "" && constants.GOOGLE_CLIENT_SECRET != "",
|
||||
IsFacebookLoginEnabled: constants.FACEBOOK_CLIENT_ID != "" && constants.FACEBOOK_CLIENT_SECRET != "",
|
||||
IsTwitterLoginEnabled: constants.TWITTER_CLIENT_ID != "" && constants.TWITTER_CLIENT_SECRET != "",
|
||||
IsBasicAuthenticationEnabled: constants.DISABLE_BASIC_AUTHENTICATION != "true",
|
||||
IsEmailVerificationEnabled: constants.DISABLE_EMAIL_VERIFICATION != "true",
|
||||
IsMagicLoginEnabled: constants.DISABLE_MAGIC_LOGIN != "true" && constants.DISABLE_EMAIL_VERIFICATION != "true",
|
||||
IsBasicAuthenticationEnabled: !constants.DISABLE_BASIC_AUTHENTICATION,
|
||||
IsEmailVerificationEnabled: !constants.DISABLE_EMAIL_VERIFICATION,
|
||||
IsMagicLoginEnabled: !constants.DISABLE_MAGIC_LOGIN,
|
||||
}
|
||||
}
|
||||
|
@@ -5,21 +5,32 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// function to get hostname
|
||||
func GetHostName(auth_url string) string {
|
||||
u, err := url.Parse(auth_url)
|
||||
// GetHostName function returns hostname and port
|
||||
func GetHostParts(uri string) (string, string) {
|
||||
tempURI := uri
|
||||
if !strings.HasPrefix(tempURI, "http") && strings.HasPrefix(tempURI, "https") {
|
||||
tempURI = "https://" + tempURI
|
||||
}
|
||||
|
||||
u, err := url.Parse(tempURI)
|
||||
if err != nil {
|
||||
return `localhost`
|
||||
return "localhost", "8080"
|
||||
}
|
||||
|
||||
host := u.Hostname()
|
||||
port := u.Port()
|
||||
|
||||
return host
|
||||
return host, port
|
||||
}
|
||||
|
||||
// function to get domain name
|
||||
func GetDomainName(auth_url string) string {
|
||||
u, err := url.Parse(auth_url)
|
||||
// GetDomainName function to get domain name
|
||||
func GetDomainName(uri string) string {
|
||||
tempURI := uri
|
||||
if !strings.HasPrefix(tempURI, "http") && strings.HasPrefix(tempURI, "https") {
|
||||
tempURI = "https://" + tempURI
|
||||
}
|
||||
|
||||
u, err := url.Parse(tempURI)
|
||||
if err != nil {
|
||||
return `localhost`
|
||||
}
|
||||
|
26
server/utils/urls_test.go
Normal file
26
server/utils/urls_test.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetHostName(t *testing.T) {
|
||||
authorizer_url := "http://test.herokuapp.com:80"
|
||||
|
||||
host, port := GetHostParts(authorizer_url)
|
||||
expectedHost := "test.herokuapp.com"
|
||||
|
||||
assert.Equal(t, host, expectedHost, "hostname should be equal")
|
||||
assert.Equal(t, port, "80", "port should be 80")
|
||||
}
|
||||
|
||||
func TestGetDomainName(t *testing.T) {
|
||||
authorizer_url := "http://test.herokuapp.com"
|
||||
|
||||
got := GetDomainName(authorizer_url)
|
||||
want := "herokuapp.com"
|
||||
|
||||
assert.Equal(t, got, want, "domain name should be equal")
|
||||
}
|
@@ -2,6 +2,7 @@ package utils
|
||||
|
||||
import (
|
||||
"net/mail"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
@@ -13,16 +14,32 @@ func IsValidEmail(email string) bool {
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func IsValidRedirectURL(url string) bool {
|
||||
func IsValidOrigin(url string) bool {
|
||||
if len(constants.ALLOWED_ORIGINS) == 1 && constants.ALLOWED_ORIGINS[0] == "*" {
|
||||
return true
|
||||
}
|
||||
|
||||
hasValidURL := false
|
||||
urlDomain := GetDomainName(url)
|
||||
hostName, port := GetHostParts(url)
|
||||
currentOrigin := hostName + ":" + port
|
||||
|
||||
for _, val := range constants.ALLOWED_ORIGINS {
|
||||
if strings.Contains(val, urlDomain) {
|
||||
for _, origin := range constants.ALLOWED_ORIGINS {
|
||||
replacedString := origin
|
||||
// if has regex whitelisted domains
|
||||
if strings.Contains(origin, "*") {
|
||||
replacedString = strings.Replace(origin, ".", "\\.", -1)
|
||||
replacedString = strings.Replace(replacedString, "*", ".*", -1)
|
||||
|
||||
if strings.HasPrefix(replacedString, ".*") {
|
||||
replacedString += "\\b"
|
||||
}
|
||||
|
||||
if strings.HasSuffix(replacedString, ".*") {
|
||||
replacedString = "\\b" + replacedString
|
||||
}
|
||||
}
|
||||
|
||||
if matched, _ := regexp.MatchString(replacedString, currentOrigin); matched {
|
||||
hasValidURL = true
|
||||
break
|
||||
}
|
||||
|
34
server/utils/validator_test.go
Normal file
34
server/utils/validator_test.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIsValidEmail(t *testing.T) {
|
||||
validEmail := "lakhan@gmail.com"
|
||||
invalidEmail1 := "lakhan"
|
||||
invalidEmail2 := "lakhan.me"
|
||||
|
||||
assert.True(t, IsValidEmail(validEmail), "it should be valid email")
|
||||
assert.False(t, IsValidEmail(invalidEmail1), "it should be invalid email")
|
||||
assert.False(t, IsValidEmail(invalidEmail2), "it should be invalid email")
|
||||
}
|
||||
|
||||
func TestIsValidOrigin(t *testing.T) {
|
||||
// don't use portocal(http/https) for ALLOWED_ORIGINS while testing,
|
||||
// as we trim them off while running the main function
|
||||
constants.ALLOWED_ORIGINS = []string{"localhost:8080", "*.google.com", "*.google.in", "*abc.*"}
|
||||
|
||||
assert.False(t, IsValidOrigin("http://myapp.com"), "it should be invalid origin")
|
||||
assert.False(t, IsValidOrigin("http://appgoogle.com"), "it should be invalid origin")
|
||||
assert.True(t, IsValidOrigin("http://app.google.com"), "it should be valid origin")
|
||||
assert.False(t, IsValidOrigin("http://app.google.ind"), "it should be invalid origin")
|
||||
assert.True(t, IsValidOrigin("http://app.google.in"), "it should be valid origin")
|
||||
assert.True(t, IsValidOrigin("http://xyx.abc.com"), "it should be valid origin")
|
||||
assert.True(t, IsValidOrigin("http://xyx.abc.in"), "it should be valid origin")
|
||||
assert.True(t, IsValidOrigin("http://xyxabc.in"), "it should be valid origin")
|
||||
assert.True(t, IsValidOrigin("http://localhost:8080"), "it should be valid origin")
|
||||
}
|
@@ -8,8 +8,9 @@ import (
|
||||
)
|
||||
|
||||
type UserInfo struct {
|
||||
Email string `json:"email"`
|
||||
Host string `json:"host"`
|
||||
Email string `json:"email"`
|
||||
Host string `json:"host"`
|
||||
RedirectURL string `json:"redirect_url"`
|
||||
}
|
||||
|
||||
type CustomClaim struct {
|
||||
@@ -18,7 +19,6 @@ type CustomClaim struct {
|
||||
UserInfo
|
||||
}
|
||||
|
||||
// TODO convert tokenType to enum
|
||||
func CreateVerificationToken(email string, tokenType string) (string, error) {
|
||||
t := jwt.New(jwt.GetSigningMethod(constants.JWT_TYPE))
|
||||
|
||||
@@ -28,7 +28,7 @@ func CreateVerificationToken(email string, tokenType string) (string, error) {
|
||||
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
||||
},
|
||||
tokenType,
|
||||
UserInfo{Email: email, Host: constants.AUTHORIZER_URL},
|
||||
UserInfo{Email: email, Host: constants.AUTHORIZER_URL, RedirectURL: constants.APP_URL},
|
||||
}
|
||||
|
||||
return t.SignedString([]byte(constants.JWT_SECRET))
|
||||
|
Reference in New Issue
Block a user