Compare commits
26 Commits
0.1.0-beta
...
0.1.0-beta
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b376ee3b73 | ||
![]() |
27944cf7b5 | ||
![]() |
cab0b54567 | ||
![]() |
a50f8eba57 | ||
![]() |
814dc61414 | ||
![]() |
7abad9db01 | ||
![]() |
17777b111d | ||
![]() |
c7494a0bca | ||
![]() |
e23d6f92e5 | ||
![]() |
585091fefb | ||
![]() |
eabd88718d | ||
![]() |
072cd46809 | ||
![]() |
17676fa13b | ||
![]() |
8b510ed556 | ||
![]() |
3ac0b44446 | ||
![]() |
b1dd6f2c3b | ||
![]() |
f5ea94f63c | ||
![]() |
653befc737 | ||
![]() |
6fed439ec2 | ||
![]() |
5bd6fa5bc9 | ||
![]() |
75709e9f48 | ||
![]() |
b1b7f47f4c | ||
![]() |
e429a1f860 | ||
![]() |
6819597a79 | ||
![]() |
b34b385be5 | ||
![]() |
5acf59d16e |
@@ -5,6 +5,7 @@ ADMIN_SECRET=admin
|
||||
DISABLE_EMAIL_VERIFICATION=true
|
||||
JWT_SECRET=random_string
|
||||
JWT_TYPE=HS256
|
||||
ROLES=user,admin
|
||||
DEFAULT_ROLE=user
|
||||
ROLES=user
|
||||
DEFAULT_ROLES=user
|
||||
PROTECTED_ROLES=admin
|
||||
JWT_ROLE_CLAIM=role
|
12
.github/workflows/release.yaml
vendored
12
.github/workflows/release.yaml
vendored
@@ -41,32 +41,34 @@ jobs:
|
||||
make clean && \
|
||||
CGO_ENABLED=1 GOOS=windows CC=/usr/bin/x86_64-w64-mingw32-gcc make && \
|
||||
mv build/server build/server.exe && \
|
||||
zip -vr authorizer-${VERSION}-windows-amd64.zip .env app build templates
|
||||
zip -vr authorizer-${VERSION}-windows-amd64.zip .env app/build build templates
|
||||
- name: Package files for linux
|
||||
run: |
|
||||
make clean && \
|
||||
CGO_ENABLED=1 make && \
|
||||
tar cvfz authorizer-${VERSION}-linux-amd64.tar.gz .env app build templates
|
||||
tar cvfz authorizer-${VERSION}-linux-amd64.tar.gz .env app/build build templates
|
||||
- name: Upload assets
|
||||
run: |
|
||||
github-assets-uploader -f authorizer-${VERSION}-windows-amd64.zip -mediatype application/zip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION} && \
|
||||
github-assets-uploader -f authorizer-${VERSION}-linux-amd64.tar.gz -mediatype application/gzip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
|
||||
uses: docker/metadata-action@v3
|
||||
with:
|
||||
images: lakhansamani/authorizer
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
build-args: |
|
||||
VERSION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }}
|
||||
|
@@ -3,9 +3,10 @@ WORKDIR /app
|
||||
COPY server server
|
||||
COPY Makefile .
|
||||
|
||||
ARG VERSION=0.1.0-beta.0
|
||||
ENV VERSION="${VERSION}"
|
||||
ARG VERSION="latest"
|
||||
ENV VERSION="$VERSION"
|
||||
|
||||
RUN echo "$VERSION"
|
||||
RUN apk add build-base &&\
|
||||
make clean && make && \
|
||||
chmod 777 build/server
|
||||
|
6
TODO.md
6
TODO.md
@@ -14,3 +14,9 @@ For the first version we will only support setting roles master list via env
|
||||
- [x] Return roles in users list for super admin
|
||||
- [x] Add roles to the JWT token generation
|
||||
- [x] Validate token should also validate the role, if roles to validate again is present in request
|
||||
|
||||
# Misc
|
||||
|
||||
- [x] Fix email template
|
||||
- [x] Add support for organization name in .env
|
||||
- [x] Add support for organization logo in .env
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
30
app/package-lock.json
generated
30
app/package-lock.json
generated
@@ -8,7 +8,7 @@
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@authorizerdev/authorizer-react": "^0.1.0-beta.16",
|
||||
"@authorizerdev/authorizer-react": "^0.1.0-beta.19",
|
||||
"@types/react": "^17.0.15",
|
||||
"@types/react-dom": "^17.0.9",
|
||||
"esbuild": "^0.12.17",
|
||||
@@ -22,9 +22,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@authorizerdev/authorizer-js": {
|
||||
"version": "0.1.0-beta.16",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.1.0-beta.16.tgz",
|
||||
"integrity": "sha512-RyjWhVbLYvmkcAT+2ddpRhrt7P5DBVKtGKjHWRugSqenTW7XFQMlhQqx5Z09DeqLw3Lsq0VVZ5h8tWcExHvwEw==",
|
||||
"version": "0.1.0-beta.22",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.1.0-beta.22.tgz",
|
||||
"integrity": "sha512-fHyZDL49eEsbxxIb2xxW6iLM+/N60m5e2NeVVlMFZjZFX9eUetdFatCOrPTuukGul7l8wO6YofTnwqOLOVjKrA==",
|
||||
"dependencies": {
|
||||
"node-fetch": "^2.6.1"
|
||||
},
|
||||
@@ -33,11 +33,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@authorizerdev/authorizer-react": {
|
||||
"version": "0.1.0-beta.16",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.1.0-beta.16.tgz",
|
||||
"integrity": "sha512-b00LH0gtfMh/opFaGDF+EkxDOpNkCj/TNVjyW2wbiOFdC4HgLhc+UbPgL5/rDDol4XQsToL3SmwxmSxB2lWWuQ==",
|
||||
"version": "0.1.0-beta.19",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.1.0-beta.19.tgz",
|
||||
"integrity": "sha512-ScAG3Auu0KirxuxpQ25JGtYCDmiC/3DM/ngnGO+XBaW+bWUARifH2HPkOnddUxkKPAfrKyxoa/l730W8mxhy+A==",
|
||||
"dependencies": {
|
||||
"@authorizerdev/authorizer-js": "^0.1.0-beta.16",
|
||||
"@authorizerdev/authorizer-js": "^0.1.0-beta.22",
|
||||
"final-form": "^4.20.2",
|
||||
"react-final-form": "^6.5.3",
|
||||
"styled-components": "^5.3.0"
|
||||
@@ -797,19 +797,19 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@authorizerdev/authorizer-js": {
|
||||
"version": "0.1.0-beta.16",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.1.0-beta.16.tgz",
|
||||
"integrity": "sha512-RyjWhVbLYvmkcAT+2ddpRhrt7P5DBVKtGKjHWRugSqenTW7XFQMlhQqx5Z09DeqLw3Lsq0VVZ5h8tWcExHvwEw==",
|
||||
"version": "0.1.0-beta.22",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.1.0-beta.22.tgz",
|
||||
"integrity": "sha512-fHyZDL49eEsbxxIb2xxW6iLM+/N60m5e2NeVVlMFZjZFX9eUetdFatCOrPTuukGul7l8wO6YofTnwqOLOVjKrA==",
|
||||
"requires": {
|
||||
"node-fetch": "^2.6.1"
|
||||
}
|
||||
},
|
||||
"@authorizerdev/authorizer-react": {
|
||||
"version": "0.1.0-beta.16",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.1.0-beta.16.tgz",
|
||||
"integrity": "sha512-b00LH0gtfMh/opFaGDF+EkxDOpNkCj/TNVjyW2wbiOFdC4HgLhc+UbPgL5/rDDol4XQsToL3SmwxmSxB2lWWuQ==",
|
||||
"version": "0.1.0-beta.19",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.1.0-beta.19.tgz",
|
||||
"integrity": "sha512-ScAG3Auu0KirxuxpQ25JGtYCDmiC/3DM/ngnGO+XBaW+bWUARifH2HPkOnddUxkKPAfrKyxoa/l730W8mxhy+A==",
|
||||
"requires": {
|
||||
"@authorizerdev/authorizer-js": "^0.1.0-beta.16",
|
||||
"@authorizerdev/authorizer-js": "^0.1.0-beta.22",
|
||||
"final-form": "^4.20.2",
|
||||
"react-final-form": "^6.5.3",
|
||||
"styled-components": "^5.3.0"
|
||||
|
@@ -10,7 +10,7 @@
|
||||
"author": "Lakhan Samani",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@authorizerdev/authorizer-react": "^0.1.0-beta.16",
|
||||
"@authorizerdev/authorizer-react": "^0.1.0-beta.19",
|
||||
"@types/react": "^17.0.15",
|
||||
"@types/react-dom": "^17.0.9",
|
||||
"esbuild": "^0.12.17",
|
||||
|
@@ -4,30 +4,52 @@ import { AuthorizerProvider } from '@authorizerdev/authorizer-react';
|
||||
import Root from './Root';
|
||||
|
||||
export default function App() {
|
||||
// @ts-ignore
|
||||
const globalState: Record<string, string> = window['__authorizer__'];
|
||||
return (
|
||||
<div style={{ display: 'flex', justifyContent: 'center' }}>
|
||||
<div
|
||||
style={{
|
||||
width: 400,
|
||||
margin: `10px auto`,
|
||||
border: `1px solid #D1D5DB`,
|
||||
padding: `25px 20px`,
|
||||
borderRadius: 5,
|
||||
}}
|
||||
>
|
||||
<BrowserRouter>
|
||||
<AuthorizerProvider
|
||||
config={{
|
||||
authorizerURL: globalState.authorizerURL,
|
||||
redirectURL: globalState.redirectURL,
|
||||
}}
|
||||
>
|
||||
<Root />
|
||||
</AuthorizerProvider>
|
||||
</BrowserRouter>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
// @ts-ignore
|
||||
const globalState: Record<string, string> = window['__authorizer__'];
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
marginTop: 20,
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={`${globalState.organizationLogo}`}
|
||||
alt="logo"
|
||||
style={{ height: 60, width: 60, objectFit: 'cover' }}
|
||||
/>
|
||||
<h1>{globalState.organizationName}</h1>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
width: 400,
|
||||
margin: `10px auto`,
|
||||
border: `1px solid #D1D5DB`,
|
||||
padding: `25px 20px`,
|
||||
borderRadius: 5,
|
||||
}}
|
||||
>
|
||||
<BrowserRouter>
|
||||
<AuthorizerProvider
|
||||
config={{
|
||||
authorizerURL: globalState.authorizerURL,
|
||||
redirectURL: globalState.redirectURL,
|
||||
}}
|
||||
>
|
||||
<Root />
|
||||
</AuthorizerProvider>
|
||||
</BrowserRouter>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@@ -10,9 +10,9 @@ export default function Root() {
|
||||
|
||||
useEffect(() => {
|
||||
if (token) {
|
||||
const url = new URL(config.redirectURL);
|
||||
const url = new URL(config.redirectURL || '/app');
|
||||
if (url.origin !== window.location.origin) {
|
||||
window.location.href = config.redirectURL;
|
||||
window.location.href = config.redirectURL || '/app';
|
||||
}
|
||||
}
|
||||
return () => {};
|
||||
|
@@ -2,9 +2,9 @@ import React, { Fragment } from 'react';
|
||||
import { Authorizer } from '@authorizerdev/authorizer-react';
|
||||
|
||||
export default function Login() {
|
||||
return (
|
||||
<Fragment>
|
||||
<Authorizer />
|
||||
</Fragment>
|
||||
);
|
||||
return (
|
||||
<Fragment>
|
||||
<Authorizer />
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
VERSION="$1"
|
||||
make clean && CGO_ENABLED=1 VERSION=${VERSION} make
|
||||
FILE_NAME=authorizer-${VERSION}-darwin-amd64.tar.gz
|
||||
tar cvfz ${FILE_NAME} .env app build templates
|
||||
tar cvfz ${FILE_NAME} .env app/build build templates
|
||||
AUTH="Authorization: token $GITHUB_TOKEN"
|
||||
RELASE_INFO=$(curl -sH "$AUTH" https://api.github.com/repos/authorizerdev/authorizer/releases/tags/${VERSION})
|
||||
echo $RELASE_INFO
|
||||
|
@@ -13,7 +13,6 @@ var (
|
||||
JWT_TYPE = ""
|
||||
JWT_SECRET = ""
|
||||
ALLOWED_ORIGINS = []string{}
|
||||
ALLOWED_CALLBACK_URLS = []string{}
|
||||
AUTHORIZER_URL = ""
|
||||
PORT = "8080"
|
||||
REDIS_URL = ""
|
||||
@@ -24,9 +23,10 @@ var (
|
||||
DISABLE_BASIC_AUTHENTICATION = "false"
|
||||
|
||||
// ROLES
|
||||
ROLES = []string{}
|
||||
DEFAULT_ROLE = ""
|
||||
JWT_ROLE_CLAIM = "role"
|
||||
ROLES = []string{}
|
||||
PROTECTED_ROLES = []string{}
|
||||
DEFAULT_ROLES = []string{}
|
||||
JWT_ROLE_CLAIM = "role"
|
||||
|
||||
// OAuth login
|
||||
GOOGLE_CLIENT_ID = ""
|
||||
@@ -37,4 +37,8 @@ var (
|
||||
FACEBOOK_CLIENT_SECRET = ""
|
||||
TWITTER_CLIENT_ID = ""
|
||||
TWITTER_CLIENT_SECRET = ""
|
||||
|
||||
// Org envs
|
||||
ORGANIZATION_NAME = "Authorizer"
|
||||
ORGANIZATION_LOGO = "https://authorizer.dev/images/logo.png"
|
||||
)
|
||||
|
@@ -23,8 +23,7 @@ func (r *Role) BeforeCreate(tx *gorm.DB) (err error) {
|
||||
func (mgr *manager) SaveRoles(roles []Role) error {
|
||||
res := mgr.db.Clauses(
|
||||
clause.OnConflict{
|
||||
OnConstraint: "authorizer_roles_role_key",
|
||||
DoNothing: true,
|
||||
DoNothing: true,
|
||||
}).Create(&roles)
|
||||
if res.Error != nil {
|
||||
log.Println(`Error saving roles`)
|
||||
|
107
server/env.go
107
server/env.go
@@ -7,47 +7,38 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
// build variables
|
||||
var Version string
|
||||
|
||||
// ParseArgs -> to parse the cli flag and get db url. This is useful with heroku button
|
||||
func ParseArgs() {
|
||||
dbURL := flag.String("database_url", "", "Database connection string")
|
||||
dbType := flag.String("databse_type", "", "Database type, possible values are postgres,mysql,sqlite")
|
||||
authorizerURL := flag.String("authorizer_url", "", "URL for authorizer instance, eg: https://xyz.herokuapp.com")
|
||||
|
||||
flag.Parse()
|
||||
if *dbURL != "" {
|
||||
constants.DATABASE_URL = *dbURL
|
||||
}
|
||||
|
||||
if *dbType != "" {
|
||||
constants.DATABASE_TYPE = *dbType
|
||||
}
|
||||
|
||||
if *authorizerURL != "" {
|
||||
constants.AUTHORIZER_URL = *authorizerURL
|
||||
}
|
||||
}
|
||||
var (
|
||||
Version string
|
||||
ARG_DB_URL *string
|
||||
ARG_DB_TYPE *string
|
||||
ARG_AUTHORIZER_URL *string
|
||||
ARG_ENV_FILE *string
|
||||
)
|
||||
|
||||
// InitEnv -> to initialize env and through error if required env are not present
|
||||
func InitEnv() {
|
||||
envPath := `.env`
|
||||
envFile := flag.String("env_file", "", "Env file path")
|
||||
ARG_DB_URL = flag.String("database_url", "", "Database connection string")
|
||||
ARG_DB_TYPE = flag.String("database_type", "", "Database type, possible values are postgres,mysql,sqlite")
|
||||
ARG_AUTHORIZER_URL = flag.String("authorizer_url", "", "URL for authorizer instance, eg: https://xyz.herokuapp.com")
|
||||
ARG_ENV_FILE = flag.String("env_file", "", "Env file path")
|
||||
|
||||
flag.Parse()
|
||||
if *envFile != "" {
|
||||
envPath = *envFile
|
||||
if *ARG_ENV_FILE != "" {
|
||||
envPath = *ARG_ENV_FILE
|
||||
}
|
||||
|
||||
err := godotenv.Load(envPath)
|
||||
if err != nil {
|
||||
log.Println("Error loading .env file")
|
||||
}
|
||||
|
||||
constants.VERSION = Version
|
||||
|
||||
constants.ADMIN_SECRET = os.Getenv("ADMIN_SECRET")
|
||||
constants.ENV = os.Getenv("ENV")
|
||||
constants.DATABASE_TYPE = os.Getenv("DATABASE_TYPE")
|
||||
@@ -73,7 +64,6 @@ func InitEnv() {
|
||||
constants.RESET_PASSWORD_URL = strings.TrimPrefix(os.Getenv("RESET_PASSWORD_URL"), "/")
|
||||
constants.DISABLE_BASIC_AUTHENTICATION = os.Getenv("DISABLE_BASIC_AUTHENTICATION")
|
||||
constants.DISABLE_EMAIL_VERIFICATION = os.Getenv("DISABLE_EMAIL_VERIFICATION")
|
||||
constants.DEFAULT_ROLE = os.Getenv("DEFAULT_ROLE")
|
||||
constants.JWT_ROLE_CLAIM = os.Getenv("JWT_ROLE_CLAIM")
|
||||
|
||||
if constants.ADMIN_SECRET == "" {
|
||||
@@ -104,20 +94,18 @@ func InitEnv() {
|
||||
}
|
||||
constants.ALLOWED_ORIGINS = allowedOrigins
|
||||
|
||||
allowedCallbackSplit := strings.Split(os.Getenv("ALLOWED_CALLBACK_URLS"), ",")
|
||||
allowedCallbacks := []string{}
|
||||
for _, val := range allowedCallbackSplit {
|
||||
trimVal := strings.TrimSpace(val)
|
||||
if trimVal != "" {
|
||||
allowedCallbacks = append(allowedCallbacks, trimVal)
|
||||
}
|
||||
if *ARG_AUTHORIZER_URL != "" {
|
||||
constants.AUTHORIZER_URL = *ARG_AUTHORIZER_URL
|
||||
}
|
||||
|
||||
if *ARG_DB_URL != "" {
|
||||
constants.DATABASE_URL = *ARG_DB_URL
|
||||
}
|
||||
|
||||
if *ARG_DB_TYPE != "" {
|
||||
constants.DATABASE_TYPE = *ARG_DB_TYPE
|
||||
}
|
||||
if len(allowedCallbackSplit) == 0 {
|
||||
allowedCallbackSplit = []string{"*"}
|
||||
}
|
||||
constants.ALLOWED_CALLBACK_URLS = allowedCallbackSplit
|
||||
|
||||
ParseArgs()
|
||||
if constants.DATABASE_URL == "" {
|
||||
panic("Database url is required")
|
||||
}
|
||||
@@ -148,7 +136,26 @@ func InitEnv() {
|
||||
|
||||
rolesSplit := strings.Split(os.Getenv("ROLES"), ",")
|
||||
roles := []string{}
|
||||
defaultRole := ""
|
||||
if len(rolesSplit) == 0 {
|
||||
roles = []string{"user"}
|
||||
}
|
||||
|
||||
defaultRoleSplit := strings.Split(os.Getenv("DEFAULT_ROLES"), ",")
|
||||
defaultRoles := []string{}
|
||||
|
||||
if len(defaultRoleSplit) == 0 {
|
||||
defaultRoles = []string{"user"}
|
||||
}
|
||||
|
||||
protectedRolesSplit := strings.Split(os.Getenv("PROTECTED_ROLES"), ",")
|
||||
protectedRoles := []string{}
|
||||
|
||||
if len(protectedRolesSplit) > 0 {
|
||||
for _, val := range protectedRolesSplit {
|
||||
trimVal := strings.TrimSpace(val)
|
||||
protectedRoles = append(protectedRoles, trimVal)
|
||||
}
|
||||
}
|
||||
|
||||
for _, val := range rolesSplit {
|
||||
trimVal := strings.TrimSpace(val)
|
||||
@@ -156,22 +163,28 @@ func InitEnv() {
|
||||
roles = append(roles, trimVal)
|
||||
}
|
||||
|
||||
if trimVal == constants.DEFAULT_ROLE {
|
||||
defaultRole = trimVal
|
||||
if utils.StringContains(defaultRoleSplit, trimVal) {
|
||||
defaultRoles = append(defaultRoles, trimVal)
|
||||
}
|
||||
}
|
||||
if len(roles) > 0 && defaultRole == "" {
|
||||
|
||||
if len(roles) > 0 && len(defaultRoles) == 0 && len(defaultRoleSplit) > 0 {
|
||||
panic(`Invalid DEFAULT_ROLE environment variable. It can be one from give ROLES environment variable value`)
|
||||
}
|
||||
|
||||
if len(roles) == 0 {
|
||||
roles = []string{"user", "admin"}
|
||||
constants.DEFAULT_ROLE = "user"
|
||||
}
|
||||
|
||||
constants.ROLES = roles
|
||||
constants.DEFAULT_ROLES = defaultRoles
|
||||
constants.PROTECTED_ROLES = protectedRoles
|
||||
|
||||
if constants.JWT_ROLE_CLAIM == "" {
|
||||
constants.JWT_ROLE_CLAIM = "role"
|
||||
}
|
||||
|
||||
if os.Getenv("ORGANIZATION_NAME") != "" {
|
||||
constants.ORGANIZATION_NAME = os.Getenv("ORGANIZATION_NAME")
|
||||
}
|
||||
|
||||
if os.Getenv("ORGANIZATION_LOGO") != "" {
|
||||
constants.ORGANIZATION_LOGO = os.Getenv("ORGANIZATION_LOGO")
|
||||
}
|
||||
}
|
||||
|
@@ -81,7 +81,7 @@ type ComplexityRoot struct {
|
||||
Query struct {
|
||||
Meta func(childComplexity int) int
|
||||
Profile func(childComplexity int) int
|
||||
Token func(childComplexity int, role *string) int
|
||||
Token func(childComplexity int, roles []string) int
|
||||
Users func(childComplexity int) int
|
||||
VerificationRequests func(childComplexity int) int
|
||||
}
|
||||
@@ -129,7 +129,7 @@ type MutationResolver interface {
|
||||
type QueryResolver interface {
|
||||
Meta(ctx context.Context) (*model.Meta, error)
|
||||
Users(ctx context.Context) ([]*model.User, error)
|
||||
Token(ctx context.Context, role *string) (*model.AuthResponse, error)
|
||||
Token(ctx context.Context, roles []string) (*model.AuthResponse, error)
|
||||
Profile(ctx context.Context) (*model.User, error)
|
||||
VerificationRequests(ctx context.Context) ([]*model.VerificationRequest, error)
|
||||
}
|
||||
@@ -379,7 +379,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return e.complexity.Query.Token(childComplexity, args["role"].(*string)), true
|
||||
return e.complexity.Query.Token(childComplexity, args["roles"].([]string)), true
|
||||
|
||||
case "Query.users":
|
||||
if e.complexity.Query.Users == nil {
|
||||
@@ -648,13 +648,13 @@ input SignUpInput {
|
||||
password: String!
|
||||
confirmPassword: String!
|
||||
image: String
|
||||
roles: [String]
|
||||
roles: [String!]
|
||||
}
|
||||
|
||||
input LoginInput {
|
||||
email: String!
|
||||
password: String!
|
||||
role: String
|
||||
roles: [String!]
|
||||
}
|
||||
|
||||
input VerifyEmailInput {
|
||||
@@ -677,12 +677,12 @@ input UpdateProfileInput {
|
||||
}
|
||||
|
||||
input AdminUpdateUserInput {
|
||||
id: ID!
|
||||
email: String
|
||||
firstName: String
|
||||
lastName: String
|
||||
image: String
|
||||
roles: [String]
|
||||
id: ID!
|
||||
email: String
|
||||
firstName: String
|
||||
lastName: String
|
||||
image: String
|
||||
roles: [String]
|
||||
}
|
||||
|
||||
input ForgotPasswordInput {
|
||||
@@ -704,7 +704,7 @@ type Mutation {
|
||||
login(params: LoginInput!): AuthResponse!
|
||||
logout: Response!
|
||||
updateProfile(params: UpdateProfileInput!): Response!
|
||||
adminUpdateUser(params: AdminUpdateUserInput!): User!
|
||||
adminUpdateUser(params: AdminUpdateUserInput!): User!
|
||||
verifyEmail(params: VerifyEmailInput!): AuthResponse!
|
||||
resendVerifyEmail(params: ResendVerifyEmailInput!): Response!
|
||||
forgotPassword(params: ForgotPasswordInput!): Response!
|
||||
@@ -715,7 +715,7 @@ type Mutation {
|
||||
type Query {
|
||||
meta: Meta!
|
||||
users: [User!]!
|
||||
token(role: String): AuthResponse
|
||||
token(roles: [String!]): AuthResponse
|
||||
profile: User!
|
||||
verificationRequests: [VerificationRequest!]!
|
||||
}
|
||||
@@ -880,15 +880,15 @@ func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs
|
||||
func (ec *executionContext) field_Query_token_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||
var err error
|
||||
args := map[string]interface{}{}
|
||||
var arg0 *string
|
||||
if tmp, ok := rawArgs["role"]; ok {
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("role"))
|
||||
arg0, err = ec.unmarshalOString2ᚖstring(ctx, tmp)
|
||||
var arg0 []string
|
||||
if tmp, ok := rawArgs["roles"]; ok {
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("roles"))
|
||||
arg0, err = ec.unmarshalOString2ᚕstringᚄ(ctx, tmp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
args["role"] = arg0
|
||||
args["roles"] = arg0
|
||||
return args, nil
|
||||
}
|
||||
|
||||
@@ -1884,7 +1884,7 @@ func (ec *executionContext) _Query_token(ctx context.Context, field graphql.Coll
|
||||
fc.Args = args
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return ec.resolvers.Query().Token(rctx, args["role"].(*string))
|
||||
return ec.resolvers.Query().Token(rctx, args["roles"].([]string))
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
@@ -3842,11 +3842,11 @@ func (ec *executionContext) unmarshalInputLoginInput(ctx context.Context, obj in
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
case "role":
|
||||
case "roles":
|
||||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("role"))
|
||||
it.Role, err = ec.unmarshalOString2ᚖstring(ctx, v)
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("roles"))
|
||||
it.Roles, err = ec.unmarshalOString2ᚕstringᚄ(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
@@ -3970,7 +3970,7 @@ func (ec *executionContext) unmarshalInputSignUpInput(ctx context.Context, obj i
|
||||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("roles"))
|
||||
it.Roles, err = ec.unmarshalOString2ᚕᚖstring(ctx, v)
|
||||
it.Roles, err = ec.unmarshalOString2ᚕstringᚄ(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
@@ -5280,6 +5280,42 @@ func (ec *executionContext) marshalOString2string(ctx context.Context, sel ast.S
|
||||
return graphql.MarshalString(v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalOString2ᚕstringᚄ(ctx context.Context, v interface{}) ([]string, error) {
|
||||
if v == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var vSlice []interface{}
|
||||
if v != nil {
|
||||
if tmp1, ok := v.([]interface{}); ok {
|
||||
vSlice = tmp1
|
||||
} else {
|
||||
vSlice = []interface{}{v}
|
||||
}
|
||||
}
|
||||
var err error
|
||||
res := make([]string, len(vSlice))
|
||||
for i := range vSlice {
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i))
|
||||
res[i], err = ec.unmarshalNString2string(ctx, vSlice[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalOString2ᚕstringᚄ(ctx context.Context, sel ast.SelectionSet, v []string) graphql.Marshaler {
|
||||
if v == nil {
|
||||
return graphql.Null
|
||||
}
|
||||
ret := make(graphql.Array, len(v))
|
||||
for i := range v {
|
||||
ret[i] = ec.marshalNString2string(ctx, sel, v[i])
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalOString2ᚕᚖstring(ctx context.Context, v interface{}) ([]*string, error) {
|
||||
if v == nil {
|
||||
return nil, nil
|
||||
|
@@ -32,9 +32,9 @@ type ForgotPasswordInput struct {
|
||||
}
|
||||
|
||||
type LoginInput struct {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
Role *string `json:"role"`
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
Roles []string `json:"roles"`
|
||||
}
|
||||
|
||||
type Meta struct {
|
||||
@@ -62,13 +62,13 @@ type Response struct {
|
||||
}
|
||||
|
||||
type SignUpInput struct {
|
||||
FirstName *string `json:"firstName"`
|
||||
LastName *string `json:"lastName"`
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
ConfirmPassword string `json:"confirmPassword"`
|
||||
Image *string `json:"image"`
|
||||
Roles []*string `json:"roles"`
|
||||
FirstName *string `json:"firstName"`
|
||||
LastName *string `json:"lastName"`
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
ConfirmPassword string `json:"confirmPassword"`
|
||||
Image *string `json:"image"`
|
||||
Roles []string `json:"roles"`
|
||||
}
|
||||
|
||||
type UpdateProfileInput struct {
|
||||
|
@@ -61,13 +61,13 @@ input SignUpInput {
|
||||
password: String!
|
||||
confirmPassword: String!
|
||||
image: String
|
||||
roles: [String]
|
||||
roles: [String!]
|
||||
}
|
||||
|
||||
input LoginInput {
|
||||
email: String!
|
||||
password: String!
|
||||
role: String
|
||||
roles: [String!]
|
||||
}
|
||||
|
||||
input VerifyEmailInput {
|
||||
@@ -90,12 +90,12 @@ input UpdateProfileInput {
|
||||
}
|
||||
|
||||
input AdminUpdateUserInput {
|
||||
id: ID!
|
||||
email: String
|
||||
firstName: String
|
||||
lastName: String
|
||||
image: String
|
||||
roles: [String]
|
||||
id: ID!
|
||||
email: String
|
||||
firstName: String
|
||||
lastName: String
|
||||
image: String
|
||||
roles: [String]
|
||||
}
|
||||
|
||||
input ForgotPasswordInput {
|
||||
@@ -117,7 +117,7 @@ type Mutation {
|
||||
login(params: LoginInput!): AuthResponse!
|
||||
logout: Response!
|
||||
updateProfile(params: UpdateProfileInput!): Response!
|
||||
adminUpdateUser(params: AdminUpdateUserInput!): User!
|
||||
adminUpdateUser(params: AdminUpdateUserInput!): User!
|
||||
verifyEmail(params: VerifyEmailInput!): AuthResponse!
|
||||
resendVerifyEmail(params: ResendVerifyEmailInput!): Response!
|
||||
forgotPassword(params: ForgotPasswordInput!): Response!
|
||||
@@ -128,7 +128,7 @@ type Mutation {
|
||||
type Query {
|
||||
meta: Meta!
|
||||
users: [User!]!
|
||||
token(role: String): AuthResponse
|
||||
token(roles: [String!]): AuthResponse
|
||||
profile: User!
|
||||
verificationRequests: [VerificationRequest!]!
|
||||
}
|
||||
|
@@ -59,8 +59,8 @@ func (r *queryResolver) Users(ctx context.Context) ([]*model.User, error) {
|
||||
return resolvers.Users(ctx)
|
||||
}
|
||||
|
||||
func (r *queryResolver) Token(ctx context.Context, role *string) (*model.AuthResponse, error) {
|
||||
return resolvers.Token(ctx, role)
|
||||
func (r *queryResolver) Token(ctx context.Context, roles []string) (*model.AuthResponse, error) {
|
||||
return resolvers.Token(ctx, roles)
|
||||
}
|
||||
|
||||
func (r *queryResolver) Profile(ctx context.Context) (*model.User, error) {
|
||||
|
@@ -75,8 +75,10 @@ func AppHandler() gin.HandlerFunc {
|
||||
}
|
||||
c.HTML(http.StatusOK, "app.tmpl", gin.H{
|
||||
"data": map[string]string{
|
||||
"authorizerURL": stateObj.AuthorizerURL,
|
||||
"redirectURL": stateObj.RedirectURL,
|
||||
"authorizerURL": stateObj.AuthorizerURL,
|
||||
"redirectURL": stateObj.RedirectURL,
|
||||
"organizationName": constants.ORGANIZATION_NAME,
|
||||
"organizationLogo": constants.ORGANIZATION_LOGO,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@@ -19,28 +19,29 @@ import (
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
func processGoogleUserInfo(code string, role string, c *gin.Context) error {
|
||||
func processGoogleUserInfo(code string, roles []string, c *gin.Context) (db.User, error) {
|
||||
user := db.User{}
|
||||
token, err := oauth.OAuthProvider.GoogleConfig.Exchange(oauth2.NoContext, code)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid google exchange code: %s", err.Error())
|
||||
return user, fmt.Errorf("invalid google exchange code: %s", err.Error())
|
||||
}
|
||||
client := oauth.OAuthProvider.GoogleConfig.Client(oauth2.NoContext, token)
|
||||
response, err := client.Get(constants.GoogleUserInfoURL)
|
||||
if err != nil {
|
||||
return err
|
||||
return user, err
|
||||
}
|
||||
|
||||
defer response.Body.Close()
|
||||
body, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read google response body: %s", err.Error())
|
||||
return user, fmt.Errorf("failed to read google response body: %s", err.Error())
|
||||
}
|
||||
|
||||
userRawData := make(map[string]string)
|
||||
json.Unmarshal(body, &userRawData)
|
||||
|
||||
existingUser, err := db.Mgr.GetUserByEmail(userRawData["email"])
|
||||
user := db.User{
|
||||
user = db.User{
|
||||
FirstName: userRawData["given_name"],
|
||||
LastName: userRawData["family_name"],
|
||||
Image: userRawData["picture"],
|
||||
@@ -50,7 +51,7 @@ func processGoogleUserInfo(code string, role string, c *gin.Context) error {
|
||||
if err != nil {
|
||||
// user not registered, register user and generate session token
|
||||
user.SignupMethod = enum.Google.String()
|
||||
user.Roles = role
|
||||
user.Roles = strings.Join(roles, ",")
|
||||
} else {
|
||||
// user exists in db, check if method was google
|
||||
// if not append google to existing signup method and save it
|
||||
@@ -61,34 +62,25 @@ func processGoogleUserInfo(code string, role string, c *gin.Context) error {
|
||||
}
|
||||
user.SignupMethod = signupMethod
|
||||
user.Password = existingUser.Password
|
||||
if !utils.IsValidRole(strings.Split(existingUser.Roles, ","), role) {
|
||||
return fmt.Errorf("invalid role")
|
||||
if !utils.IsValidRoles(strings.Split(existingUser.Roles, ","), roles) {
|
||||
return user, fmt.Errorf("invalid role")
|
||||
}
|
||||
|
||||
user.Roles = existingUser.Roles
|
||||
}
|
||||
|
||||
user, _ = db.Mgr.SaveUser(user)
|
||||
user, _ = db.Mgr.GetUserByEmail(user.Email)
|
||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||
|
||||
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, role)
|
||||
|
||||
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, role)
|
||||
utils.SetCookie(c, accessToken)
|
||||
session.SetToken(userIdStr, refreshToken)
|
||||
return nil
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func processGithubUserInfo(code string, role string, c *gin.Context) error {
|
||||
func processGithubUserInfo(code string, roles []string, c *gin.Context) (db.User, error) {
|
||||
user := db.User{}
|
||||
token, err := oauth.OAuthProvider.GithubConfig.Exchange(oauth2.NoContext, code)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid github exchange code: %s", err.Error())
|
||||
return user, fmt.Errorf("invalid github exchange code: %s", err.Error())
|
||||
}
|
||||
client := http.Client{}
|
||||
req, err := http.NewRequest("GET", constants.GithubUserInfoURL, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating github user info request: %s", err.Error())
|
||||
return user, fmt.Errorf("error creating github user info request: %s", err.Error())
|
||||
}
|
||||
req.Header = http.Header{
|
||||
"Authorization": []string{fmt.Sprintf("token %s", token.AccessToken)},
|
||||
@@ -96,13 +88,13 @@ func processGithubUserInfo(code string, role string, c *gin.Context) error {
|
||||
|
||||
response, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
return user, err
|
||||
}
|
||||
|
||||
defer response.Body.Close()
|
||||
body, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read github response body: %s", err.Error())
|
||||
return user, fmt.Errorf("failed to read github response body: %s", err.Error())
|
||||
}
|
||||
|
||||
userRawData := make(map[string]string)
|
||||
@@ -118,7 +110,7 @@ func processGithubUserInfo(code string, role string, c *gin.Context) error {
|
||||
if len(name) > 1 && strings.TrimSpace(name[1]) != "" {
|
||||
lastName = name[0]
|
||||
}
|
||||
user := db.User{
|
||||
user = db.User{
|
||||
FirstName: firstName,
|
||||
LastName: lastName,
|
||||
Image: userRawData["avatar_url"],
|
||||
@@ -128,7 +120,7 @@ func processGithubUserInfo(code string, role string, c *gin.Context) error {
|
||||
if err != nil {
|
||||
// user not registered, register user and generate session token
|
||||
user.SignupMethod = enum.Github.String()
|
||||
user.Roles = role
|
||||
user.Roles = strings.Join(roles, ",")
|
||||
} else {
|
||||
// user exists in db, check if method was google
|
||||
// if not append google to existing signup method and save it
|
||||
@@ -140,45 +132,38 @@ func processGithubUserInfo(code string, role string, c *gin.Context) error {
|
||||
user.SignupMethod = signupMethod
|
||||
user.Password = existingUser.Password
|
||||
|
||||
if !utils.IsValidRole(strings.Split(existingUser.Roles, ","), role) {
|
||||
return fmt.Errorf("invalid role")
|
||||
if !utils.IsValidRoles(strings.Split(existingUser.Roles, ","), roles) {
|
||||
return user, fmt.Errorf("invalid role")
|
||||
}
|
||||
|
||||
user.Roles = existingUser.Roles
|
||||
}
|
||||
|
||||
user, _ = db.Mgr.SaveUser(user)
|
||||
user, _ = db.Mgr.GetUserByEmail(user.Email)
|
||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, role)
|
||||
|
||||
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, role)
|
||||
utils.SetCookie(c, accessToken)
|
||||
session.SetToken(userIdStr, refreshToken)
|
||||
return nil
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func processFacebookUserInfo(code string, role string, c *gin.Context) error {
|
||||
func processFacebookUserInfo(code string, roles []string, c *gin.Context) (db.User, error) {
|
||||
user := db.User{}
|
||||
token, err := oauth.OAuthProvider.FacebookConfig.Exchange(oauth2.NoContext, code)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid facebook exchange code: %s", err.Error())
|
||||
return user, fmt.Errorf("invalid facebook exchange code: %s", err.Error())
|
||||
}
|
||||
client := http.Client{}
|
||||
req, err := http.NewRequest("GET", constants.FacebookUserInfoURL+token.AccessToken, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating facebook user info request: %s", err.Error())
|
||||
return user, fmt.Errorf("error creating facebook user info request: %s", err.Error())
|
||||
}
|
||||
|
||||
response, err := client.Do(req)
|
||||
if err != nil {
|
||||
log.Println("err:", err)
|
||||
return err
|
||||
return user, err
|
||||
}
|
||||
|
||||
defer response.Body.Close()
|
||||
body, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read facebook response body: %s", err.Error())
|
||||
return user, fmt.Errorf("failed to read facebook response body: %s", err.Error())
|
||||
}
|
||||
|
||||
userRawData := make(map[string]interface{})
|
||||
@@ -189,7 +174,7 @@ func processFacebookUserInfo(code string, role string, c *gin.Context) error {
|
||||
|
||||
picObject := userRawData["picture"].(map[string]interface{})["data"]
|
||||
picDataObject := picObject.(map[string]interface{})
|
||||
user := db.User{
|
||||
user = db.User{
|
||||
FirstName: fmt.Sprintf("%v", userRawData["first_name"]),
|
||||
LastName: fmt.Sprintf("%v", userRawData["last_name"]),
|
||||
Image: fmt.Sprintf("%v", picDataObject["url"]),
|
||||
@@ -200,7 +185,7 @@ func processFacebookUserInfo(code string, role string, c *gin.Context) error {
|
||||
if err != nil {
|
||||
// user not registered, register user and generate session token
|
||||
user.SignupMethod = enum.Github.String()
|
||||
user.Roles = role
|
||||
user.Roles = strings.Join(roles, ",")
|
||||
} else {
|
||||
// user exists in db, check if method was google
|
||||
// if not append google to existing signup method and save it
|
||||
@@ -212,22 +197,14 @@ func processFacebookUserInfo(code string, role string, c *gin.Context) error {
|
||||
user.SignupMethod = signupMethod
|
||||
user.Password = existingUser.Password
|
||||
|
||||
if !utils.IsValidRole(strings.Split(existingUser.Roles, ","), role) {
|
||||
return fmt.Errorf("invalid role")
|
||||
if !utils.IsValidRoles(strings.Split(existingUser.Roles, ","), roles) {
|
||||
return user, fmt.Errorf("invalid role")
|
||||
}
|
||||
|
||||
user.Roles = existingUser.Roles
|
||||
}
|
||||
|
||||
user, _ = db.Mgr.SaveUser(user)
|
||||
user, _ = db.Mgr.GetUserByEmail(user.Email)
|
||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, role)
|
||||
|
||||
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, role)
|
||||
utils.SetCookie(c, accessToken)
|
||||
session.SetToken(userIdStr, refreshToken)
|
||||
return nil
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func OAuthCallbackHandler() gin.HandlerFunc {
|
||||
@@ -249,18 +226,19 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
role := sessionSplit[2]
|
||||
roles := strings.Split(sessionSplit[2], ",")
|
||||
redirectURL := sessionSplit[1]
|
||||
|
||||
var err error
|
||||
user := db.User{}
|
||||
code := c.Request.FormValue("code")
|
||||
switch provider {
|
||||
case enum.Google.String():
|
||||
err = processGoogleUserInfo(code, role, c)
|
||||
user, err = processGoogleUserInfo(code, roles, c)
|
||||
case enum.Github.String():
|
||||
err = processGithubUserInfo(code, role, c)
|
||||
user, err = processGithubUserInfo(code, roles, c)
|
||||
case enum.Facebook.String():
|
||||
err = processFacebookUserInfo(code, role, c)
|
||||
user, err = processFacebookUserInfo(code, roles, c)
|
||||
default:
|
||||
err = fmt.Errorf(`invalid oauth provider`)
|
||||
}
|
||||
@@ -269,6 +247,16 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
||||
c.JSON(400, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
user, _ = db.Mgr.SaveUser(user)
|
||||
user, _ = db.Mgr.GetUserByEmail(user.Email)
|
||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, roles)
|
||||
|
||||
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, roles)
|
||||
utils.SetCookie(c, accessToken)
|
||||
session.SetToken(userIdStr, refreshToken)
|
||||
|
||||
c.Redirect(http.StatusTemporaryRedirect, redirectURL)
|
||||
}
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/enum"
|
||||
@@ -18,7 +19,7 @@ func OAuthLoginHandler() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// TODO validate redirect URL
|
||||
redirectURL := c.Query("redirectURL")
|
||||
role := c.Query("role")
|
||||
roles := c.Query("roles")
|
||||
|
||||
if redirectURL == "" {
|
||||
c.JSON(400, gin.H{
|
||||
@@ -27,20 +28,24 @@ func OAuthLoginHandler() gin.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
if role != "" {
|
||||
if roles != "" {
|
||||
// validate role
|
||||
if !utils.IsValidRole(constants.ROLES, role) {
|
||||
rolesSplit := strings.Split(roles, ",")
|
||||
|
||||
// use protected roles verification for admin login only.
|
||||
// though if not associated with user, it will be rejected from oauth_callback
|
||||
if !utils.IsValidRoles(append([]string{}, append(constants.ROLES, constants.PROTECTED_ROLES...)...), rolesSplit) {
|
||||
c.JSON(400, gin.H{
|
||||
"error": "invalid role",
|
||||
})
|
||||
return
|
||||
}
|
||||
} else {
|
||||
role = constants.DEFAULT_ROLE
|
||||
roles = strings.Join(constants.DEFAULT_ROLES, ",")
|
||||
}
|
||||
|
||||
uuid := uuid.New()
|
||||
oauthStateString := uuid.String() + "___" + redirectURL + "___" + role
|
||||
oauthStateString := uuid.String() + "___" + redirectURL + "___" + roles
|
||||
|
||||
provider := c.Param("oauth_provider")
|
||||
|
||||
|
@@ -3,6 +3,7 @@ package handlers
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
@@ -50,9 +51,10 @@ func VerifyEmailHandler() gin.HandlerFunc {
|
||||
db.Mgr.DeleteToken(claim.Email)
|
||||
|
||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, user.Roles)
|
||||
roles := strings.Split(user.Roles, ",")
|
||||
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, roles)
|
||||
|
||||
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, user.Roles)
|
||||
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, roles)
|
||||
|
||||
session.SetToken(userIdStr, refreshToken)
|
||||
utils.SetCookie(c, accessToken)
|
||||
|
@@ -7,6 +7,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/enum"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
@@ -30,7 +31,6 @@ func AdminUpdateUser(ctx context.Context, params model.AdminUpdateUserInput) (*m
|
||||
}
|
||||
|
||||
user, err := db.Mgr.GetUserByID(params.ID)
|
||||
|
||||
if err != nil {
|
||||
return res, fmt.Errorf(`User not found`)
|
||||
}
|
||||
@@ -92,7 +92,7 @@ func AdminUpdateUser(ctx context.Context, params model.AdminUpdateUserInput) (*m
|
||||
inputRoles = append(inputRoles, *item)
|
||||
}
|
||||
|
||||
if !utils.IsValidRolesArray(inputRoles) {
|
||||
if !utils.IsValidRoles(append([]string{}, append(constants.ROLES, constants.PROTECTED_ROLES...)...), inputRoles) {
|
||||
return res, fmt.Errorf("invalid list of roles")
|
||||
}
|
||||
|
||||
@@ -114,9 +114,8 @@ func AdminUpdateUser(ctx context.Context, params model.AdminUpdateUserInput) (*m
|
||||
return res, err
|
||||
}
|
||||
|
||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||
res = &model.User{
|
||||
ID: userIdStr,
|
||||
ID: params.ID,
|
||||
Email: user.Email,
|
||||
Image: &user.Image,
|
||||
FirstName: &user.FirstName,
|
||||
|
@@ -46,19 +46,19 @@ func Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, e
|
||||
log.Println("Compare password error:", err)
|
||||
return res, fmt.Errorf(`invalid password`)
|
||||
}
|
||||
role := constants.DEFAULT_ROLE
|
||||
if params.Role != nil {
|
||||
// validate role
|
||||
if !utils.IsValidRole(strings.Split(user.Roles, ","), *params.Role) {
|
||||
return res, fmt.Errorf(`invalid role`)
|
||||
roles := constants.DEFAULT_ROLES
|
||||
currentRoles := strings.Split(user.Roles, ",")
|
||||
if len(params.Roles) > 0 {
|
||||
if !utils.IsValidRoles(currentRoles, params.Roles) {
|
||||
return res, fmt.Errorf(`invalid roles`)
|
||||
}
|
||||
|
||||
role = *params.Role
|
||||
roles = params.Roles
|
||||
}
|
||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, role)
|
||||
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, roles)
|
||||
|
||||
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, role)
|
||||
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, roles)
|
||||
|
||||
session.SetToken(userIdStr, refreshToken)
|
||||
|
||||
|
@@ -37,16 +37,15 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
|
||||
|
||||
inputRoles := []string{}
|
||||
|
||||
if params.Roles != nil && len(params.Roles) > 0 {
|
||||
if len(params.Roles) > 0 {
|
||||
// check if roles exists
|
||||
for _, item := range params.Roles {
|
||||
inputRoles = append(inputRoles, *item)
|
||||
}
|
||||
if !utils.IsValidRolesArray(inputRoles) {
|
||||
if !utils.IsValidRoles(constants.ROLES, params.Roles) {
|
||||
return res, fmt.Errorf(`invalid roles`)
|
||||
} else {
|
||||
inputRoles = params.Roles
|
||||
}
|
||||
} else {
|
||||
inputRoles = []string{constants.DEFAULT_ROLE}
|
||||
inputRoles = constants.DEFAULT_ROLES
|
||||
}
|
||||
|
||||
// find user with email
|
||||
@@ -85,6 +84,7 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
|
||||
return res, err
|
||||
}
|
||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||
roles := strings.Split(user.Roles, ",")
|
||||
userToReturn := &model.User{
|
||||
ID: userIdStr,
|
||||
Email: user.Email,
|
||||
@@ -123,9 +123,9 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
|
||||
}
|
||||
} else {
|
||||
|
||||
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, constants.DEFAULT_ROLE)
|
||||
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, roles)
|
||||
|
||||
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, constants.DEFAULT_ROLE)
|
||||
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, roles)
|
||||
|
||||
session.SetToken(userIdStr, refreshToken)
|
||||
res = &model.AuthResponse{
|
||||
|
@@ -14,7 +14,7 @@ import (
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
)
|
||||
|
||||
func Token(ctx context.Context, role *string) (*model.AuthResponse, error) {
|
||||
func Token(ctx context.Context, roles []string) (*model.AuthResponse, error) {
|
||||
var res *model.AuthResponse
|
||||
|
||||
gc, err := utils.GinContextFromContext(ctx)
|
||||
@@ -30,16 +30,11 @@ func Token(ctx context.Context, role *string) (*model.AuthResponse, error) {
|
||||
expiresAt := claim["exp"].(int64)
|
||||
email := fmt.Sprintf("%v", claim["email"])
|
||||
|
||||
claimRole := fmt.Sprintf("%v", claim[constants.JWT_ROLE_CLAIM])
|
||||
user, err := db.Mgr.GetUserByEmail(email)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
if role != nil && role != &claimRole {
|
||||
return res, fmt.Errorf(`unauthorized. invalid role for a given token`)
|
||||
}
|
||||
|
||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||
|
||||
sessionToken := session.GetToken(userIdStr)
|
||||
@@ -47,15 +42,30 @@ func Token(ctx context.Context, role *string) (*model.AuthResponse, error) {
|
||||
if sessionToken == "" {
|
||||
return res, fmt.Errorf(`unauthorized`)
|
||||
}
|
||||
// TODO check if refresh/session token has expired
|
||||
|
||||
expiresTimeObj := time.Unix(expiresAt, 0)
|
||||
currentTimeObj := time.Now()
|
||||
|
||||
claimRoleInterface := claim[constants.JWT_ROLE_CLAIM].([]interface{})
|
||||
claimRoles := make([]string, len(claimRoleInterface))
|
||||
for i, v := range claimRoleInterface {
|
||||
claimRoles[i] = v.(string)
|
||||
}
|
||||
|
||||
if len(roles) > 0 {
|
||||
for _, v := range roles {
|
||||
if !utils.StringContains(claimRoles, v) {
|
||||
return res, fmt.Errorf(`unauthorized`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if accessTokenErr != nil || expiresTimeObj.Sub(currentTimeObj).Minutes() <= 5 {
|
||||
// if access token has expired and refresh/session token is valid
|
||||
// generate new accessToken
|
||||
token, expiresAt, _ = utils.CreateAuthToken(user, enum.AccessToken, claimRole)
|
||||
token, expiresAt, _ = utils.CreateAuthToken(user, enum.AccessToken, claimRoles)
|
||||
}
|
||||
|
||||
utils.SetCookie(gc, token)
|
||||
res = &model.AuthResponse{
|
||||
Message: `Token verified`,
|
||||
|
@@ -124,31 +124,6 @@ func UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model
|
||||
}()
|
||||
}
|
||||
|
||||
// TODO this idea needs to be verified otherwise every user can make themselves super admin
|
||||
// rolesToSave := ""
|
||||
// if params.Roles != nil && len(params.Roles) > 0 {
|
||||
// currentRoles := strings.Split(user.Roles, ",")
|
||||
// inputRoles := []string{}
|
||||
// for _, item := range params.Roles {
|
||||
// inputRoles = append(inputRoles, *item)
|
||||
// }
|
||||
|
||||
// if !utils.IsValidRolesArray(inputRoles) {
|
||||
// return res, fmt.Errorf("invalid list of roles")
|
||||
// }
|
||||
|
||||
// if !utils.IsStringArrayEqual(inputRoles, currentRoles) {
|
||||
// rolesToSave = strings.Join(inputRoles, ",")
|
||||
// }
|
||||
|
||||
// session.DeleteToken(fmt.Sprintf("%v", user.ID))
|
||||
// utils.DeleteCookie(gc)
|
||||
// }
|
||||
|
||||
// if rolesToSave != "" {
|
||||
// user.Roles = rolesToSave
|
||||
// }
|
||||
|
||||
_, err = db.Mgr.UpdateUser(user)
|
||||
if err != nil {
|
||||
log.Println("Error updating user:", err)
|
||||
|
@@ -6,7 +6,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/enum"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
@@ -43,9 +42,10 @@ func VerifyEmail(ctx context.Context, params model.VerifyEmailInput) (*model.Aut
|
||||
db.Mgr.DeleteToken(claim.Email)
|
||||
|
||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, constants.DEFAULT_ROLE)
|
||||
roles := strings.Split(user.Roles, ",")
|
||||
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, roles)
|
||||
|
||||
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, constants.DEFAULT_ROLE)
|
||||
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, roles)
|
||||
|
||||
session.SetToken(userIdStr, refreshToken)
|
||||
|
||||
|
@@ -26,7 +26,7 @@ type UserAuthClaim struct {
|
||||
*JWTCustomClaim `json:"authorizer"`
|
||||
}
|
||||
|
||||
func CreateAuthToken(user db.User, tokenType enum.TokenType, role string) (string, int64, error) {
|
||||
func CreateAuthToken(user db.User, tokenType enum.TokenType, roles []string) (string, int64, error) {
|
||||
t := jwt.New(jwt.GetSigningMethod(constants.JWT_TYPE))
|
||||
expiryBound := time.Hour
|
||||
if tokenType == enum.RefreshToken {
|
||||
@@ -41,7 +41,7 @@ func CreateAuthToken(user db.User, tokenType enum.TokenType, role string) (strin
|
||||
"email": user.Email,
|
||||
"id": user.ID,
|
||||
"allowed_roles": strings.Split(user.Roles, ","),
|
||||
constants.JWT_ROLE_CLAIM: role,
|
||||
constants.JWT_ROLE_CLAIM: roles,
|
||||
}
|
||||
|
||||
t.Claims = &UserAuthClaim{
|
||||
|
@@ -18,3 +18,12 @@ func WriteToFile(filename string, data string) error {
|
||||
}
|
||||
return file.Sync()
|
||||
}
|
||||
|
||||
func StringContains(s []string, e string) bool {
|
||||
for _, a := range s {
|
||||
if a == e {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@@ -16,17 +16,91 @@ func SendVerificationMail(toEmail, token string) error {
|
||||
|
||||
Subject := "Please verify your email"
|
||||
message := fmt.Sprintf(`
|
||||
<!DOCTYPE HTML PULBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html"; charset=ISO-8859-1">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Please verify your email by clicking on the link below </h1><br/>
|
||||
<a href="%s">Click here to verify</a>
|
||||
</body>
|
||||
</html>
|
||||
`, constants.AUTHORIZER_URL+"/verify_email"+"?token="+token)
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta content="width=device-width, initial-scale=1" name="viewport">
|
||||
<meta name="x-apple-disable-message-reformatting">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta content="telephone=no" name="format-detection">
|
||||
<title></title>
|
||||
<!--[if (mso 16)]>
|
||||
<style type="text/css">
|
||||
a {}
|
||||
</style>
|
||||
<![endif]-->
|
||||
<!--[if gte mso 9]><style>sup { font-size: 100%% !important; }</style><![endif]-->
|
||||
<!--[if gte mso 9]>
|
||||
<xml>
|
||||
<o:OfficeDocumentSettings>
|
||||
<o:AllowPNG></o:AllowPNG>
|
||||
<o:PixelsPerInch>96</o:PixelsPerInch>
|
||||
</o:OfficeDocumentSettings>
|
||||
</xml>
|
||||
<![endif]-->
|
||||
</head>
|
||||
|
||||
<body style="font-family: sans-serif;">
|
||||
<div class="es-wrapper-color">
|
||||
<!--[if gte mso 9]>
|
||||
<v:background xmlns:v="urn:schemas-microsoft-com:vml" fill="t">
|
||||
<v:fill type="tile" color="#ffffff"></v:fill>
|
||||
</v:background>
|
||||
<![endif]-->
|
||||
<table class="es-wrapper" width="100%%" cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-email-paddings" valign="top">
|
||||
<table class="es-content esd-footer-popover" cellspacing="0" cellpadding="0" align="center">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-stripe" align="center">
|
||||
<table class="es-content-body" style="border-left:1px solid transparent;border-right:1px solid transparent;border-top:1px solid transparent;border-bottom:1px solid transparent;padding:20px 0px;" width="600" cellspacing="0" cellpadding="0" bgcolor="#ffffff" align="center">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-structure es-p20t es-p40b es-p40r es-p40l" esd-custom-block-id="8537" align="left">
|
||||
<table width="100%%" cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-container-frame" width="518" align="left">
|
||||
<table width="100%%" cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-block-image es-m-txt-c es-p5b" style="font-size:0;padding:10px" align="center"><a target="_blank"><img src="%s" alt="icon" style="display: block;" title="icon" width="30"></a></td>
|
||||
</tr>
|
||||
|
||||
<tr style="background: rgb(249,250,251);padding: 10px;margin-bottom:10px;border-radius:5px;">
|
||||
<td class="esd-block-text es-m-txt-c es-p15t" align="center" style="padding:10px;padding-bottom:30px;">
|
||||
<p>Hey there 👋</p>
|
||||
<p>We received a request to sign-up for <b>%s</b>. If this is correct, please confirm your email address by clicking the button below.</p> <br/>
|
||||
<a href="%s" class="es-button" target="_blank" style="text-decoration: none;padding:10px 15px;background-color: rgba(59,130,246,1);color: #fff;font-size: 1em;border-radius:5px;">Confirm Email</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div style="position: absolute; left: -9999px; top: -9999px; margin: 0px;"></div>
|
||||
</body>
|
||||
</html>
|
||||
`, constants.ORGANIZATION_LOGO, constants.ORGANIZATION_NAME, constants.AUTHORIZER_URL+"/verify_email"+"?token="+token)
|
||||
bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message)
|
||||
|
||||
return sender.SendMail(Receiver, Subject, bodyMessage)
|
||||
@@ -46,17 +120,92 @@ func SendForgotPasswordMail(toEmail, token, host string) error {
|
||||
Subject := "Reset Password"
|
||||
|
||||
message := fmt.Sprintf(`
|
||||
<!DOCTYPE HTML PULBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html"; charset=ISO-8859-1">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Please use the link below to reset password </h1><br/>
|
||||
<a href="%s">Reset Password</a>
|
||||
</body>
|
||||
</html>
|
||||
`, constants.RESET_PASSWORD_URL+"?token="+token)
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta content="width=device-width, initial-scale=1" name="viewport">
|
||||
<meta name="x-apple-disable-message-reformatting">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta content="telephone=no" name="format-detection">
|
||||
<title></title>
|
||||
<!--[if (mso 16)]>
|
||||
<style type="text/css">
|
||||
a {}
|
||||
</style>
|
||||
<![endif]-->
|
||||
<!--[if gte mso 9]><style>sup { font-size: 100%% !important; }</style><![endif]-->
|
||||
<!--[if gte mso 9]>
|
||||
<xml>
|
||||
<o:OfficeDocumentSettings>
|
||||
<o:AllowPNG></o:AllowPNG>
|
||||
<o:PixelsPerInch>96</o:PixelsPerInch>
|
||||
</o:OfficeDocumentSettings>
|
||||
</xml>
|
||||
<![endif]-->
|
||||
</head>
|
||||
|
||||
<body style="font-family: sans-serif;">
|
||||
<div class="es-wrapper-color">
|
||||
<!--[if gte mso 9]>
|
||||
<v:background xmlns:v="urn:schemas-microsoft-com:vml" fill="t">
|
||||
<v:fill type="tile" color="#ffffff"></v:fill>
|
||||
</v:background>
|
||||
<![endif]-->
|
||||
<table class="es-wrapper" width="100%%" cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-email-paddings" valign="top">
|
||||
<table class="es-content esd-footer-popover" cellspacing="0" cellpadding="0" align="center">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-stripe" align="center">
|
||||
<table class="es-content-body" style="border-left:1px solid transparent;border-right:1px solid transparent;border-top:1px solid transparent;border-bottom:1px solid transparent;padding:20px 0px;" width="600" cellspacing="0" cellpadding="0" bgcolor="#ffffff" align="center">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-structure es-p20t es-p40b es-p40r es-p40l" esd-custom-block-id="8537" align="left">
|
||||
<table width="100%%" cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-container-frame" width="518" align="left">
|
||||
<table width="100%%" cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-block-image es-m-txt-c es-p5b" style="font-size:0;padding:10px" align="center"><a target="_blank"><img src="%s" alt="icon" style="display: block;" title="icon" width="30"></a></td>
|
||||
</tr>
|
||||
|
||||
<tr style="background: rgb(249,250,251);padding: 10px;margin-bottom:10px;border-radius:5px;">
|
||||
<td class="esd-block-text es-m-txt-c es-p15t" align="center" style="padding:10px;padding-bottom:30px;">
|
||||
<p>Hey there 👋</p>
|
||||
<p>We received a request to reset password for email: <b>%s</b>. If this is correct, please reset the password clicking the button below.</p> <br/>
|
||||
<a href="%s" class="es-button" target="_blank" style="text-decoration: none;padding:10px 15px;background-color: rgba(59,130,246,1);color: #fff;font-size: 1em;border-radius:5px;">Reset Password</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div style="position: absolute; left: -9999px; top: -9999px; margin: 0px;"></div>
|
||||
</body>
|
||||
</html>
|
||||
`, constants.ORGANIZATION_LOGO, toEmail, constants.RESET_PASSWORD_URL+"?token="+token)
|
||||
|
||||
bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message)
|
||||
|
||||
return sender.SendMail(Receiver, Subject, bodyMessage)
|
||||
|
@@ -40,30 +40,14 @@ func IsSuperAdmin(gc *gin.Context) bool {
|
||||
return secret == constants.ADMIN_SECRET
|
||||
}
|
||||
|
||||
func IsValidRolesArray(roles []string) bool {
|
||||
func IsValidRoles(userRoles []string, roles []string) bool {
|
||||
valid := true
|
||||
currentRoleMap := map[string]bool{}
|
||||
|
||||
for _, currentRole := range constants.ROLES {
|
||||
currentRoleMap[currentRole] = true
|
||||
}
|
||||
for _, inputRole := range roles {
|
||||
if !currentRoleMap[inputRole] {
|
||||
for _, role := range roles {
|
||||
if !StringContains(userRoles, role) {
|
||||
valid = false
|
||||
break
|
||||
}
|
||||
}
|
||||
return valid
|
||||
}
|
||||
|
||||
func IsValidRole(userRoles []string, role string) bool {
|
||||
valid := false
|
||||
for _, currentRole := range userRoles {
|
||||
if role == currentRole {
|
||||
valid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return valid
|
||||
}
|
||||
|
Reference in New Issue
Block a user