Feat/dashboard (#105)

This commit is contained in:
Lakhan Samani 2022-01-17 11:32:13 +05:30 committed by GitHub
parent 7ce96367a3
commit f1b4141367
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
120 changed files with 3381 additions and 3044 deletions

View File

@ -70,7 +70,11 @@ docker run --name arangodb -d -p 8529:8529 -e ARANGO_NO_AUTH=1 arangodb/arangodb
If you are adding new resolver,
1. create new resolver test file [here](https://github.com/authorizerdev/authorizer/tree/main/server/__test__)
<<<<<<< HEAD
Naming convention filename: `resolver_name_test.go` function name: `resolverNameTest(t *testing.T, s TestSetup)`
=======
Naming convention filename: `resolver_name_test.go` function name: `resolverNameTest(s TestSetup, t *testing.T)`
>>>>>>> main
2. Add your tests [here](https://github.com/authorizerdev/authorizer/blob/main/server/__test__/resolvers_test.go#L38)
**Command to run tests:**

View File

@ -10,4 +10,4 @@ build-dashboard:
clean:
rm -rf build
test:
cd server && go clean --testcache && go test -v ./__test__
cd server && go clean --testcache && go test -v ./test

View File

@ -42,7 +42,11 @@ export default function App() {
<BrowserRouter>
<AuthorizerProvider
config={{
<<<<<<< HEAD
authorizerURL: window.location.origin,
=======
authorizerURL: globalState.authorizerURL,
>>>>>>> main
redirectURL: globalState.redirectURL,
}}
>

File diff suppressed because it is too large Load Diff

0
dashboard/src/Router.tsx Normal file
View File

View File

@ -0,0 +1,5 @@
import React from 'react';
export default function AuthLayout() {
return <h1>Auth Layout</h1>;
}

View File

@ -0,0 +1,5 @@
import React from 'react';
export default function DefaultLayout() {
return <h1>Default Layout</h1>;
}

View File

View File

@ -1,26 +0,0 @@
package test
import (
"fmt"
"testing"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/stretchr/testify/assert"
)
func adminLogoutTests(s TestSetup, t *testing.T) {
t.Run(`should get admin session`, func(t *testing.T) {
req, ctx := createContext(s)
_, err := resolvers.AdminLogout(ctx)
assert.NotNil(t, err)
h, err := utils.HashPassword(constants.EnvData.ADMIN_SECRET)
assert.Nil(t, err)
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", constants.EnvData.ADMIN_COOKIE_NAME, h))
_, err = resolvers.AdminLogout(ctx)
assert.Nil(t, err)
})
}

View File

@ -1,28 +0,0 @@
package test
import (
"fmt"
"log"
"testing"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/stretchr/testify/assert"
)
func adminSessionTests(s TestSetup, t *testing.T) {
t.Run(`should get admin session`, func(t *testing.T) {
req, ctx := createContext(s)
_, err := resolvers.AdminSession(ctx)
log.Println("error:", err)
assert.NotNil(t, err)
h, err := utils.HashPassword(constants.EnvData.ADMIN_SECRET)
assert.Nil(t, err)
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", constants.EnvData.ADMIN_COOKIE_NAME, h))
_, err = resolvers.AdminSession(ctx)
assert.Nil(t, err)
})
}

View File

@ -1,25 +0,0 @@
package test
import (
"testing"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/stretchr/testify/assert"
)
func TestEnvs(t *testing.T) {
constants.EnvData.ENV_PATH = "../../.env.sample"
assert.Equal(t, constants.EnvData.ADMIN_SECRET, "admin")
assert.Equal(t, constants.EnvData.ENV, "production")
assert.False(t, constants.EnvData.DISABLE_EMAIL_VERIFICATION)
assert.False(t, constants.EnvData.DISABLE_MAGIC_LINK_LOGIN)
assert.False(t, constants.EnvData.DISABLE_BASIC_AUTHENTICATION)
assert.Equal(t, constants.EnvData.JWT_TYPE, "HS256")
assert.Equal(t, constants.EnvData.JWT_SECRET, "random_string")
assert.Equal(t, constants.EnvData.JWT_ROLE_CLAIM, "role")
assert.EqualValues(t, constants.EnvData.ROLES, []string{"user"})
assert.EqualValues(t, constants.EnvData.DEFAULT_ROLES, []string{"user"})
assert.EqualValues(t, constants.EnvData.PROTECTED_ROLES, []string{"admin"})
assert.EqualValues(t, constants.EnvData.ALLOWED_ORIGINS, []string{"*"})
}

View File

@ -1,65 +0,0 @@
package test
import (
"log"
"testing"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/env"
)
func TestResolvers(t *testing.T) {
databases := map[string]string{
enum.Sqlite.String(): "../../data.db",
enum.Arangodb.String(): "http://localhost:8529",
enum.Mongodb.String(): "mongodb://localhost:27017",
}
for dbType, dbURL := range databases {
constants.EnvData.DATABASE_URL = dbURL
constants.EnvData.DATABASE_TYPE = dbType
db.InitDB()
// clean the persisted config for test to use fresh config
config, err := db.Mgr.GetConfig()
if err == nil {
config.Config = []byte{}
db.Mgr.UpdateConfig(config)
}
env.PersistEnv()
s := testSetup()
defer s.Server.Close()
log.Println("EnvData:", constants.EnvData)
t.Run("should pass tests for "+dbType, func(t *testing.T) {
// admin tests
adminSignupTests(s, t)
verificationRequestsTest(s, t)
usersTest(s, t)
deleteUserTest(s, t)
updateUserTest(s, t)
adminLoginTests(s, t)
adminLogoutTests(s, t)
adminSessionTests(s, t)
updateConfigTests(s, t)
configTests(s, t)
// user tests
loginTests(s, t)
signupTests(s, t)
forgotPasswordTest(s, t)
resendVerifyEmailTests(s, t)
resetPasswordTest(s, t)
verifyEmailTest(s, t)
sessionTests(s, t)
profileTests(s, t)
updateProfileTests(s, t)
magicLinkLoginTests(s, t)
logoutTests(s, t)
metaTests(s, t)
})
}
}

View File

@ -1,62 +0,0 @@
package constants
type EnvConst struct {
ADMIN_SECRET string
ENV string
ENV_PATH string
VERSION string
DATABASE_TYPE string
DATABASE_URL string
DATABASE_NAME string
SMTP_HOST string
SMTP_PORT string
SMTP_PASSWORD string
SMTP_USERNAME string
SENDER_EMAIL string
JWT_TYPE string
JWT_SECRET string
ALLOWED_ORIGINS []string
AUTHORIZER_URL string
APP_URL string
PORT string
REDIS_URL string
COOKIE_NAME string
ADMIN_COOKIE_NAME string
RESET_PASSWORD_URL string
ENCRYPTION_KEY string `json:"-"`
IS_PROD bool
DISABLE_EMAIL_VERIFICATION bool
DISABLE_BASIC_AUTHENTICATION bool
DISABLE_MAGIC_LINK_LOGIN bool
DISABLE_LOGIN_PAGE bool
// ROLES
ROLES []string
PROTECTED_ROLES []string
DEFAULT_ROLES []string
JWT_ROLE_CLAIM string
// OAuth login
GOOGLE_CLIENT_ID string
GOOGLE_CLIENT_SECRET string
GITHUB_CLIENT_ID string
GITHUB_CLIENT_SECRET string
FACEBOOK_CLIENT_ID string
FACEBOOK_CLIENT_SECRET string
// Org envs
ORGANIZATION_NAME string
ORGANIZATION_LOGO string
}
var EnvData = EnvConst{
ADMIN_COOKIE_NAME: "authorizer-admin",
JWT_ROLE_CLAIM: "role",
ORGANIZATION_NAME: "Authorizer",
ORGANIZATION_LOGO: "https://authorizer.dev/images/logo.png",
DISABLE_EMAIL_VERIFICATION: false,
DISABLE_BASIC_AUTHENTICATION: false,
DISABLE_MAGIC_LINK_LOGIN: false,
DISABLE_LOGIN_PAGE: false,
IS_PROD: false,
}

View File

@ -0,0 +1,16 @@
package constants
const (
// DbTypePostgres is the postgres database type
DbTypePostgres = "postgres"
// DbTypeSqlite is the sqlite database type
DbTypeSqlite = "sqlite"
// DbTypeMysql is the mysql database type
DbTypeMysql = "mysql"
// DbTypeSqlserver is the sqlserver database type
DbTypeSqlserver = "sqlserver"
// DbTypeArangodb is the arangodb database type
DbTypeArangodb = "arangodb"
// DbTypeMongodb is the mongodb database type
DbTypeMongodb = "mongodb"
)

87
server/constants/env.go Normal file
View File

@ -0,0 +1,87 @@
package constants
const (
// EnvKeyEnv key for env variable ENV
EnvKeyEnv = "ENV"
// EnvKeyEnvPath key for cli arg variable ENV_PATH
EnvKeyEnvPath = "ENV_PATH"
// EnvKeyVersion key for build arg version
EnvKeyVersion = "VERSION"
// EnvKeyAuthorizerURL key for env variable AUTHORIZER_URL
EnvKeyAuthorizerURL = "AUTHORIZER_URL"
// EnvKeyPort key for env variable PORT
EnvKeyPort = "PORT"
// EnvKeyAdminSecret key for env variable ADMIN_SECRET
EnvKeyAdminSecret = "ADMIN_SECRET"
// EnvKeyDatabaseType key for env variable DATABASE_TYPE
EnvKeyDatabaseType = "DATABASE_TYPE"
// EnvKeyDatabaseURL key for env variable DATABASE_URL
EnvKeyDatabaseURL = "DATABASE_URL"
// EnvKeyDatabaseName key for env variable DATABASE_NAME
EnvKeyDatabaseName = "DATABASE_NAME"
// EnvKeySmtpHost key for env variable SMTP_HOST
EnvKeySmtpHost = "SMTP_HOST"
// EnvKeySmtpPort key for env variable SMTP_PORT
EnvKeySmtpPort = "SMTP_PORT"
// EnvKeySmtpUsername key for env variable SMTP_USERNAME
EnvKeySmtpUsername = "SMTP_USERNAME"
// EnvKeySmtpPassword key for env variable SMTP_PASSWORD
EnvKeySmtpPassword = "SMTP_PASSWORD"
// EnvKeySenderEmail key for env variable SENDER_EMAIL
EnvKeySenderEmail = "SENDER_EMAIL"
// EnvKeyJwtType key for env variable JWT_TYPE
EnvKeyJwtType = "JWT_TYPE"
// EnvKeyJwtSecret key for env variable JWT_SECRET
EnvKeyJwtSecret = "JWT_SECRET"
// EnvKeyAllowedOrigins key for env variable ALLOWED_ORIGINS
EnvKeyAllowedOrigins = "ALLOWED_ORIGINS"
// EnvKeyAppURL key for env variable APP_URL
EnvKeyAppURL = "APP_URL"
// EnvKeyRedisURL key for env variable REDIS_URL
EnvKeyRedisURL = "REDIS_URL"
// EnvKeyCookieName key for env variable COOKIE_NAME
EnvKeyCookieName = "COOKIE_NAME"
// EnvKeyAdminCookieName key for env variable ADMIN_COOKIE_NAME
EnvKeyAdminCookieName = "ADMIN_COOKIE_NAME"
// EnvKeyResetPasswordURL key for env variable RESET_PASSWORD_URL
EnvKeyResetPasswordURL = "RESET_PASSWORD_URL"
// EnvKeyEncryptionKey key for env variable ENCRYPTION_KEY
EnvKeyEncryptionKey = "ENCRYPTION_KEY"
// EnvKeyDisableEmailVerification key for env variable DISABLE_EMAIL_VERIFICATION
EnvKeyDisableEmailVerification = "DISABLE_EMAIL_VERIFICATION"
// EnvKeyDisableBasicAuthentication key for env variable DISABLE_BASIC_AUTH
EnvKeyDisableBasicAuthentication = "DISABLE_BASIC_AUTHENTICATION"
// EnvKeyDisableMagicLinkLogin key for env variable DISABLE_MAGIC_LINK_LOGIN
EnvKeyDisableMagicLinkLogin = "DISABLE_MAGIC_LINK_LOGIN"
// EnvKeyDisableLoginPage key for env variable DISABLE_LOGIN_PAGE
EnvKeyDisableLoginPage = "DISABLE_LOGIN_PAGE"
// EnvKeyRoles key for env variable ROLES
EnvKeyRoles = "ROLES"
// EnvKeyProtectedRoles key for env variable PROTECTED_ROLES
EnvKeyProtectedRoles = "PROTECTED_ROLES"
// EnvKeyDefaultRoles key for env variable DEFAULT_ROLES
EnvKeyDefaultRoles = "DEFAULT_ROLES"
// EnvKeyJwtRoleClaim key for env variable JWT_ROLE_CLAIM
EnvKeyJwtRoleClaim = "JWT_ROLE_CLAIM"
// EnvKeyGoogleClientID key for env variable GOOGLE_CLIENT_ID
EnvKeyGoogleClientID = "GOOGLE_CLIENT_ID"
// EnvKeyGoogleClientSecret key for env variable GOOGLE_CLIENT_SECRET
EnvKeyGoogleClientSecret = "GOOGLE_CLIENT_SECRET"
// EnvKeyGithubClientID key for env variable GITHUB_CLIENT_ID
EnvKeyGithubClientID = "GITHUB_CLIENT_ID"
// EnvKeyGithubClientSecret key for env variable GITHUB_CLIENT_SECRET
EnvKeyGithubClientSecret = "GITHUB_CLIENT_SECRET"
// EnvKeyFacebookClientID key for env variable FACEBOOK_CLIENT_ID
EnvKeyFacebookClientID = "FACEBOOK_CLIENT_ID"
// EnvKeyFacebookClientSecret key for env variable FACEBOOK_CLIENT_SECRET
EnvKeyFacebookClientSecret = "FACEBOOK_CLIENT_SECRET"
// EnvKeyOrganizationName key for env variable ORGANIZATION_NAME
EnvKeyOrganizationName = "ORGANIZATION_NAME"
// EnvKeyOrganizationLogo key for env variable ORGANIZATION_LOGO
EnvKeyOrganizationLogo = "ORGANIZATION_LOGO"
// EnvKeyIsProd key for env variable IS_PROD
EnvKeyIsProd = "IS_PROD"
// EnvKeyCustomAccessTokenScript key for env variable CUSTOM_ACCESS_TOKEN_SCRIPT
EnvKeyCustomAccessTokenScript = "CUSTOM_ACCESS_TOKEN_SCRIPT"
)

View File

@ -1,6 +1,6 @@
package constants
var (
const (
// 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"

View File

@ -0,0 +1,14 @@
package constants
const (
// SignupMethodBasicAuth is the basic_auth signup method
SignupMethodBasicAuth = "basic_auth"
// SignupMethodMagicLinkLogin is the magic_link_login signup method
SignupMethodMagicLinkLogin = "magic_link_login"
// SignupMethodGoogle is the google signup method
SignupMethodGoogle = "google"
// SignupMethodGithub is the github signup method
SignupMethodGithub = "github"
// SignupMethodFacebook is the facebook signup method
SignupMethodFacebook = "facebook"
)

View File

@ -0,0 +1,8 @@
package constants
const (
// TokenTypeRefreshToken is the refresh_token token type
TokenTypeRefreshToken = "refresh_token"
// TokenTypeAccessToken is the access_token token type
TokenTypeAccessToken = "access_token"
)

View File

@ -0,0 +1,12 @@
package constants
const (
// VerificationTypeBasicAuthSignup is the basic_auth_signup verification type
VerificationTypeBasicAuthSignup = "basic_auth_signup"
// VerificationTypeMagicLinkLogin is the magic_link_login verification type
VerificationTypeMagicLinkLogin = "magic_link_login"
// VerificationTypeUpdateEmail is the update_email verification type
VerificationTypeUpdateEmail = "update_email"
// VerificationTypeForgotPassword is the forgot_password verification type
VerificationTypeForgotPassword = "forgot_password"
)

View File

@ -8,6 +8,7 @@ import (
arangoDriver "github.com/arangodb/go-driver"
"github.com/arangodb/go-driver/http"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
)
// for this we need arangodb instance up and running
@ -17,7 +18,7 @@ import (
func initArangodb() (arangoDriver.Database, error) {
ctx := context.Background()
conn, err := http.NewConnection(http.ConnectionConfig{
Endpoints: []string{constants.EnvData.DATABASE_URL},
Endpoints: []string{envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDatabaseURL).(string)},
})
if err != nil {
return nil, err
@ -32,16 +33,16 @@ func initArangodb() (arangoDriver.Database, error) {
var arangodb driver.Database
arangodb_exists, err := arangoClient.DatabaseExists(nil, constants.EnvData.DATABASE_NAME)
arangodb_exists, err := arangoClient.DatabaseExists(nil, envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDatabaseName).(string))
if arangodb_exists {
log.Println(constants.EnvData.DATABASE_NAME + " db exists already")
arangodb, err = arangoClient.Database(nil, constants.EnvData.DATABASE_NAME)
log.Println(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDatabaseName).(string) + " db exists already")
arangodb, err = arangoClient.Database(nil, envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDatabaseName).(string))
if err != nil {
return nil, err
}
} else {
arangodb, err = arangoClient.CreateDatabase(nil, constants.EnvData.DATABASE_NAME, nil)
arangodb, err = arangoClient.CreateDatabase(nil, envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDatabaseName).(string), nil)
if err != nil {
return nil, err
}

View File

@ -5,7 +5,7 @@ import (
arangoDriver "github.com/arangodb/go-driver"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/envstore"
"go.mongodb.org/mongo-driver/mongo"
"gorm.io/driver/mysql"
"gorm.io/driver/postgres"
@ -66,9 +66,9 @@ func InitDB() {
var sqlDB *gorm.DB
var err error
IsORMSupported = constants.EnvData.DATABASE_TYPE != enum.Arangodb.String() && constants.EnvData.DATABASE_TYPE != enum.Mongodb.String()
IsArangoDB = constants.EnvData.DATABASE_TYPE == enum.Arangodb.String()
IsMongoDB = constants.EnvData.DATABASE_TYPE == enum.Mongodb.String()
IsORMSupported = envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDatabaseType).(string) != constants.DbTypeArangodb && envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDatabaseType).(string) != constants.DbTypeMongodb
IsArangoDB = envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDatabaseType).(string) == constants.DbTypeArangodb
IsMongoDB = envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDatabaseType).(string) == constants.DbTypeMongodb
// sql db orm config
ormConfig := &gorm.Config{
@ -77,22 +77,22 @@ func InitDB() {
},
}
log.Println("db type:", constants.EnvData.DATABASE_TYPE)
log.Println("db type:", envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDatabaseType).(string))
switch constants.EnvData.DATABASE_TYPE {
case enum.Postgres.String():
sqlDB, err = gorm.Open(postgres.Open(constants.EnvData.DATABASE_URL), ormConfig)
switch envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDatabaseType).(string) {
case constants.DbTypePostgres:
sqlDB, err = gorm.Open(postgres.Open(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDatabaseURL).(string)), ormConfig)
break
case enum.Sqlite.String():
sqlDB, err = gorm.Open(sqlite.Open(constants.EnvData.DATABASE_URL), ormConfig)
case constants.DbTypeSqlite:
sqlDB, err = gorm.Open(sqlite.Open(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDatabaseURL).(string)), ormConfig)
break
case enum.Mysql.String():
sqlDB, err = gorm.Open(mysql.Open(constants.EnvData.DATABASE_URL), ormConfig)
case constants.DbTypeMysql:
sqlDB, err = gorm.Open(mysql.Open(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDatabaseURL).(string)), ormConfig)
break
case enum.SQLServer.String():
sqlDB, err = gorm.Open(sqlserver.Open(constants.EnvData.DATABASE_URL), ormConfig)
case constants.DbTypeSqlserver:
sqlDB, err = gorm.Open(sqlserver.Open(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDatabaseURL).(string)), ormConfig)
break
case enum.Arangodb.String():
case constants.DbTypeArangodb:
arangodb, err := initArangodb()
if err != nil {
log.Fatal("error initializing arangodb:", err)
@ -105,7 +105,7 @@ func InitDB() {
}
break
case enum.Mongodb.String():
case constants.DbTypeMongodb:
mongodb, err := initMongodb()
if err != nil {
log.Fatal("error initializing mongodb connection:", err)

View File

@ -5,6 +5,7 @@ import (
"time"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
@ -12,7 +13,7 @@ import (
)
func initMongodb() (*mongo.Database, error) {
mongodbOptions := options.Client().ApplyURI(constants.EnvData.DATABASE_URL)
mongodbOptions := options.Client().ApplyURI(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDatabaseURL).(string))
maxWait := time.Duration(5 * time.Second)
mongodbOptions.ConnectTimeout = &maxWait
mongoClient, err := mongo.NewClient(mongodbOptions)
@ -30,7 +31,7 @@ func initMongodb() (*mongo.Database, error) {
return nil, err
}
mongodb := mongoClient.Database(constants.EnvData.DATABASE_NAME, options.Database())
mongodb := mongoClient.Database(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDatabaseName).(string), options.Database())
mongodb.CreateCollection(ctx, Collections.User, options.CreateCollection())
userCollection := mongodb.Collection(Collections.User, options.Collection())

View File

@ -3,11 +3,13 @@ package db
import (
"fmt"
"log"
"strings"
"time"
"github.com/arangodb/go-driver"
arangoDriver "github.com/arangodb/go-driver"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/google/uuid"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
@ -43,7 +45,7 @@ func (mgr *manager) AddUser(user User) (User, error) {
}
if user.Roles == "" {
user.Roles = constants.EnvData.DEFAULT_ROLES[0]
user.Roles = strings.Join(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDefaultRoles).([]string), ",")
}
if IsORMSupported {
@ -185,6 +187,7 @@ func (mgr *manager) GetUsers() ([]User, error) {
return users, nil
}
// GetUserByEmail function to get user by email
func (mgr *manager) GetUserByEmail(email string) (User, error) {
var user User
@ -233,6 +236,7 @@ func (mgr *manager) GetUserByEmail(email string) (User, error) {
return user, nil
}
// GetUserByID function to get user by ID
func (mgr *manager) GetUserByID(id string) (User, error) {
var user User
@ -281,6 +285,7 @@ func (mgr *manager) GetUserByID(id string) (User, error) {
return user, nil
}
// DeleteUser function to delete user
func (mgr *manager) DeleteUser(user User) error {
if IsORMSupported {
result := mgr.sqlDB.Delete(&user)

View File

@ -1,23 +1,44 @@
package email
import (
"bytes"
"crypto/tls"
"encoding/json"
"log"
"strconv"
"text/template"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
gomail "gopkg.in/mail.v2"
)
// addEmailTemplate is used to add html template in email body
func addEmailTemplate(a string, b map[string]interface{}, templateName string) string {
tmpl, err := template.New(templateName).Parse(a)
if err != nil {
output, _ := json.Marshal(b)
return string(output)
}
buf := &bytes.Buffer{}
err = tmpl.Execute(buf, b)
if err != nil {
panic(err)
}
s := buf.String()
return s
}
// SendMail function to send mail
func SendMail(to []string, Subject, bodyMessage string) error {
m := gomail.NewMessage()
m.SetHeader("From", constants.EnvData.SENDER_EMAIL)
m.SetHeader("From", envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeySenderEmail).(string))
m.SetHeader("To", to...)
m.SetHeader("Subject", Subject)
m.SetBody("text/html", bodyMessage)
port, _ := strconv.Atoi(constants.EnvData.SMTP_PORT)
d := gomail.NewDialer(constants.EnvData.SMTP_HOST, port, constants.EnvData.SMTP_USERNAME, constants.EnvData.SMTP_PASSWORD)
if constants.EnvData.ENV == "development" {
port, _ := strconv.Atoi(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeySmtpPort).(string))
d := gomail.NewDialer(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeySmtpHost).(string), port, envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeySmtpUsername).(string), envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeySmtpPassword).(string))
if envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyEnv).(string) == "development" {
d.TLSConfig = &tls.Config{InsecureSkipVerify: true}
}
if err := d.DialAndSend(m); err != nil {

View File

@ -0,0 +1,112 @@
package email
import (
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
)
// SendForgotPasswordMail to send forgot password email
func SendForgotPasswordMail(toEmail, token, host string) error {
resetPasswordUrl := envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyResetPasswordURL).(string)
if resetPasswordUrl == "" {
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.EnvKeyResetPasswordURL, envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAuthorizerURL).(string)+"/app/reset-password")
}
// The receiver needs to be in slice as the receive supports multiple receiver
Receiver := []string{toEmail}
Subject := "Reset Password"
message := `
<!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" clicktracking="off"><img src="{{.org_logo}}" 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 have received a request to reset password for email: <b>{{.org_name}}</b>. If this is correct, please reset the password clicking the button below.</p> <br/>
<a clicktracking="off" href="{{.verification_url}}" 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>
`
data := make(map[string]interface{}, 3)
data["org_logo"] = envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyOrganizationLogo).(string)
data["org_name"] = envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyOrganizationName).(string)
data["verification_url"] = resetPasswordUrl + "?token=" + token
message = addEmailTemplate(message, data, "reset_password_email.tmpl")
return SendMail(Receiver, Subject, message)
}

View File

@ -0,0 +1,107 @@
package email
import (
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
)
// SendVerificationMail to send verification email
func SendVerificationMail(toEmail, token string) error {
// The receiver needs to be in slice as the receive supports multiple receiver
Receiver := []string{toEmail}
Subject := "Please verify your email"
message := `
<!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" clicktracking="off"><img src="{{.org_logo}}" 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 have received request to verify email for <b>{{.org_name}}</b>. If this is correct, please confirm your email address by clicking the button below.</p> <br/>
<a
clicktracking="off" href="{{.verification_url}}" 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>
`
data := make(map[string]interface{}, 3)
data["org_logo"] = envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyOrganizationLogo).(string)
data["org_name"] = envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyOrganizationName).(string)
data["verification_url"] = envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAuthorizerURL).(string) + "/verify_email?token=" + token
message = addEmailTemplate(message, data, "verify_email.tmpl")
// bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message)
return SendMail(Receiver, Subject, message)
}

View File

@ -1,23 +0,0 @@
package enum
type DbType int
const (
Postgres DbType = iota
Sqlite
Mysql
SQLServer
Arangodb
Mongodb
)
func (d DbType) String() string {
return [...]string{
"postgres",
"sqlite",
"mysql",
"sqlserver",
"arangodb",
"mongodb",
}[d]
}

View File

@ -1,15 +0,0 @@
package enum
type OAuthProvider int
const (
GoogleProvider OAuthProvider = iota
GithubProvider
)
func (d OAuthProvider) String() string {
return [...]string{
"google_provider",
"github_provider",
}[d]
}

View File

@ -1,21 +0,0 @@
package enum
type SignupMethod int
const (
BasicAuth SignupMethod = iota
MagicLinkLogin
Google
Github
Facebook
)
func (d SignupMethod) String() string {
return [...]string{
"basic_auth",
"magic_link_login",
"google",
"github",
"facebook",
}[d]
}

View File

@ -1,15 +0,0 @@
package enum
type TokenType int
const (
RefreshToken TokenType = iota
AccessToken
)
func (d TokenType) String() string {
return [...]string{
"refresh_token",
"access_token",
}[d]
}

View File

@ -1,17 +0,0 @@
package enum
type VerificationType int
const (
BasicAuthSignup VerificationType = iota
UpdateEmail
ForgotPassword
)
func (d VerificationType) String() string {
return [...]string{
"basic_auth_signup",
"update_email",
"forgot_password",
}[d]
}

255
server/env/env.go vendored
View File

@ -6,188 +6,197 @@ import (
"strings"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/google/uuid"
"github.com/joho/godotenv"
)
// build variables
// TODO move this to env store
var (
ARG_DB_URL *string
ARG_DB_TYPE *string
ARG_AUTHORIZER_URL *string
ARG_ENV_FILE *string
// ARG_DB_URL is the cli arg variable for the database url
ARG_DB_URL *string
// ARG_DB_TYPE is the cli arg variable for the database type
ARG_DB_TYPE *string
// ARG_ENV_FILE is the cli arg variable for the env file
ARG_ENV_FILE *string
)
// InitEnv -> to initialize env and through error if required env are not present
// InitEnv to initialize EnvData and through error if required env are not present
func InitEnv() {
if constants.EnvData.ENV_PATH == "" {
constants.EnvData.ENV_PATH = `.env`
// get clone of current store
envData := envstore.EnvInMemoryStoreObj.GetEnvStoreClone()
if envData[constants.EnvKeyEnv] == nil || envData[constants.EnvKeyEnv] == "" {
envData[constants.EnvKeyEnv] = os.Getenv("ENV")
if envData[constants.EnvKeyEnv] == "" {
envData[constants.EnvKeyEnv] = "production"
}
if envData[constants.EnvKeyEnv] == "production" {
envData[constants.EnvKeyIsProd] = true
os.Setenv("GIN_MODE", "release")
} else {
envData[constants.EnvKeyIsProd] = false
}
}
// set authorizer url to empty string so that fresh url is obtained with every server start
envData[constants.EnvKeyAuthorizerURL] = ""
if envData[constants.EnvKeyAppURL] == nil || envData[constants.EnvKeyAppURL] == "" {
envData[constants.EnvKeyAppURL] = os.Getenv(constants.EnvKeyAppURL)
}
if envData[constants.EnvKeyEnvPath] == nil || envData[constants.EnvKeyEnvPath].(string) == "" {
envData[constants.EnvKeyEnvPath] = `.env`
}
if ARG_ENV_FILE != nil && *ARG_ENV_FILE != "" {
constants.EnvData.ENV_PATH = *ARG_ENV_FILE
envData[constants.EnvKeyEnvPath] = *ARG_ENV_FILE
}
err := godotenv.Load(constants.EnvData.ENV_PATH)
err := godotenv.Load(envData[constants.EnvKeyEnvPath].(string))
if err != nil {
log.Printf("error loading %s file", constants.EnvData.ENV_PATH)
log.Printf("error loading %s file", envData[constants.EnvKeyEnvPath])
}
if constants.EnvData.ADMIN_SECRET == "" {
constants.EnvData.ADMIN_SECRET = os.Getenv("ADMIN_SECRET")
if envData[constants.EnvKeyPort] == nil || envData[constants.EnvKeyPort].(string) == "" {
envData[constants.EnvKeyPort] = os.Getenv("PORT")
if envData[constants.EnvKeyPort].(string) == "" {
envData[constants.EnvKeyPort] = "8080"
}
}
if constants.EnvData.DATABASE_TYPE == "" {
constants.EnvData.DATABASE_TYPE = os.Getenv("DATABASE_TYPE")
log.Println(constants.EnvData.DATABASE_TYPE)
if envData[constants.EnvKeyAdminSecret] == nil || envData[constants.EnvKeyAdminSecret].(string) == "" {
envData[constants.EnvKeyAdminSecret] = os.Getenv("ADMIN_SECRET")
}
if envData[constants.EnvKeyDatabaseType] == nil || envData[constants.EnvKeyDatabaseType].(string) == "" {
envData[constants.EnvKeyDatabaseType] = os.Getenv("DATABASE_TYPE")
log.Println(envData[constants.EnvKeyDatabaseType].(string))
if ARG_DB_TYPE != nil && *ARG_DB_TYPE != "" {
constants.EnvData.DATABASE_TYPE = *ARG_DB_TYPE
envData[constants.EnvKeyDatabaseType] = *ARG_DB_TYPE
}
if constants.EnvData.DATABASE_TYPE == "" {
if envData[constants.EnvKeyDatabaseType].(string) == "" {
panic("DATABASE_TYPE is required")
}
}
if constants.EnvData.DATABASE_URL == "" {
constants.EnvData.DATABASE_URL = os.Getenv("DATABASE_URL")
if envData[constants.EnvKeyDatabaseURL] == nil || envData[constants.EnvKeyDatabaseURL].(string) == "" {
envData[constants.EnvKeyDatabaseURL] = os.Getenv("DATABASE_URL")
if ARG_DB_URL != nil && *ARG_DB_URL != "" {
constants.EnvData.DATABASE_URL = *ARG_DB_URL
envData[constants.EnvKeyDatabaseURL] = *ARG_DB_URL
}
if constants.EnvData.DATABASE_URL == "" {
if envData[constants.EnvKeyDatabaseURL] == "" {
panic("DATABASE_URL is required")
}
}
if constants.EnvData.DATABASE_NAME == "" {
constants.EnvData.DATABASE_NAME = os.Getenv("DATABASE_NAME")
if constants.EnvData.DATABASE_NAME == "" {
constants.EnvData.DATABASE_NAME = "authorizer"
if envData[constants.EnvKeyDatabaseName] == nil || envData[constants.EnvKeyDatabaseName].(string) == "" {
envData[constants.EnvKeyDatabaseName] = os.Getenv("DATABASE_NAME")
if envData[constants.EnvKeyDatabaseName].(string) == "" {
envData[constants.EnvKeyDatabaseName] = "authorizer"
}
}
if constants.EnvData.ENV == "" {
constants.EnvData.ENV = os.Getenv("ENV")
if constants.EnvData.ENV == "" {
constants.EnvData.ENV = "production"
}
if envData[constants.EnvKeySmtpHost] == nil || envData[constants.EnvKeySmtpHost].(string) == "" {
envData[constants.EnvKeySmtpHost] = os.Getenv("SMTP_HOST")
}
if constants.EnvData.ENV == "production" {
constants.EnvData.IS_PROD = true
os.Setenv("GIN_MODE", "release")
} else {
constants.EnvData.IS_PROD = false
if envData[constants.EnvKeySmtpPort] == nil || envData[constants.EnvKeySmtpPort].(string) == "" {
envData[constants.EnvKeySmtpPort] = os.Getenv("SMTP_PORT")
}
if envData[constants.EnvKeySmtpUsername] == nil || envData[constants.EnvKeySmtpUsername].(string) == "" {
envData[constants.EnvKeySmtpUsername] = os.Getenv("SMTP_USERNAME")
}
if envData[constants.EnvKeySmtpPassword] == nil || envData[constants.EnvKeySmtpPassword].(string) == "" {
envData[constants.EnvKeySmtpPassword] = os.Getenv("SMTP_PASSWORD")
}
if envData[constants.EnvKeySenderEmail] == nil || envData[constants.EnvKeySenderEmail].(string) == "" {
envData[constants.EnvKeySenderEmail] = os.Getenv("SENDER_EMAIL")
}
if envData[constants.EnvKeyJwtSecret] == nil || envData[constants.EnvKeyJwtSecret].(string) == "" {
envData[constants.EnvKeyJwtSecret] = os.Getenv("JWT_SECRET")
if envData[constants.EnvKeyJwtSecret].(string) == "" {
envData[constants.EnvKeyJwtSecret] = uuid.New().String()
}
}
if constants.EnvData.SMTP_HOST == "" {
constants.EnvData.SMTP_HOST = os.Getenv("SMTP_HOST")
}
if constants.EnvData.SMTP_PORT == "" {
constants.EnvData.SMTP_PORT = os.Getenv("SMTP_PORT")
}
if constants.EnvData.SMTP_USERNAME == "" {
constants.EnvData.SMTP_USERNAME = os.Getenv("SMTP_USERNAME")
}
if constants.EnvData.SMTP_PASSWORD == "" {
constants.EnvData.SMTP_PASSWORD = os.Getenv("SMTP_PASSWORD")
}
if constants.EnvData.SENDER_EMAIL == "" {
constants.EnvData.SENDER_EMAIL = os.Getenv("SENDER_EMAIL")
}
if constants.EnvData.JWT_SECRET == "" {
constants.EnvData.JWT_SECRET = os.Getenv("JWT_SECRET")
if constants.EnvData.JWT_SECRET == "" {
constants.EnvData.JWT_SECRET = uuid.New().String()
if envData[constants.EnvKeyJwtType] == nil || envData[constants.EnvKeyJwtType].(string) == "" {
envData[constants.EnvKeyJwtType] = os.Getenv("JWT_TYPE")
if envData[constants.EnvKeyJwtType].(string) == "" {
envData[constants.EnvKeyJwtType] = "HS256"
}
}
if constants.EnvData.JWT_TYPE == "" {
constants.EnvData.JWT_TYPE = os.Getenv("JWT_TYPE")
if constants.EnvData.JWT_TYPE == "" {
constants.EnvData.JWT_TYPE = "HS256"
if envData[constants.EnvKeyJwtRoleClaim] == nil || envData[constants.EnvKeyJwtRoleClaim].(string) == "" {
envData[constants.EnvKeyJwtRoleClaim] = os.Getenv("JWT_ROLE_CLAIM")
if envData[constants.EnvKeyJwtRoleClaim].(string) == "" {
envData[constants.EnvKeyJwtRoleClaim] = "role"
}
}
if constants.EnvData.JWT_ROLE_CLAIM == "" {
constants.EnvData.JWT_ROLE_CLAIM = os.Getenv("JWT_ROLE_CLAIM")
if envData[constants.EnvKeyRedisURL] == nil || envData[constants.EnvKeyRedisURL].(string) == "" {
envData[constants.EnvKeyRedisURL] = os.Getenv("REDIS_URL")
}
if constants.EnvData.JWT_ROLE_CLAIM == "" {
constants.EnvData.JWT_ROLE_CLAIM = "role"
if envData[constants.EnvKeyCookieName] == nil || envData[constants.EnvKeyCookieName].(string) == "" {
envData[constants.EnvKeyCookieName] = os.Getenv("COOKIE_NAME")
if envData[constants.EnvKeyCookieName].(string) == "" {
envData[constants.EnvKeyCookieName] = "authorizer"
}
}
if constants.EnvData.AUTHORIZER_URL == "" {
constants.EnvData.AUTHORIZER_URL = strings.TrimSuffix(os.Getenv("AUTHORIZER_URL"), "/")
if ARG_AUTHORIZER_URL != nil && *ARG_AUTHORIZER_URL != "" {
constants.EnvData.AUTHORIZER_URL = *ARG_AUTHORIZER_URL
}
if envData[constants.EnvKeyGoogleClientID] == nil || envData[constants.EnvKeyGoogleClientID].(string) == "" {
envData[constants.EnvKeyGoogleClientID] = os.Getenv("GOOGLE_CLIENT_ID")
}
if constants.EnvData.PORT == "" {
constants.EnvData.PORT = os.Getenv("PORT")
if constants.EnvData.PORT == "" {
constants.EnvData.PORT = "8080"
}
if envData[constants.EnvKeyGoogleClientSecret] == nil || envData[constants.EnvKeyGoogleClientSecret].(string) == "" {
envData[constants.EnvKeyGoogleClientSecret] = os.Getenv("GOOGLE_CLIENT_SECRET")
}
if constants.EnvData.REDIS_URL == "" {
constants.EnvData.REDIS_URL = os.Getenv("REDIS_URL")
if envData[constants.EnvKeyGithubClientID] == nil || envData[constants.EnvKeyGithubClientID].(string) == "" {
envData[constants.EnvKeyGithubClientID] = os.Getenv("GITHUB_CLIENT_ID")
}
if constants.EnvData.COOKIE_NAME == "" {
constants.EnvData.COOKIE_NAME = os.Getenv("COOKIE_NAME")
if constants.EnvData.COOKIE_NAME == "" {
constants.EnvData.COOKIE_NAME = "authorizer"
}
if envData[constants.EnvKeyGithubClientSecret] == nil || envData[constants.EnvKeyGithubClientSecret].(string) == "" {
envData[constants.EnvKeyGithubClientSecret] = os.Getenv("GITHUB_CLIENT_SECRET")
}
if constants.EnvData.GOOGLE_CLIENT_ID == "" {
constants.EnvData.GOOGLE_CLIENT_ID = os.Getenv("GOOGLE_CLIENT_ID")
if envData[constants.EnvKeyFacebookClientID] == nil || envData[constants.EnvKeyFacebookClientID].(string) == "" {
envData[constants.EnvKeyFacebookClientID] = os.Getenv("FACEBOOK_CLIENT_ID")
}
if constants.EnvData.GOOGLE_CLIENT_SECRET == "" {
constants.EnvData.GOOGLE_CLIENT_SECRET = os.Getenv("GOOGLE_CLIENT_SECRET")
if envData[constants.EnvKeyFacebookClientSecret] == nil || envData[constants.EnvKeyFacebookClientSecret].(string) == "" {
envData[constants.EnvKeyFacebookClientSecret] = os.Getenv("FACEBOOK_CLIENT_SECRET")
}
if constants.EnvData.GITHUB_CLIENT_ID == "" {
constants.EnvData.GITHUB_CLIENT_ID = os.Getenv("GITHUB_CLIENT_ID")
if envData[constants.EnvKeyResetPasswordURL] == nil || envData[constants.EnvKeyResetPasswordURL].(string) == "" {
envData[constants.EnvKeyResetPasswordURL] = strings.TrimPrefix(os.Getenv("RESET_PASSWORD_URL"), "/")
}
if constants.EnvData.GITHUB_CLIENT_SECRET == "" {
constants.EnvData.GITHUB_CLIENT_SECRET = os.Getenv("GITHUB_CLIENT_SECRET")
envData[constants.EnvKeyDisableBasicAuthentication] = os.Getenv("DISABLE_BASIC_AUTHENTICATION") == "true"
envData[constants.EnvKeyDisableEmailVerification] = os.Getenv("DISABLE_EMAIL_VERIFICATION") == "true"
envData[constants.EnvKeyDisableMagicLinkLogin] = os.Getenv("DISABLE_MAGIC_LINK_LOGIN") == "true"
envData[constants.EnvKeyDisableLoginPage] = os.Getenv("DISABLE_LOGIN_PAGE") == "true"
// no need to add nil check as its already done above
if envData[constants.EnvKeySmtpHost].(string) == "" || envData[constants.EnvKeySmtpUsername].(string) == "" || envData[constants.EnvKeySmtpPassword].(string) == "" || envData[constants.EnvKeySenderEmail].(string) == "" {
envData[constants.EnvKeyDisableEmailVerification] = true
envData[constants.EnvKeyDisableMagicLinkLogin] = true
}
if constants.EnvData.FACEBOOK_CLIENT_ID == "" {
constants.EnvData.FACEBOOK_CLIENT_ID = os.Getenv("FACEBOOK_CLIENT_ID")
}
if constants.EnvData.FACEBOOK_CLIENT_SECRET == "" {
constants.EnvData.FACEBOOK_CLIENT_SECRET = os.Getenv("FACEBOOK_CLIENT_SECRET")
}
if constants.EnvData.RESET_PASSWORD_URL == "" {
constants.EnvData.RESET_PASSWORD_URL = strings.TrimPrefix(os.Getenv("RESET_PASSWORD_URL"), "/")
}
constants.EnvData.DISABLE_BASIC_AUTHENTICATION = os.Getenv("DISABLE_BASIC_AUTHENTICATION") == "true"
constants.EnvData.DISABLE_EMAIL_VERIFICATION = os.Getenv("DISABLE_EMAIL_VERIFICATION") == "true"
constants.EnvData.DISABLE_MAGIC_LINK_LOGIN = os.Getenv("DISABLE_MAGIC_LINK_LOGIN") == "true"
constants.EnvData.DISABLE_LOGIN_PAGE = os.Getenv("DISABLE_LOGIN_PAGE") == "true"
if constants.EnvData.SMTP_HOST == "" || constants.EnvData.SMTP_USERNAME == "" || constants.EnvData.SMTP_PASSWORD == "" || constants.EnvData.SENDER_EMAIL == "" {
constants.EnvData.DISABLE_EMAIL_VERIFICATION = true
constants.EnvData.DISABLE_MAGIC_LINK_LOGIN = true
if envData[constants.EnvKeyDisableEmailVerification].(bool) {
envData[constants.EnvKeyDisableMagicLinkLogin] = true
}
allowedOriginsSplit := strings.Split(os.Getenv("ALLOWED_ORIGINS"), ",")
@ -216,11 +225,7 @@ func InitEnv() {
allowedOrigins = []string{"*"}
}
constants.EnvData.ALLOWED_ORIGINS = allowedOrigins
if constants.EnvData.DISABLE_EMAIL_VERIFICATION {
constants.EnvData.DISABLE_MAGIC_LINK_LOGIN = true
}
envData[constants.EnvKeyAllowedOrigins] = allowedOrigins
rolesEnv := strings.TrimSpace(os.Getenv("ROLES"))
rolesSplit := strings.Split(rolesEnv, ",")
@ -263,15 +268,17 @@ func InitEnv() {
panic(`Invalid DEFAULT_ROLE environment variable. It can be one from give ROLES environment variable value`)
}
constants.EnvData.ROLES = roles
constants.EnvData.DEFAULT_ROLES = defaultRoles
constants.EnvData.PROTECTED_ROLES = protectedRoles
envData[constants.EnvKeyRoles] = roles
envData[constants.EnvKeyDefaultRoles] = defaultRoles
envData[constants.EnvKeyProtectedRoles] = protectedRoles
if os.Getenv("ORGANIZATION_NAME") != "" {
constants.EnvData.ORGANIZATION_NAME = os.Getenv("ORGANIZATION_NAME")
envData[constants.EnvKeyOrganizationName] = os.Getenv("ORGANIZATION_NAME")
}
if os.Getenv("ORGANIZATION_LOGO") != "" {
constants.EnvData.ORGANIZATION_LOGO = os.Getenv("ORGANIZATION_LOGO")
envData[constants.EnvKeyOrganizationLogo] = os.Getenv("ORGANIZATION_LOGO")
}
envstore.EnvInMemoryStoreObj.UpdateEnvStore(envData)
}

View File

@ -9,20 +9,22 @@ import (
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/google/uuid"
)
// PersistEnv persists the environment variables to the database
func PersistEnv() error {
config, err := db.Mgr.GetConfig()
// config not found in db
if err != nil {
// AES encryption needs 32 bit key only, so we chop off last 4 characters from 36 bit uuid
hash := uuid.New().String()[:36-4]
constants.EnvData.ENCRYPTION_KEY = hash
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.EnvKeyEncryptionKey, hash)
encodedHash := utils.EncryptB64(hash)
configData, err := json.Marshal(constants.EnvData)
configData, err := json.Marshal(envstore.EnvInMemoryStoreObj.GetEnvStoreClone())
if err != nil {
return err
}
@ -45,7 +47,8 @@ func PersistEnv() error {
if err != nil {
return err
}
constants.EnvData.ENCRYPTION_KEY = decryptedEncryptionKey
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.EnvKeyEncryptionKey, decryptedEncryptionKey)
decryptedConfigs, err := utils.DecryptAES(config.Config)
if err != nil {
return err
@ -119,7 +122,7 @@ func PersistEnv() error {
}
if hasChanged {
encryptedConfig, err := utils.EncryptConfig(jsonData)
encryptedConfig, err := utils.EncryptEnvData(jsonData)
if err != nil {
return err
}

65
server/envstore/store.go Normal file
View File

@ -0,0 +1,65 @@
package envstore
import (
"sync"
"github.com/authorizerdev/authorizer/server/constants"
)
// EnvInMemoryStore struct
type EnvInMemoryStore struct {
mutex sync.Mutex
store map[string]interface{}
}
// EnvInMemoryStoreObj global variable for EnvInMemoryStore
var EnvInMemoryStoreObj = &EnvInMemoryStore{
store: map[string]interface{}{
constants.EnvKeyAdminCookieName: "authorizer-admin",
constants.EnvKeyJwtRoleClaim: "role",
constants.EnvKeyOrganizationName: "Authorizer",
constants.EnvKeyOrganizationLogo: "https://www.authorizer.io/images/logo.png",
constants.EnvKeyDisableBasicAuthentication: false,
constants.EnvKeyDisableMagicLinkLogin: false,
constants.EnvKeyDisableEmailVerification: false,
constants.EnvKeyDisableLoginPage: false,
},
}
// UpdateEnvStore to update the whole env store object
func (e *EnvInMemoryStore) UpdateEnvStore(data map[string]interface{}) {
e.mutex.Lock()
defer e.mutex.Unlock()
// just override the keys + new keys
for key, value := range data {
e.store[key] = value
}
}
// UpdateEnvVariable to update the particular env variable
func (e *EnvInMemoryStore) UpdateEnvVariable(key string, value interface{}) map[string]interface{} {
e.mutex.Lock()
defer e.mutex.Unlock()
e.store[key] = value
return e.store
}
// GetEnvStore to get the env variable from env store object
func (e *EnvInMemoryStore) GetEnvVariable(key string) interface{} {
// e.mutex.Lock()
// defer e.mutex.Unlock()
return e.store[key]
}
// GetEnvStoreClone to get clone of current env store object
func (e *EnvInMemoryStore) GetEnvStoreClone() map[string]interface{} {
e.mutex.Lock()
defer e.mutex.Unlock()
result := make(map[string]interface{})
for key, value := range e.store {
result[key] = value
}
return result
}

View File

@ -1182,6 +1182,7 @@ input UpdateProfileInput {
input UpdateUserInput {
id: ID!
email: String
email_verified: Boolean
given_name: String
family_name: String
middle_name: String
@ -6646,6 +6647,14 @@ func (ec *executionContext) unmarshalInputUpdateUserInput(ctx context.Context, o
if err != nil {
return it, err
}
case "email_verified":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email_verified"))
it.EmailVerified, err = ec.unmarshalOBoolean2ᚖbool(ctx, v)
if err != nil {
return it, err
}
case "given_name":
var err error

View File

@ -168,17 +168,18 @@ type UpdateProfileInput struct {
}
type UpdateUserInput struct {
ID string `json:"id"`
Email *string `json:"email"`
GivenName *string `json:"given_name"`
FamilyName *string `json:"family_name"`
MiddleName *string `json:"middle_name"`
Nickname *string `json:"nickname"`
Gender *string `json:"gender"`
Birthdate *string `json:"birthdate"`
PhoneNumber *string `json:"phone_number"`
Picture *string `json:"picture"`
Roles []*string `json:"roles"`
ID string `json:"id"`
Email *string `json:"email"`
EmailVerified *bool `json:"email_verified"`
GivenName *string `json:"given_name"`
FamilyName *string `json:"family_name"`
MiddleName *string `json:"middle_name"`
Nickname *string `json:"nickname"`
Gender *string `json:"gender"`
Birthdate *string `json:"birthdate"`
PhoneNumber *string `json:"phone_number"`
Picture *string `json:"picture"`
Roles []*string `json:"roles"`
}
type User struct {

View File

@ -189,6 +189,7 @@ input UpdateProfileInput {
input UpdateUserInput {
id: ID!
email: String
email_verified: Boolean
given_name: String
family_name: String
middle_name: String

View File

@ -12,47 +12,47 @@ import (
)
func (r *mutationResolver) Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse, error) {
return resolvers.Signup(ctx, params)
return resolvers.SignupResolver(ctx, params)
}
func (r *mutationResolver) Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, error) {
return resolvers.Login(ctx, params)
return resolvers.LoginResolver(ctx, params)
}
func (r *mutationResolver) MagicLinkLogin(ctx context.Context, params model.MagicLinkLoginInput) (*model.Response, error) {
return resolvers.MagicLinkLogin(ctx, params)
return resolvers.MagicLinkLoginResolver(ctx, params)
}
func (r *mutationResolver) Logout(ctx context.Context) (*model.Response, error) {
return resolvers.Logout(ctx)
return resolvers.LogoutResolver(ctx)
}
func (r *mutationResolver) UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model.Response, error) {
return resolvers.UpdateProfile(ctx, params)
return resolvers.UpdateProfileResolver(ctx, params)
}
func (r *mutationResolver) VerifyEmail(ctx context.Context, params model.VerifyEmailInput) (*model.AuthResponse, error) {
return resolvers.VerifyEmail(ctx, params)
return resolvers.VerifyEmailResolver(ctx, params)
}
func (r *mutationResolver) ResendVerifyEmail(ctx context.Context, params model.ResendVerifyEmailInput) (*model.Response, error) {
return resolvers.ResendVerifyEmail(ctx, params)
return resolvers.ResendVerifyEmailResolver(ctx, params)
}
func (r *mutationResolver) ForgotPassword(ctx context.Context, params model.ForgotPasswordInput) (*model.Response, error) {
return resolvers.ForgotPassword(ctx, params)
return resolvers.ForgotPasswordResolver(ctx, params)
}
func (r *mutationResolver) ResetPassword(ctx context.Context, params model.ResetPasswordInput) (*model.Response, error) {
return resolvers.ResetPassword(ctx, params)
return resolvers.ResetPasswordResolver(ctx, params)
}
func (r *mutationResolver) DeleteUser(ctx context.Context, params model.DeleteUserInput) (*model.Response, error) {
return resolvers.DeleteUser(ctx, params)
return resolvers.DeleteUserResolver(ctx, params)
}
func (r *mutationResolver) UpdateUser(ctx context.Context, params model.UpdateUserInput) (*model.User, error) {
return resolvers.UpdateUser(ctx, params)
return resolvers.UpdateUserResolver(ctx, params)
}
func (r *mutationResolver) AdminSignup(ctx context.Context, params model.AdminSignupInput) (*model.Response, error) {
@ -64,7 +64,7 @@ func (r *mutationResolver) AdminLogin(ctx context.Context, params model.AdminLog
}
func (r *mutationResolver) AdminLogout(ctx context.Context) (*model.Response, error) {
return resolvers.AdminLogout(ctx)
return resolvers.AdminLogoutResolver(ctx)
}
func (r *mutationResolver) UpdateConfig(ctx context.Context, params model.UpdateConfigInput) (*model.Response, error) {
@ -72,27 +72,27 @@ func (r *mutationResolver) UpdateConfig(ctx context.Context, params model.Update
}
func (r *queryResolver) Meta(ctx context.Context) (*model.Meta, error) {
return resolvers.Meta(ctx)
return resolvers.MetaResolver(ctx)
}
func (r *queryResolver) Session(ctx context.Context, roles []string) (*model.AuthResponse, error) {
return resolvers.Session(ctx, roles)
return resolvers.SessionResolver(ctx, roles)
}
func (r *queryResolver) Profile(ctx context.Context) (*model.User, error) {
return resolvers.Profile(ctx)
return resolvers.ProfileResolver(ctx)
}
func (r *queryResolver) Users(ctx context.Context) ([]*model.User, error) {
return resolvers.Users(ctx)
return resolvers.UsersResolver(ctx)
}
func (r *queryResolver) VerificationRequests(ctx context.Context) ([]*model.VerificationRequest, error) {
return resolvers.VerificationRequests(ctx)
return resolvers.VerificationRequestsResolver(ctx)
}
func (r *queryResolver) AdminSession(ctx context.Context) (*model.Response, error) {
return resolvers.AdminSession(ctx)
return resolvers.AdminSessionResolver(ctx)
}
func (r *queryResolver) Config(ctx context.Context) (*model.Config, error) {
@ -105,5 +105,7 @@ func (r *Resolver) Mutation() generated.MutationResolver { return &mutationResol
// Query returns generated.QueryResolver implementation.
func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }
type mutationResolver struct{ *Resolver }
type queryResolver struct{ *Resolver }
type (
mutationResolver struct{ *Resolver }
queryResolver struct{ *Resolver }
)

View File

@ -7,15 +7,19 @@ import (
"strings"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/gin-gonic/gin"
)
// State is the struct that holds authorizer url and redirect url
// They are provided via query string in the request
type State struct {
AuthorizerURL string `json:"authorizerURL"`
RedirectURL string `json:"redirectURL"`
}
// AppHandler is the handler for the /app route
func AppHandler() gin.HandlerFunc {
return func(c *gin.Context) {
state := c.Query("state")
@ -23,14 +27,8 @@ func AppHandler() gin.HandlerFunc {
var stateObj State
if state == "" {
// cookie, err := utils.GetAuthToken(c)
// if err != nil {
// c.JSON(400, gin.H{"error": "invalid state"})
// return
// }
stateObj.AuthorizerURL = constants.EnvData.AUTHORIZER_URL
stateObj.RedirectURL = constants.EnvData.AUTHORIZER_URL + "/app"
stateObj.AuthorizerURL = envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAuthorizerURL).(string)
stateObj.RedirectURL = stateObj.AuthorizerURL + "/app"
} else {
decodedState, err := utils.DecryptB64(state)
@ -59,7 +57,7 @@ func AppHandler() gin.HandlerFunc {
}
// validate host and domain of authorizer url
if strings.TrimSuffix(stateObj.AuthorizerURL, "/") != constants.EnvData.AUTHORIZER_URL {
if strings.TrimSuffix(stateObj.AuthorizerURL, "/") != envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAuthorizerURL).(string) {
c.JSON(400, gin.H{"error": "invalid host url"})
return
}
@ -76,8 +74,8 @@ func AppHandler() gin.HandlerFunc {
"data": map[string]string{
"authorizerURL": stateObj.AuthorizerURL,
"redirectURL": stateObj.RedirectURL,
"organizationName": constants.EnvData.ORGANIZATION_NAME,
"organizationLogo": constants.EnvData.ORGANIZATION_LOGO,
"organizationName": envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyOrganizationName).(string),
"organizationLogo": envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyOrganizationLogo).(string),
},
})
}

View File

@ -4,14 +4,16 @@ import (
"net/http"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/gin-gonic/gin"
)
// DashboardHandler is the handler for the /dashboard route
func DashboardHandler() gin.HandlerFunc {
return func(c *gin.Context) {
isOnboardingCompleted := false
if constants.EnvData.ADMIN_SECRET != "" {
if envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAdminSecret) != nil && envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAdminSecret).(string) != "" {
isOnboardingCompleted = true
}

View File

@ -7,7 +7,7 @@ import (
"github.com/gin-gonic/gin"
)
// Defining the Graphql handler
// GraphqlHandler is the main handler that handels all the graphql requests
func GraphqlHandler() gin.HandlerFunc {
// NewExecutableSchema and Config are in the generated.go file
// Resolver is in the resolver.go file

View File

@ -12,7 +12,7 @@ import (
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/oauth"
"github.com/authorizerdev/authorizer/server/session"
"github.com/authorizerdev/authorizer/server/utils"
@ -21,6 +21,131 @@ import (
"golang.org/x/oauth2"
)
// OAuthCallbackHandler handles the OAuth callback for various oauth providers
func OAuthCallbackHandler() gin.HandlerFunc {
return func(c *gin.Context) {
provider := c.Param("oauth_provider")
state := c.Request.FormValue("state")
sessionState := session.GetSocailLoginState(state)
if sessionState == "" {
c.JSON(400, gin.H{"error": "invalid oauth state"})
}
session.RemoveSocialLoginState(state)
// contains random token, redirect url, role
sessionSplit := strings.Split(state, "___")
// TODO validate redirect url
if len(sessionSplit) < 2 {
c.JSON(400, gin.H{"error": "invalid redirect url"})
return
}
inputRoles := strings.Split(sessionSplit[2], ",")
redirectURL := sessionSplit[1]
var err error
user := db.User{}
code := c.Request.FormValue("code")
switch provider {
case constants.SignupMethodGoogle:
user, err = processGoogleUserInfo(code)
case constants.SignupMethodGithub:
user, err = processGithubUserInfo(code)
case constants.SignupMethodFacebook:
user, err = processFacebookUserInfo(code)
default:
err = fmt.Errorf(`invalid oauth provider`)
}
if err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
existingUser, err := db.Mgr.GetUserByEmail(user.Email)
if err != nil {
// user not registered, register user and generate session token
user.SignupMethods = provider
// make sure inputRoles don't include protected roles
hasProtectedRole := false
for _, ir := range inputRoles {
if utils.StringSliceContains(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyProtectedRoles).([]string), ir) {
hasProtectedRole = true
}
}
if hasProtectedRole {
c.JSON(400, gin.H{"error": "invalid role"})
return
}
user.Roles = strings.Join(inputRoles, ",")
now := time.Now().Unix()
user.EmailVerifiedAt = &now
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
signupMethod := existingUser.SignupMethods
if !strings.Contains(signupMethod, provider) {
signupMethod = signupMethod + "," + provider
}
user.SignupMethods = signupMethod
user.Password = existingUser.Password
// There multiple scenarios with roles here in social login
// 1. user has access to protected roles + roles and trying to login
// 2. user has not signed up for one of the available role but trying to signup.
// Need to modify roles in this case
// find the unassigned roles
existingRoles := strings.Split(existingUser.Roles, ",")
unasignedRoles := []string{}
for _, ir := range inputRoles {
if !utils.StringSliceContains(existingRoles, ir) {
unasignedRoles = append(unasignedRoles, ir)
}
}
if len(unasignedRoles) > 0 {
// check if it contains protected unassigned role
hasProtectedRole := false
for _, ur := range unasignedRoles {
if utils.StringSliceContains(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyProtectedRoles).([]string), ur) {
hasProtectedRole = true
}
}
if hasProtectedRole {
c.JSON(400, gin.H{"error": "invalid role"})
return
} else {
user.Roles = existingUser.Roles + "," + strings.Join(unasignedRoles, ",")
}
} else {
user.Roles = existingUser.Roles
}
user.Key = existingUser.Key
user.ID = existingUser.ID
user, err = db.Mgr.UpdateUser(user)
}
user, _ = db.Mgr.GetUserByEmail(user.Email)
userIdStr := fmt.Sprintf("%v", user.ID)
refreshToken, _, _ := utils.CreateAuthToken(user, constants.TokenTypeRefreshToken, inputRoles)
accessToken, _, _ := utils.CreateAuthToken(user, constants.TokenTypeAccessToken, inputRoles)
utils.SetCookie(c, accessToken)
session.SetUserSession(userIdStr, accessToken, refreshToken)
utils.SaveSessionInDB(user.ID, c)
c.Redirect(http.StatusTemporaryRedirect, redirectURL)
}
}
func processGoogleUserInfo(code string) (db.User, error) {
user := db.User{}
ctx := context.Background()
@ -145,127 +270,3 @@ func processFacebookUserInfo(code string) (db.User, error) {
return user, nil
}
func OAuthCallbackHandler() gin.HandlerFunc {
return func(c *gin.Context) {
provider := c.Param("oauth_provider")
state := c.Request.FormValue("state")
sessionState := session.GetSocailLoginState(state)
if sessionState == "" {
c.JSON(400, gin.H{"error": "invalid oauth state"})
}
session.RemoveSocialLoginState(state)
// contains random token, redirect url, role
sessionSplit := strings.Split(state, "___")
// TODO validate redirect url
if len(sessionSplit) < 2 {
c.JSON(400, gin.H{"error": "invalid redirect url"})
return
}
inputRoles := strings.Split(sessionSplit[2], ",")
redirectURL := sessionSplit[1]
var err error
user := db.User{}
code := c.Request.FormValue("code")
switch provider {
case enum.Google.String():
user, err = processGoogleUserInfo(code)
case enum.Github.String():
user, err = processGithubUserInfo(code)
case enum.Facebook.String():
user, err = processFacebookUserInfo(code)
default:
err = fmt.Errorf(`invalid oauth provider`)
}
if err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
existingUser, err := db.Mgr.GetUserByEmail(user.Email)
if err != nil {
// user not registered, register user and generate session token
user.SignupMethods = provider
// make sure inputRoles don't include protected roles
hasProtectedRole := false
for _, ir := range inputRoles {
if utils.StringSliceContains(constants.EnvData.PROTECTED_ROLES, ir) {
hasProtectedRole = true
}
}
if hasProtectedRole {
c.JSON(400, gin.H{"error": "invalid role"})
return
}
user.Roles = strings.Join(inputRoles, ",")
now := time.Now().Unix()
user.EmailVerifiedAt = &now
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
signupMethod := existingUser.SignupMethods
if !strings.Contains(signupMethod, provider) {
signupMethod = signupMethod + "," + provider
}
user.SignupMethods = signupMethod
user.Password = existingUser.Password
// There multiple scenarios with roles here in social login
// 1. user has access to protected roles + roles and trying to login
// 2. user has not signed up for one of the available role but trying to signup.
// Need to modify roles in this case
// find the unassigned roles
existingRoles := strings.Split(existingUser.Roles, ",")
unasignedRoles := []string{}
for _, ir := range inputRoles {
if !utils.StringSliceContains(existingRoles, ir) {
unasignedRoles = append(unasignedRoles, ir)
}
}
if len(unasignedRoles) > 0 {
// check if it contains protected unassigned role
hasProtectedRole := false
for _, ur := range unasignedRoles {
if utils.StringSliceContains(constants.EnvData.PROTECTED_ROLES, ur) {
hasProtectedRole = true
}
}
if hasProtectedRole {
c.JSON(400, gin.H{"error": "invalid role"})
return
} else {
user.Roles = existingUser.Roles + "," + strings.Join(unasignedRoles, ",")
}
} else {
user.Roles = existingUser.Roles
}
user.Key = existingUser.Key
user.ID = existingUser.ID
user, err = db.Mgr.UpdateUser(user)
}
user, _ = db.Mgr.GetUserByEmail(user.Email)
userIdStr := fmt.Sprintf("%v", user.ID)
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, inputRoles)
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, inputRoles)
utils.SetCookie(c, accessToken)
session.SetToken(userIdStr, accessToken, refreshToken)
utils.CreateSession(user.ID, c)
c.Redirect(http.StatusTemporaryRedirect, redirectURL)
}
}

View File

@ -5,7 +5,7 @@ import (
"strings"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/oauth"
"github.com/authorizerdev/authorizer/server/session"
"github.com/authorizerdev/authorizer/server/utils"
@ -13,8 +13,7 @@ import (
"github.com/google/uuid"
)
// set host in the oauth state that is useful for redirecting
// OAuthLoginHandler set host in the oauth state that is useful for redirecting to oauth_callback
func OAuthLoginHandler() gin.HandlerFunc {
return func(c *gin.Context) {
// TODO validate redirect URL
@ -34,14 +33,14 @@ func OAuthLoginHandler() gin.HandlerFunc {
// 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.EnvData.ROLES, constants.EnvData.PROTECTED_ROLES...)...), rolesSplit) {
if !utils.IsValidRoles(append([]string{}, append(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyRoles).([]string), envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyProtectedRoles).([]string)...)...), rolesSplit) {
c.JSON(400, gin.H{
"error": "invalid role",
})
return
}
} else {
roles = strings.Join(constants.EnvData.DEFAULT_ROLES, ",")
roles = strings.Join(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDefaultRoles).([]string), ",")
}
uuid := uuid.New()
@ -50,32 +49,32 @@ func OAuthLoginHandler() gin.HandlerFunc {
provider := c.Param("oauth_provider")
isProviderConfigured := true
switch provider {
case enum.Google.String():
case constants.SignupMethodGoogle:
if oauth.OAuthProviders.GoogleConfig == nil {
isProviderConfigured = false
break
}
session.SetSocailLoginState(oauthStateString, enum.Google.String())
session.SetSocailLoginState(oauthStateString, constants.SignupMethodGoogle)
// during the init of OAuthProvider authorizer url might be empty
oauth.OAuthProviders.GoogleConfig.RedirectURL = constants.EnvData.AUTHORIZER_URL + "/oauth_callback/google"
oauth.OAuthProviders.GoogleConfig.RedirectURL = envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAuthorizerURL).(string) + "/oauth_callback/google"
url := oauth.OAuthProviders.GoogleConfig.AuthCodeURL(oauthStateString)
c.Redirect(http.StatusTemporaryRedirect, url)
case enum.Github.String():
case constants.SignupMethodGithub:
if oauth.OAuthProviders.GithubConfig == nil {
isProviderConfigured = false
break
}
session.SetSocailLoginState(oauthStateString, enum.Github.String())
oauth.OAuthProviders.GithubConfig.RedirectURL = constants.EnvData.AUTHORIZER_URL + "/oauth_callback/github"
session.SetSocailLoginState(oauthStateString, constants.SignupMethodGithub)
oauth.OAuthProviders.GithubConfig.RedirectURL = envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAuthorizerURL).(string) + "/oauth_callback/github"
url := oauth.OAuthProviders.GithubConfig.AuthCodeURL(oauthStateString)
c.Redirect(http.StatusTemporaryRedirect, url)
case enum.Facebook.String():
case constants.SignupMethodFacebook:
if oauth.OAuthProviders.FacebookConfig == nil {
isProviderConfigured = false
break
}
session.SetSocailLoginState(oauthStateString, enum.Facebook.String())
oauth.OAuthProviders.FacebookConfig.RedirectURL = constants.EnvData.AUTHORIZER_URL + "/oauth_callback/facebook"
session.SetSocailLoginState(oauthStateString, constants.SignupMethodFacebook)
oauth.OAuthProviders.FacebookConfig.RedirectURL = envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAuthorizerURL).(string) + "/oauth_callback/facebook"
url := oauth.OAuthProviders.FacebookConfig.AuthCodeURL(oauthStateString)
c.Redirect(http.StatusTemporaryRedirect, url)
default:

View File

@ -5,6 +5,7 @@ import (
"github.com/gin-gonic/gin"
)
// PlaygroundHandler is the handler for the /playground route
func PlaygroundHandler() gin.HandlerFunc {
h := playground.Handler("GraphQL", "/graphql")

14
server/handlers/root.go Normal file
View File

@ -0,0 +1,14 @@
package handlers
import (
"net/http"
"github.com/gin-gonic/gin"
)
// RootHandler is the handler for / root route.
func RootHandler() gin.HandlerFunc {
return func(c *gin.Context) {
c.Redirect(http.StatusTemporaryRedirect, "/dashboard")
}
}

View File

@ -5,13 +5,15 @@ 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/session"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/gin-gonic/gin"
)
// VerifyEmailHandler handles the verify email route.
// It verifies email based on JWT token in query string
func VerifyEmailHandler() gin.HandlerFunc {
return func(c *gin.Context) {
errorRes := gin.H{
@ -54,12 +56,12 @@ func VerifyEmailHandler() gin.HandlerFunc {
db.Mgr.DeleteVerificationRequest(verificationRequest)
roles := strings.Split(user.Roles, ",")
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, roles)
refreshToken, _, _ := utils.CreateAuthToken(user, constants.TokenTypeRefreshToken, roles)
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, roles)
accessToken, _, _ := utils.CreateAuthToken(user, constants.TokenTypeAccessToken, roles)
session.SetToken(user.ID, accessToken, refreshToken)
utils.CreateSession(user.ID, c)
session.SetUserSession(user.ID, accessToken, refreshToken)
utils.SaveSessionInDB(user.ID, c)
utils.SetCookie(c, accessToken)
c.Redirect(http.StatusTemporaryRedirect, claim.RedirectURL)
}

View File

@ -6,11 +6,10 @@ import (
"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/envstore"
"github.com/authorizerdev/authorizer/server/oauth"
"github.com/authorizerdev/authorizer/server/router"
"github.com/authorizerdev/authorizer/server/routes"
"github.com/authorizerdev/authorizer/server/session"
"github.com/authorizerdev/authorizer/server/utils"
)
var VERSION string
@ -18,11 +17,10 @@ var VERSION string
func main() {
env.ARG_DB_URL = flag.String("database_url", "", "Database connection string")
env.ARG_DB_TYPE = flag.String("database_type", "", "Database type, possible values are postgres,mysql,sqlite")
env.ARG_AUTHORIZER_URL = flag.String("authorizer_url", "", "URL for authorizer instance, eg: https://xyz.herokuapp.com")
env.ARG_ENV_FILE = flag.String("env_file", "", "Env file path")
flag.Parse()
constants.EnvData.VERSION = VERSION
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.EnvKeyVersion, VERSION)
env.InitEnv()
db.InitDB()
@ -30,27 +28,8 @@ func main() {
session.InitSession()
oauth.InitOAuth()
utils.InitServer()
router := router.InitRouter()
router := routes.InitRouter()
router.LoadHTMLGlob("templates/*")
// login page app related routes.
// if we put them in router file then tests would fail as templates or build path will be different
if !constants.EnvData.DISABLE_LOGIN_PAGE {
app := router.Group("/app")
{
app.Static("/build", "app/build")
app.GET("/", handlers.AppHandler())
app.GET("/reset-password", handlers.AppHandler())
}
}
app := router.Group("/dashboard")
{
app.Static("/build", "dashboard/build")
app.GET("/", handlers.DashboardHandler())
}
router.Run(":" + constants.EnvData.PORT)
router.Run(":" + envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyPort).(string))
}

View File

@ -2,19 +2,19 @@ package middlewares
import (
"context"
"log"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/gin-contrib/location"
"github.com/gin-gonic/gin"
)
// GinContextToContextMiddleware is a middleware to add gin context in context
func GinContextToContextMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
if constants.EnvData.AUTHORIZER_URL == "" {
if envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAuthorizerURL).(string) == "" {
url := location.Get(c)
constants.EnvData.AUTHORIZER_URL = url.Scheme + "://" + c.Request.Host
log.Println("authorizer url:", constants.EnvData.AUTHORIZER_URL)
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.EnvKeyAuthorizerURL, url.Scheme+"://"+c.Request.Host)
}
ctx := context.WithValue(c.Request.Context(), "GinContextKey", c)
c.Request = c.Request.WithContext(ctx)

View File

@ -5,6 +5,7 @@ import (
"github.com/gin-gonic/gin"
)
// CORSMiddleware is a middleware to add cors headers
func CORSMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
origin := c.Request.Header.Get("Origin")

View File

@ -5,56 +5,62 @@ import (
"log"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/coreos/go-oidc/v3/oidc"
"golang.org/x/oauth2"
facebookOAuth2 "golang.org/x/oauth2/facebook"
githubOAuth2 "golang.org/x/oauth2/github"
)
// OAuthProviders is a struct that contains reference all the OAuth providers
type OAuthProvider struct {
GoogleConfig *oauth2.Config
GithubConfig *oauth2.Config
FacebookConfig *oauth2.Config
}
// OIDCProviders is a struct that contains reference all the OpenID providers
type OIDCProvider struct {
GoogleOIDC *oidc.Provider
}
var (
// OAuthProviders is a global variable that contains instance for all enabled the OAuth providers
OAuthProviders OAuthProvider
OIDCProviders OIDCProvider
// OIDCProviders is a global variable that contains instance for all enabled the OpenID providers
OIDCProviders OIDCProvider
)
// InitOAuth initializes the OAuth providers based on EnvData
func InitOAuth() {
ctx := context.Background()
if constants.EnvData.GOOGLE_CLIENT_ID != "" && constants.EnvData.GOOGLE_CLIENT_SECRET != "" {
if envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyGoogleClientID).(string) != "" && envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyGoogleClientSecret).(string) != "" {
p, err := oidc.NewProvider(ctx, "https://accounts.google.com")
if err != nil {
log.Fatalln("error creating oidc provider for google:", err)
}
OIDCProviders.GoogleOIDC = p
OAuthProviders.GoogleConfig = &oauth2.Config{
ClientID: constants.EnvData.GOOGLE_CLIENT_ID,
ClientSecret: constants.EnvData.GOOGLE_CLIENT_SECRET,
RedirectURL: constants.EnvData.AUTHORIZER_URL + "/oauth_callback/google",
ClientID: envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyGoogleClientID).(string),
ClientSecret: envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyGoogleClientSecret).(string),
RedirectURL: envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAuthorizerURL).(string) + "/oauth_callback/google",
Endpoint: OIDCProviders.GoogleOIDC.Endpoint(),
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
}
}
if constants.EnvData.GITHUB_CLIENT_ID != "" && constants.EnvData.GITHUB_CLIENT_SECRET != "" {
if envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyGithubClientID).(string) != "" && envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyGithubClientSecret).(string) != "" {
OAuthProviders.GithubConfig = &oauth2.Config{
ClientID: constants.EnvData.GITHUB_CLIENT_ID,
ClientSecret: constants.EnvData.GITHUB_CLIENT_SECRET,
RedirectURL: constants.EnvData.AUTHORIZER_URL + "/oauth_callback/github",
ClientID: envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyGithubClientID).(string),
ClientSecret: envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyGithubClientSecret).(string),
RedirectURL: envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAuthorizerURL).(string) + "/oauth_callback/github",
Endpoint: githubOAuth2.Endpoint,
}
}
if constants.EnvData.FACEBOOK_CLIENT_ID != "" && constants.EnvData.FACEBOOK_CLIENT_SECRET != "" {
if envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyFacebookClientID).(string) != "" && envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyGoogleClientID).(string) != "" {
OAuthProviders.FacebookConfig = &oauth2.Config{
ClientID: constants.EnvData.FACEBOOK_CLIENT_ID,
ClientSecret: constants.EnvData.FACEBOOK_CLIENT_SECRET,
RedirectURL: constants.EnvData.AUTHORIZER_URL + "/oauth_callback/facebook",
ClientID: envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyFacebookClientID).(string),
ClientSecret: envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyFacebookClientSecret).(string),
RedirectURL: envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAuthorizerURL).(string) + "/oauth_callback/facebook",
Endpoint: facebookOAuth2.Endpoint,
Scopes: []string{"public_profile", "email"},
}

View File

@ -5,10 +5,12 @@ import (
"fmt"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/utils"
)
// AdminLoginResolver is a resolver for admin login mutation
func AdminLoginResolver(ctx context.Context, params model.AdminLoginInput) (*model.Response, error) {
gc, err := utils.GinContextFromContext(ctx)
var res *model.Response
@ -17,11 +19,12 @@ func AdminLoginResolver(ctx context.Context, params model.AdminLoginInput) (*mod
return res, err
}
if params.AdminSecret != constants.EnvData.ADMIN_SECRET {
adminSecret := envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAdminSecret).(string)
if params.AdminSecret != adminSecret {
return res, fmt.Errorf(`invalid admin secret`)
}
hashedKey, err := utils.HashPassword(constants.EnvData.ADMIN_SECRET)
hashedKey, err := utils.EncryptPassword(adminSecret)
if err != nil {
return res, err
}

View File

@ -8,7 +8,8 @@ import (
"github.com/authorizerdev/authorizer/server/utils"
)
func AdminLogout(ctx context.Context) (*model.Response, error) {
// AdminLogoutResolver is a resolver for admin logout mutation
func AdminLogoutResolver(ctx context.Context) (*model.Response, error) {
gc, err := utils.GinContextFromContext(ctx)
var res *model.Response

View File

@ -5,11 +5,13 @@ import (
"fmt"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/utils"
)
func AdminSession(ctx context.Context) (*model.Response, error) {
// AdminSessionResolver is a resolver for admin session query
func AdminSessionResolver(ctx context.Context) (*model.Response, error) {
gc, err := utils.GinContextFromContext(ctx)
var res *model.Response
@ -21,7 +23,7 @@ func AdminSession(ctx context.Context) (*model.Response, error) {
return res, fmt.Errorf("unauthorized")
}
hashedKey, err := utils.HashPassword(constants.EnvData.ADMIN_SECRET)
hashedKey, err := utils.EncryptPassword(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAdminSecret).(string))
if err != nil {
return res, err
}

View File

@ -8,10 +8,12 @@ import (
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/utils"
)
// AdminSignupResolver is a resolver for admin signup mutation
func AdminSignupResolver(ctx context.Context, params model.AdminSignupInput) (*model.Response, error) {
gc, err := utils.GinContextFromContext(ctx)
var res *model.Response
@ -30,17 +32,18 @@ func AdminSignupResolver(ctx context.Context, params model.AdminSignupInput) (*m
return res, err
}
if constants.EnvData.ADMIN_SECRET != "" {
adminSecret := envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAdminSecret).(string)
if adminSecret != "" {
err = fmt.Errorf("admin sign up already completed")
return res, err
}
constants.EnvData.ADMIN_SECRET = params.AdminSecret
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.EnvKeyAdminSecret, params.AdminSecret)
// consvert EnvData to JSON
var jsonData map[string]interface{}
jsonBytes, err := json.Marshal(constants.EnvData)
jsonBytes, err := json.Marshal(envstore.EnvInMemoryStoreObj.GetEnvStoreClone())
if err != nil {
return res, err
}
@ -54,7 +57,7 @@ func AdminSignupResolver(ctx context.Context, params model.AdminSignupInput) (*m
return res, err
}
configData, err := utils.EncryptConfig(jsonData)
configData, err := utils.EncryptEnvData(jsonData)
if err != nil {
return res, err
}
@ -64,7 +67,7 @@ func AdminSignupResolver(ctx context.Context, params model.AdminSignupInput) (*m
return res, err
}
hashedKey, err := utils.HashPassword(params.AdminSecret)
hashedKey, err := utils.EncryptPassword(params.AdminSecret)
if err != nil {
return res, err
}

View File

@ -5,10 +5,15 @@ import (
"fmt"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/utils"
)
// TODO rename to env_data
// ConfigResolver is a resolver for config query
// This is admin only query
func ConfigResolver(ctx context.Context) (*model.Config, error) {
gc, err := utils.GinContextFromContext(ctx)
var res *model.Config
@ -21,40 +26,76 @@ func ConfigResolver(ctx context.Context) (*model.Config, error) {
return res, fmt.Errorf("unauthorized")
}
// get clone of store
store := envstore.EnvInMemoryStoreObj.GetEnvStoreClone()
adminSecret := store[constants.EnvKeyAdminSecret].(string)
databaseType := store[constants.EnvKeyDatabaseType].(string)
databaseURL := store[constants.EnvKeyDatabaseURL].(string)
databaseName := store[constants.EnvKeyDatabaseName].(string)
smtpHost := store[constants.EnvKeySmtpHost].(string)
smtpPort := store[constants.EnvKeySmtpPort].(string)
smtpUsername := store[constants.EnvKeySmtpUsername].(string)
smtpPassword := store[constants.EnvKeySmtpPassword].(string)
senderEmail := store[constants.EnvKeySenderEmail].(string)
jwtType := store[constants.EnvKeyJwtType].(string)
jwtSecret := store[constants.EnvKeyJwtSecret].(string)
jwtRoleClaim := store[constants.EnvKeyJwtRoleClaim].(string)
allowedOrigins := store[constants.EnvKeyAllowedOrigins].([]string)
authorizerURL := store[constants.EnvKeyAuthorizerURL].(string)
appURL := store[constants.EnvKeyAppURL].(string)
redisURL := store[constants.EnvKeyRedisURL].(string)
cookieName := store[constants.EnvKeyCookieName].(string)
resetPasswordURL := store[constants.EnvKeyResetPasswordURL].(string)
disableEmailVerification := store[constants.EnvKeyDisableEmailVerification].(bool)
disableBasicAuthentication := store[constants.EnvKeyDisableBasicAuthentication].(bool)
disableMagicLinkLogin := store[constants.EnvKeyDisableMagicLinkLogin].(bool)
disableLoginPage := store[constants.EnvKeyDisableLoginPage].(bool)
roles := store[constants.EnvKeyRoles].([]string)
defaultRoles := store[constants.EnvKeyDefaultRoles].([]string)
protectedRoles := store[constants.EnvKeyProtectedRoles].([]string)
googleClientID := store[constants.EnvKeyGoogleClientID].(string)
googleClientSecret := store[constants.EnvKeyGoogleClientSecret].(string)
facebookClientID := store[constants.EnvKeyFacebookClientID].(string)
facebookClientSecret := store[constants.EnvKeyFacebookClientSecret].(string)
githubClientID := store[constants.EnvKeyGithubClientID].(string)
githubClientSecret := store[constants.EnvKeyGithubClientSecret].(string)
organizationName := store[constants.EnvKeyOrganizationName].(string)
organizationLogo := store[constants.EnvKeyOrganizationLogo].(string)
res = &model.Config{
AdminSecret: &constants.EnvData.ADMIN_SECRET,
DatabaseType: &constants.EnvData.DATABASE_TYPE,
DatabaseURL: &constants.EnvData.DATABASE_URL,
DatabaseName: &constants.EnvData.DATABASE_NAME,
SMTPHost: &constants.EnvData.SMTP_HOST,
SMTPPort: &constants.EnvData.SMTP_PORT,
SMTPPassword: &constants.EnvData.SMTP_PASSWORD,
SMTPUsername: &constants.EnvData.SMTP_USERNAME,
SenderEmail: &constants.EnvData.SENDER_EMAIL,
JwtType: &constants.EnvData.JWT_TYPE,
JwtSecret: &constants.EnvData.JWT_SECRET,
AllowedOrigins: constants.EnvData.ALLOWED_ORIGINS,
AuthorizerURL: &constants.EnvData.AUTHORIZER_URL,
AppURL: &constants.EnvData.APP_URL,
RedisURL: &constants.EnvData.REDIS_URL,
CookieName: &constants.EnvData.COOKIE_NAME,
ResetPasswordURL: &constants.EnvData.RESET_PASSWORD_URL,
DisableEmailVerification: &constants.EnvData.DISABLE_EMAIL_VERIFICATION,
DisableBasicAuthentication: &constants.EnvData.DISABLE_BASIC_AUTHENTICATION,
DisableMagicLinkLogin: &constants.EnvData.DISABLE_MAGIC_LINK_LOGIN,
DisableLoginPage: &constants.EnvData.DISABLE_LOGIN_PAGE,
Roles: constants.EnvData.ROLES,
ProtectedRoles: constants.EnvData.PROTECTED_ROLES,
DefaultRoles: constants.EnvData.DEFAULT_ROLES,
JwtRoleClaim: &constants.EnvData.JWT_ROLE_CLAIM,
GoogleClientID: &constants.EnvData.GOOGLE_CLIENT_ID,
GoogleClientSecret: &constants.EnvData.GOOGLE_CLIENT_SECRET,
GithubClientID: &constants.EnvData.GITHUB_CLIENT_ID,
GithubClientSecret: &constants.EnvData.GITHUB_CLIENT_SECRET,
FacebookClientID: &constants.EnvData.FACEBOOK_CLIENT_ID,
FacebookClientSecret: &constants.EnvData.FACEBOOK_CLIENT_SECRET,
OrganizationName: &constants.EnvData.ORGANIZATION_NAME,
OrganizationLogo: &constants.EnvData.ORGANIZATION_LOGO,
AdminSecret: &adminSecret,
DatabaseType: &databaseType,
DatabaseURL: &databaseURL,
DatabaseName: &databaseName,
SMTPHost: &smtpHost,
SMTPPort: &smtpPort,
SMTPPassword: &smtpPassword,
SMTPUsername: &smtpUsername,
SenderEmail: &senderEmail,
JwtType: &jwtType,
JwtSecret: &jwtSecret,
JwtRoleClaim: &jwtRoleClaim,
AllowedOrigins: allowedOrigins,
AuthorizerURL: &authorizerURL,
AppURL: &appURL,
RedisURL: &redisURL,
CookieName: &cookieName,
ResetPasswordURL: &resetPasswordURL,
DisableEmailVerification: &disableEmailVerification,
DisableBasicAuthentication: &disableBasicAuthentication,
DisableMagicLinkLogin: &disableMagicLinkLogin,
DisableLoginPage: &disableLoginPage,
Roles: roles,
ProtectedRoles: protectedRoles,
DefaultRoles: defaultRoles,
GoogleClientID: &googleClientID,
GoogleClientSecret: &googleClientSecret,
GithubClientID: &githubClientID,
GithubClientSecret: &githubClientSecret,
FacebookClientID: &facebookClientID,
FacebookClientSecret: &facebookClientSecret,
OrganizationName: &organizationName,
OrganizationLogo: &organizationLogo,
}
return res, nil
}

View File

@ -11,7 +11,8 @@ import (
"github.com/authorizerdev/authorizer/server/utils"
)
func DeleteUser(ctx context.Context, params model.DeleteUserInput) (*model.Response, error) {
// DeleteUserResolver is a resolver for delete user mutation
func DeleteUserResolver(ctx context.Context, params model.DeleteUserInput) (*model.Response, error) {
gc, err := utils.GinContextFromContext(ctx)
var res *model.Response
if err != nil {
@ -27,7 +28,7 @@ func DeleteUser(ctx context.Context, params model.DeleteUserInput) (*model.Respo
return res, err
}
session.DeleteUserSession(fmt.Sprintf("%x", user.ID))
session.DeleteAllUserSession(fmt.Sprintf("%x", user.ID))
err = db.Mgr.DeleteUser(user)
if err != nil {

View File

@ -9,18 +9,20 @@ import (
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/email"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/utils"
)
func ForgotPassword(ctx context.Context, params model.ForgotPasswordInput) (*model.Response, error) {
// ForgotPasswordResolver is a resolver for forgot password mutation
func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInput) (*model.Response, error) {
gc, err := utils.GinContextFromContext(ctx)
var res *model.Response
if err != nil {
return res, err
}
if constants.EnvData.DISABLE_BASIC_AUTHENTICATION {
if envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDisableBasicAuthentication).(bool) {
return res, fmt.Errorf(`basic authentication is disabled for this instance`)
}
host := gc.Request.Host
@ -35,20 +37,20 @@ func ForgotPassword(ctx context.Context, params model.ForgotPasswordInput) (*mod
return res, fmt.Errorf(`user with this email not found`)
}
token, err := utils.CreateVerificationToken(params.Email, enum.ForgotPassword.String())
token, err := utils.CreateVerificationToken(params.Email, constants.VerificationTypeForgotPassword)
if err != nil {
log.Println(`error generating token`, err)
}
db.Mgr.AddVerification(db.VerificationRequest{
Token: token,
Identifier: enum.ForgotPassword.String(),
Identifier: constants.VerificationTypeForgotPassword,
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
Email: params.Email,
})
// exec it as go routin so that we can reduce the api latency
go func() {
utils.SendForgotPasswordMail(params.Email, token, host)
email.SendForgotPasswordMail(params.Email, token, host)
}()
res = &model.Response{

View File

@ -8,21 +8,22 @@ import (
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/session"
"github.com/authorizerdev/authorizer/server/utils"
"golang.org/x/crypto/bcrypt"
)
func Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, error) {
// LoginResolver is a resolver for login mutation
func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthResponse, error) {
gc, err := utils.GinContextFromContext(ctx)
var res *model.AuthResponse
if err != nil {
return res, err
}
if constants.EnvData.DISABLE_BASIC_AUTHENTICATION {
if envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDisableBasicAuthentication).(bool) {
return res, fmt.Errorf(`basic authentication is disabled for this instance`)
}
@ -32,7 +33,7 @@ func Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, e
return res, fmt.Errorf(`user with this email not found`)
}
if !strings.Contains(user.SignupMethods, enum.BasicAuth.String()) {
if !strings.Contains(user.SignupMethods, constants.SignupMethodBasicAuth) {
return res, fmt.Errorf(`user has not signed up email & password`)
}
@ -46,7 +47,7 @@ func Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, e
log.Println("compare password error:", err)
return res, fmt.Errorf(`invalid password`)
}
roles := constants.EnvData.DEFAULT_ROLES
roles := envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDefaultRoles).([]string)
currentRoles := strings.Split(user.Roles, ",")
if len(params.Roles) > 0 {
if !utils.IsValidRoles(currentRoles, params.Roles) {
@ -55,12 +56,12 @@ func Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, e
roles = params.Roles
}
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, roles)
refreshToken, _, _ := utils.CreateAuthToken(user, constants.TokenTypeRefreshToken, roles)
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, roles)
accessToken, expiresAt, _ := utils.CreateAuthToken(user, constants.TokenTypeAccessToken, roles)
session.SetToken(user.ID, accessToken, refreshToken)
utils.CreateSession(user.ID, gc)
session.SetUserSession(user.ID, accessToken, refreshToken)
utils.SaveSessionInDB(user.ID, gc)
res = &model.AuthResponse{
Message: `Logged in successfully`,

View File

@ -9,7 +9,8 @@ import (
"github.com/authorizerdev/authorizer/server/utils"
)
func Logout(ctx context.Context) (*model.Response, error) {
// LogoutResolver is a resolver for logout mutation
func LogoutResolver(ctx context.Context) (*model.Response, error) {
gc, err := utils.GinContextFromContext(ctx)
var res *model.Response
if err != nil {
@ -27,7 +28,7 @@ func Logout(ctx context.Context) (*model.Response, error) {
}
userId := fmt.Sprintf("%v", claim["id"])
session.DeleteVerificationRequest(userId, token)
session.DeleteUserSession(userId, token)
res = &model.Response{
Message: "Logged out successfully",
}

View File

@ -9,15 +9,17 @@ import (
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/email"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/utils"
)
func MagicLinkLogin(ctx context.Context, params model.MagicLinkLoginInput) (*model.Response, error) {
// MagicLinkLoginResolver is a resolver for magic link login mutation
func MagicLinkLoginResolver(ctx context.Context, params model.MagicLinkLoginInput) (*model.Response, error) {
var res *model.Response
if constants.EnvData.DISABLE_MAGIC_LINK_LOGIN {
if envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDisableMagicLinkLogin).(bool) {
return res, fmt.Errorf(`magic link login is disabled for this instance`)
}
@ -37,17 +39,17 @@ func MagicLinkLogin(ctx context.Context, params model.MagicLinkLoginInput) (*mod
existingUser, err := db.Mgr.GetUserByEmail(params.Email)
if err != nil {
user.SignupMethods = enum.MagicLinkLogin.String()
user.SignupMethods = constants.SignupMethodMagicLinkLogin
// define roles for new user
if len(params.Roles) > 0 {
// check if roles exists
if !utils.IsValidRoles(constants.EnvData.ROLES, params.Roles) {
if !utils.IsValidRoles(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyRoles).([]string), params.Roles) {
return res, fmt.Errorf(`invalid roles`)
} else {
inputRoles = params.Roles
}
} else {
inputRoles = constants.EnvData.DEFAULT_ROLES
inputRoles = envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDefaultRoles).([]string)
}
user.Roles = strings.Join(inputRoles, ",")
@ -72,7 +74,7 @@ func MagicLinkLogin(ctx context.Context, params model.MagicLinkLoginInput) (*mod
// check if it contains protected unassigned role
hasProtectedRole := false
for _, ur := range unasignedRoles {
if utils.StringSliceContains(constants.EnvData.PROTECTED_ROLES, ur) {
if utils.StringSliceContains(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyProtectedRoles).([]string), ur) {
hasProtectedRole = true
}
}
@ -87,8 +89,8 @@ func MagicLinkLogin(ctx context.Context, params model.MagicLinkLoginInput) (*mod
}
signupMethod := existingUser.SignupMethods
if !strings.Contains(signupMethod, enum.MagicLinkLogin.String()) {
signupMethod = signupMethod + "," + enum.MagicLinkLogin.String()
if !strings.Contains(signupMethod, constants.SignupMethodMagicLinkLogin) {
signupMethod = signupMethod + "," + constants.SignupMethodMagicLinkLogin
}
user.SignupMethods = signupMethod
@ -98,9 +100,9 @@ func MagicLinkLogin(ctx context.Context, params model.MagicLinkLoginInput) (*mod
}
}
if !constants.EnvData.DISABLE_EMAIL_VERIFICATION {
if !envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDisableEmailVerification).(bool) {
// insert verification request
verificationType := enum.MagicLinkLogin.String()
verificationType := constants.VerificationTypeMagicLinkLogin
token, err := utils.CreateVerificationToken(params.Email, verificationType)
if err != nil {
log.Println(`error generating token`, err)
@ -114,7 +116,7 @@ func MagicLinkLogin(ctx context.Context, params model.MagicLinkLoginInput) (*mod
// exec it as go routin so that we can reduce the api latency
go func() {
utils.SendVerificationMail(params.Email, token)
email.SendVerificationMail(params.Email, token)
}()
}

View File

@ -7,7 +7,8 @@ import (
"github.com/authorizerdev/authorizer/server/utils"
)
func Meta(ctx context.Context) (*model.Meta, error) {
// MetaResolver is a resolver for meta query
func MetaResolver(ctx context.Context) (*model.Meta, error) {
metaInfo := utils.GetMetaInfo()
return &metaInfo, nil
}

View File

@ -10,7 +10,8 @@ import (
"github.com/authorizerdev/authorizer/server/utils"
)
func Profile(ctx context.Context) (*model.User, error) {
// ProfileResolver is a resolver for profile query
func ProfileResolver(ctx context.Context) (*model.User, error) {
gc, err := utils.GinContextFromContext(ctx)
var res *model.User
if err != nil {
@ -29,7 +30,7 @@ func Profile(ctx context.Context) (*model.User, error) {
userID := fmt.Sprintf("%v", claim["id"])
email := fmt.Sprintf("%v", claim["email"])
sessionToken := session.GetToken(userID, token)
sessionToken := session.GetUserSession(userID, token)
if sessionToken == "" {
return res, fmt.Errorf(`unauthorized`)

View File

@ -8,11 +8,13 @@ import (
"time"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/email"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/utils"
)
func ResendVerifyEmail(ctx context.Context, params model.ResendVerifyEmailInput) (*model.Response, error) {
// ResendVerifyEmailResolver is a resolver for resend verify email mutation
func ResendVerifyEmailResolver(ctx context.Context, params model.ResendVerifyEmailInput) (*model.Response, error) {
var res *model.Response
params.Email = strings.ToLower(params.Email)
@ -48,7 +50,7 @@ func ResendVerifyEmail(ctx context.Context, params model.ResendVerifyEmailInput)
// exec it as go routin so that we can reduce the api latency
go func() {
utils.SendVerificationMail(params.Email, token)
email.SendVerificationMail(params.Email, token)
}()
res = &model.Response{

View File

@ -8,14 +8,15 @@ import (
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/utils"
)
func ResetPassword(ctx context.Context, params model.ResetPasswordInput) (*model.Response, error) {
// ResetPasswordResolver is a resolver for reset password mutation
func ResetPasswordResolver(ctx context.Context, params model.ResetPasswordInput) (*model.Response, error) {
var res *model.Response
if constants.EnvData.DISABLE_BASIC_AUTHENTICATION {
if envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDisableBasicAuthentication).(bool) {
return res, fmt.Errorf(`basic authentication is disabled for this instance`)
}
@ -39,12 +40,12 @@ func ResetPassword(ctx context.Context, params model.ResetPasswordInput) (*model
return res, err
}
password, _ := utils.HashPassword(params.Password)
password, _ := utils.EncryptPassword(params.Password)
user.Password = &password
signupMethod := user.SignupMethods
if !strings.Contains(signupMethod, enum.BasicAuth.String()) {
signupMethod = signupMethod + "," + enum.BasicAuth.String()
if !strings.Contains(signupMethod, constants.SignupMethodBasicAuth) {
signupMethod = signupMethod + "," + constants.SignupMethodBasicAuth
}
user.SignupMethods = signupMethod

View File

@ -7,13 +7,14 @@ import (
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/session"
"github.com/authorizerdev/authorizer/server/utils"
)
func Session(ctx context.Context, roles []string) (*model.AuthResponse, error) {
// SessionResolver is a resolver for session query
func SessionResolver(ctx context.Context, roles []string) (*model.AuthResponse, error) {
var res *model.AuthResponse
gc, err := utils.GinContextFromContext(ctx)
@ -36,7 +37,7 @@ func Session(ctx context.Context, roles []string) (*model.AuthResponse, error) {
userIdStr := fmt.Sprintf("%v", user.ID)
sessionToken := session.GetToken(userIdStr, token)
sessionToken := session.GetUserSession(userIdStr, token)
if sessionToken == "" {
return res, fmt.Errorf(`unauthorized`)
@ -45,7 +46,7 @@ func Session(ctx context.Context, roles []string) (*model.AuthResponse, error) {
expiresTimeObj := time.Unix(expiresAt, 0)
currentTimeObj := time.Now()
claimRoleInterface := claim[constants.EnvData.JWT_ROLE_CLAIM].([]interface{})
claimRoleInterface := claim[envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyJwtRoleClaim).(string)].([]interface{})
claimRoles := make([]string, len(claimRoleInterface))
for i, v := range claimRoleInterface {
claimRoles[i] = v.(string)
@ -59,14 +60,15 @@ func Session(ctx context.Context, roles []string) (*model.AuthResponse, error) {
}
}
// TODO change this logic to make it more secure
if accessTokenErr != nil || expiresTimeObj.Sub(currentTimeObj).Minutes() <= 5 {
// if access token has expired and refresh/session token is valid
// generate new accessToken
currentRefreshToken := session.GetToken(userIdStr, token)
session.DeleteVerificationRequest(userIdStr, token)
token, expiresAt, _ = utils.CreateAuthToken(user, enum.AccessToken, claimRoles)
session.SetToken(userIdStr, token, currentRefreshToken)
utils.CreateSession(user.ID, gc)
currentRefreshToken := session.GetUserSession(userIdStr, token)
session.DeleteUserSession(userIdStr, token)
token, expiresAt, _ = utils.CreateAuthToken(user, constants.TokenTypeAccessToken, claimRoles)
session.SetUserSession(userIdStr, token, currentRefreshToken)
utils.SaveSessionInDB(user.ID, gc)
}
utils.SetCookie(gc, token)

View File

@ -9,20 +9,23 @@ import (
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/email"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/session"
"github.com/authorizerdev/authorizer/server/utils"
)
func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse, error) {
// SignupResolver is a resolver for signup mutation
func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthResponse, error) {
log.Println(envstore.EnvInMemoryStoreObj.GetEnvStoreClone())
gc, err := utils.GinContextFromContext(ctx)
var res *model.AuthResponse
if err != nil {
return res, err
}
if constants.EnvData.DISABLE_BASIC_AUTHENTICATION {
if envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDisableBasicAuthentication).(bool) {
return res, fmt.Errorf(`basic authentication is disabled for this instance`)
}
if params.ConfirmPassword != params.Password {
@ -52,13 +55,13 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
if len(params.Roles) > 0 {
// check if roles exists
if !utils.IsValidRoles(constants.EnvData.ROLES, params.Roles) {
if !utils.IsValidRoles(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyRoles).([]string), params.Roles) {
return res, fmt.Errorf(`invalid roles`)
} else {
inputRoles = params.Roles
}
} else {
inputRoles = constants.EnvData.DEFAULT_ROLES
inputRoles = envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDefaultRoles).([]string)
}
user := db.User{
@ -67,7 +70,7 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
user.Roles = strings.Join(inputRoles, ",")
password, _ := utils.HashPassword(params.Password)
password, _ := utils.EncryptPassword(params.Password)
user.Password = &password
if params.GivenName != nil {
@ -102,8 +105,8 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
user.Picture = params.Picture
}
user.SignupMethods = enum.BasicAuth.String()
if constants.EnvData.DISABLE_EMAIL_VERIFICATION {
user.SignupMethods = constants.SignupMethodBasicAuth
if envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDisableEmailVerification).(bool) {
now := time.Now().Unix()
user.EmailVerifiedAt = &now
}
@ -115,9 +118,9 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
roles := strings.Split(user.Roles, ",")
userToReturn := utils.GetResponseUserData(user)
if !constants.EnvData.DISABLE_EMAIL_VERIFICATION {
if !envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDisableEmailVerification).(bool) {
// insert verification request
verificationType := enum.BasicAuthSignup.String()
verificationType := constants.VerificationTypeBasicAuthSignup
token, err := utils.CreateVerificationToken(params.Email, verificationType)
if err != nil {
log.Println(`error generating token`, err)
@ -131,7 +134,7 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
// exec it as go routin so that we can reduce the api latency
go func() {
utils.SendVerificationMail(params.Email, token)
email.SendVerificationMail(params.Email, token)
}()
res = &model.AuthResponse{
@ -140,12 +143,12 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
}
} else {
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, roles)
refreshToken, _, _ := utils.CreateAuthToken(user, constants.TokenTypeRefreshToken, roles)
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, roles)
accessToken, expiresAt, _ := utils.CreateAuthToken(user, constants.TokenTypeAccessToken, roles)
session.SetToken(userIdStr, accessToken, refreshToken)
utils.CreateSession(user.ID, gc)
session.SetUserSession(userIdStr, accessToken, refreshToken)
utils.SaveSessionInDB(user.ID, gc)
res = &model.AuthResponse{
Message: `Signed up successfully.`,
AccessToken: &accessToken,

View File

@ -9,10 +9,15 @@ import (
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/utils"
)
// TODO rename to UpdateEnvDataResolver
// UpdateConfigResolver is a resolver for update config mutation
// This is admin only mutation
func UpdateConfigResolver(ctx context.Context, params model.UpdateConfigInput) (*model.Response, error) {
gc, err := utils.GinContextFromContext(ctx)
var res *model.Response
@ -57,13 +62,13 @@ func UpdateConfigResolver(ctx context.Context, params model.UpdateConfigInput) (
// handle derivative cases like disabling email verification & magic login
// in case SMTP is off but env is set to true
if updatedData["SMTP_HOST"] == "" || updatedData["SENDER_EMAIL"] == "" || updatedData["SENDER_PASSWORD"] == "" {
if !updatedData["DISABLE_EMAIL_VERIFICATION"].(bool) {
updatedData["DISABLE_EMAIL_VERIFICATION"] = true
if updatedData[constants.EnvKeySmtpHost] == "" || updatedData[constants.EnvKeySenderEmail] == "" || updatedData[constants.EnvKeySmtpPort] == "" || updatedData[constants.EnvKeySmtpUsername] == "" || updatedData[constants.EnvKeySmtpPassword] == "" {
if !updatedData[constants.EnvKeyDisableEmailVerification].(bool) {
updatedData[constants.EnvKeyDisableEmailVerification] = true
}
if !updatedData["DISABLE_MAGIC_LINK_LOGIN"].(bool) {
updatedData["DISABLE_MAGIC_LINK_LOGIN"] = true
if !updatedData[constants.EnvKeyDisableMagicLinkLogin].(bool) {
updatedData[constants.EnvKeyDisableMagicLinkLogin] = true
}
}
@ -72,7 +77,9 @@ func UpdateConfigResolver(ctx context.Context, params model.UpdateConfigInput) (
return res, err
}
encryptedConfig, err := utils.EncryptConfig(updatedData)
envstore.EnvInMemoryStoreObj.UpdateEnvStore(updatedData)
encryptedConfig, err := utils.EncryptEnvData(updatedData)
if err != nil {
return res, err
}
@ -84,7 +91,7 @@ func UpdateConfigResolver(ctx context.Context, params model.UpdateConfigInput) (
// in case of admin secret change update the cookie with new hash
if params.AdminSecret != nil {
hashedKey, err := utils.HashPassword(constants.EnvData.ADMIN_SECRET)
hashedKey, err := utils.EncryptPassword(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAdminSecret).(string))
if err != nil {
return res, err
}

View File

@ -7,15 +7,17 @@ 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/email"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/session"
"github.com/authorizerdev/authorizer/server/utils"
"golang.org/x/crypto/bcrypt"
)
func UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model.Response, error) {
// UpdateProfileResolver is resolver for update profile mutation
func UpdateProfileResolver(ctx context.Context, params model.UpdateProfileInput) (*model.Response, error) {
gc, err := utils.GinContextFromContext(ctx)
var res *model.Response
if err != nil {
@ -33,7 +35,7 @@ func UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model
}
id := fmt.Sprintf("%v", claim["id"])
sessionToken := session.GetToken(id, token)
sessionToken := session.GetUserSession(id, token)
if sessionToken == "" {
return res, fmt.Errorf(`unauthorized`)
@ -44,8 +46,8 @@ func UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model
return res, fmt.Errorf("please enter atleast one param to update")
}
email := fmt.Sprintf("%v", claim["email"])
user, err := db.Mgr.GetUserByEmail(email)
userEmail := fmt.Sprintf("%v", claim["email"])
user, err := db.Mgr.GetUserByEmail(userEmail)
if err != nil {
return res, err
}
@ -99,7 +101,7 @@ func UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model
return res, fmt.Errorf(`password and confirm password does not match`)
}
password, _ := utils.HashPassword(*params.NewPassword)
password, _ := utils.EncryptPassword(*params.NewPassword)
user.Password = &password
}
@ -120,14 +122,14 @@ func UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model
return res, fmt.Errorf("user with this email address already exists")
}
session.DeleteUserSession(fmt.Sprintf("%v", user.ID))
session.DeleteAllUserSession(fmt.Sprintf("%v", user.ID))
utils.DeleteCookie(gc)
user.Email = newEmail
user.EmailVerifiedAt = nil
hasEmailChanged = true
// insert verification request
verificationType := enum.UpdateEmail.String()
verificationType := constants.VerificationTypeUpdateEmail
token, err := utils.CreateVerificationToken(newEmail, verificationType)
if err != nil {
log.Println(`error generating token`, err)
@ -141,7 +143,7 @@ func UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model
// exec it as go routin so that we can reduce the api latency
go func() {
utils.SendVerificationMail(newEmail, token)
email.SendVerificationMail(newEmail, token)
}()
}

View File

@ -9,13 +9,16 @@ import (
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/email"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/session"
"github.com/authorizerdev/authorizer/server/utils"
)
func UpdateUser(ctx context.Context, params model.UpdateUserInput) (*model.User, error) {
// UpdateUserResolver is a resolver for update user mutation
// This is admin only mutation
func UpdateUserResolver(ctx context.Context, params model.UpdateUserInput) (*model.User, error) {
gc, err := utils.GinContextFromContext(ctx)
var res *model.User
if err != nil {
@ -67,6 +70,15 @@ func UpdateUser(ctx context.Context, params model.UpdateUserInput) (*model.User,
user.Picture = params.Picture
}
if params.EmailVerified != nil {
if *params.EmailVerified {
now := time.Now().Unix()
user.EmailVerifiedAt = &now
} else {
user.EmailVerifiedAt = nil
}
}
if params.Email != nil && user.Email != *params.Email {
// check if valid email
if !utils.IsValidEmail(*params.Email) {
@ -80,13 +92,13 @@ func UpdateUser(ctx context.Context, params model.UpdateUserInput) (*model.User,
return res, fmt.Errorf("user with this email address already exists")
}
session.DeleteUserSession(fmt.Sprintf("%v", user.ID))
session.DeleteAllUserSession(fmt.Sprintf("%v", user.ID))
utils.DeleteCookie(gc)
user.Email = newEmail
user.EmailVerifiedAt = nil
// insert verification request
verificationType := enum.UpdateEmail.String()
verificationType := constants.VerificationTypeUpdateEmail
token, err := utils.CreateVerificationToken(newEmail, verificationType)
if err != nil {
log.Println(`error generating token`, err)
@ -100,7 +112,7 @@ func UpdateUser(ctx context.Context, params model.UpdateUserInput) (*model.User,
// exec it as go routin so that we can reduce the api latency
go func() {
utils.SendVerificationMail(newEmail, token)
email.SendVerificationMail(newEmail, token)
}()
}
@ -112,7 +124,7 @@ func UpdateUser(ctx context.Context, params model.UpdateUserInput) (*model.User,
inputRoles = append(inputRoles, *item)
}
if !utils.IsValidRoles(append([]string{}, append(constants.EnvData.ROLES, constants.EnvData.PROTECTED_ROLES...)...), inputRoles) {
if !utils.IsValidRoles(append([]string{}, append(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyRoles).([]string), envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyProtectedRoles).([]string)...)...), inputRoles) {
return res, fmt.Errorf("invalid list of roles")
}
@ -120,7 +132,7 @@ func UpdateUser(ctx context.Context, params model.UpdateUserInput) (*model.User,
rolesToSave = strings.Join(inputRoles, ",")
}
session.DeleteUserSession(fmt.Sprintf("%v", user.ID))
session.DeleteAllUserSession(fmt.Sprintf("%v", user.ID))
utils.DeleteCookie(gc)
}

View File

@ -9,7 +9,9 @@ import (
"github.com/authorizerdev/authorizer/server/utils"
)
func Users(ctx context.Context) ([]*model.User, error) {
// UsersResolver is a resolver for users query
// This is admin only query
func UsersResolver(ctx context.Context) ([]*model.User, error) {
gc, err := utils.GinContextFromContext(ctx)
var res []*model.User
if err != nil {

View File

@ -9,7 +9,9 @@ import (
"github.com/authorizerdev/authorizer/server/utils"
)
func VerificationRequests(ctx context.Context) ([]*model.VerificationRequest, error) {
// VerificationRequestsResolver is a resolver for verification requests query
// This is admin only query
func VerificationRequestsResolver(ctx context.Context) ([]*model.VerificationRequest, error) {
gc, err := utils.GinContextFromContext(ctx)
var res []*model.VerificationRequest
if err != nil {

View File

@ -6,14 +6,15 @@ 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"
"github.com/authorizerdev/authorizer/server/session"
"github.com/authorizerdev/authorizer/server/utils"
)
func VerifyEmail(ctx context.Context, params model.VerifyEmailInput) (*model.AuthResponse, error) {
// VerifyEmailResolver is a resolver for verify email mutation
func VerifyEmailResolver(ctx context.Context, params model.VerifyEmailInput) (*model.AuthResponse, error) {
gc, err := utils.GinContextFromContext(ctx)
var res *model.AuthResponse
if err != nil {
@ -44,12 +45,11 @@ func VerifyEmail(ctx context.Context, params model.VerifyEmailInput) (*model.Aut
db.Mgr.DeleteVerificationRequest(verificationRequest)
roles := strings.Split(user.Roles, ",")
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, roles)
refreshToken, _, _ := utils.CreateAuthToken(user, constants.TokenTypeRefreshToken, roles)
accessToken, expiresAt, _ := utils.CreateAuthToken(user, constants.TokenTypeAccessToken, roles)
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, roles)
session.SetToken(user.ID, accessToken, refreshToken)
utils.CreateSession(user.ID, gc)
session.SetUserSession(user.ID, accessToken, refreshToken)
utils.SaveSessionInDB(user.ID, gc)
res = &model.AuthResponse{
Message: `Email verified successfully.`,

View File

@ -1,23 +0,0 @@
package router
import (
"github.com/authorizerdev/authorizer/server/handlers"
"github.com/authorizerdev/authorizer/server/middlewares"
"github.com/gin-contrib/location"
"github.com/gin-gonic/gin"
)
func InitRouter() *gin.Engine {
router := gin.Default()
router.Use(location.Default())
router.Use(middlewares.GinContextToContextMiddleware())
router.Use(middlewares.CORSMiddleware())
router.GET("/", handlers.PlaygroundHandler())
router.POST("/graphql", handlers.GraphqlHandler())
router.GET("/verify_email", handlers.VerifyEmailHandler())
router.GET("/oauth_login/:oauth_provider", handlers.OAuthLoginHandler())
router.GET("/oauth_callback/:oauth_provider", handlers.OAuthCallbackHandler())
return router
}

44
server/routes/routes.go Normal file
View File

@ -0,0 +1,44 @@
package routes
import (
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/handlers"
"github.com/authorizerdev/authorizer/server/middlewares"
"github.com/gin-contrib/location"
"github.com/gin-gonic/gin"
)
// InitRouter initializes gin router
func InitRouter() *gin.Engine {
router := gin.Default()
router.Use(location.Default())
router.Use(middlewares.GinContextToContextMiddleware())
router.Use(middlewares.CORSMiddleware())
router.GET("/", handlers.RootHandler())
router.POST("/graphql", handlers.GraphqlHandler())
router.GET("/playground", handlers.PlaygroundHandler())
router.GET("/oauth_login/:oauth_provider", handlers.OAuthLoginHandler())
router.GET("/oauth_callback/:oauth_provider", handlers.OAuthCallbackHandler())
router.GET("/verify_email", handlers.VerifyEmailHandler())
router.LoadHTMLGlob("templates/*")
// login page app related routes.
if !envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDisableLoginPage).(bool) {
app := router.Group("/app")
{
app.Static("/build", "app/build")
app.GET("/", handlers.AppHandler())
app.GET("/reset-password", handlers.AppHandler())
}
}
// dashboard related routes
app := router.Group("/dashboard")
{
app.Static("/build", "dashboard/build")
app.GET("/", handlers.DashboardHandler())
}
return router
}

View File

@ -4,14 +4,17 @@ import (
"sync"
)
// InMemoryStore is a simple in-memory store for sessions.
type InMemoryStore struct {
mu sync.Mutex
mutex sync.Mutex
store map[string]map[string]string
socialLoginState map[string]string
}
func (c *InMemoryStore) AddToken(userId, accessToken, refreshToken string) {
c.mu.Lock()
// AddUserSession adds a user session to the in-memory store.
func (c *InMemoryStore) AddUserSession(userId, accessToken, refreshToken string) {
c.mutex.Lock()
defer c.mutex.Unlock()
// delete sessions > 500 // not recommended for production
if len(c.store) >= 500 {
c.store = map[string]map[string]string{}
@ -28,48 +31,57 @@ func (c *InMemoryStore) AddToken(userId, accessToken, refreshToken string) {
}
c.store[userId] = tempMap
}
c.mu.Unlock()
}
func (c *InMemoryStore) DeleteUserSession(userId string) {
c.mu.Lock()
// DeleteAllUserSession deletes all the user sessions from in-memory store.
func (c *InMemoryStore) DeleteAllUserSession(userId string) {
c.mutex.Lock()
defer c.mutex.Unlock()
delete(c.store, userId)
c.mu.Unlock()
}
func (c *InMemoryStore) DeleteVerificationRequest(userId, accessToken string) {
c.mu.Lock()
// DeleteUserSession deletes the particular user session from in-memory store.
func (c *InMemoryStore) DeleteUserSession(userId, accessToken string) {
c.mutex.Lock()
defer c.mutex.Unlock()
delete(c.store[userId], accessToken)
c.mu.Unlock()
}
// ClearStore clears the in-memory store.
func (c *InMemoryStore) ClearStore() {
c.mu.Lock()
c.mutex.Lock()
defer c.mutex.Unlock()
c.store = map[string]map[string]string{}
c.mu.Unlock()
}
func (c *InMemoryStore) GetToken(userId, accessToken string) string {
// GetUserSession returns the user session token from the in-memory store.
func (c *InMemoryStore) GetUserSession(userId, accessToken string) string {
// c.mutex.Lock()
// defer c.mutex.Unlock()
token := ""
c.mu.Lock()
if sessionMap, ok := c.store[userId]; ok {
if val, ok := sessionMap[accessToken]; ok {
token = val
}
}
c.mu.Unlock()
return token
}
// SetSocialLoginState sets the social login state in the in-memory store.
func (c *InMemoryStore) SetSocialLoginState(key, state string) {
c.mu.Lock()
c.mutex.Lock()
defer c.mutex.Unlock()
c.socialLoginState[key] = state
c.mu.Unlock()
}
// GetSocialLoginState gets the social login state from the in-memory store.
func (c *InMemoryStore) GetSocialLoginState(key string) string {
c.mutex.Lock()
defer c.mutex.Unlock()
state := ""
if stateVal, ok := c.socialLoginState[key]; ok {
state = stateVal
@ -78,8 +90,10 @@ func (c *InMemoryStore) GetSocialLoginState(key string) string {
return state
}
// RemoveSocialLoginState removes the social login state from the in-memory store.
func (c *InMemoryStore) RemoveSocialLoginState(key string) {
c.mu.Lock()
c.mutex.Lock()
defer c.mutex.Unlock()
delete(c.socialLoginState, key)
c.mu.Unlock()
}

View File

@ -13,7 +13,8 @@ type RedisStore struct {
store *redis.Client
}
func (c *RedisStore) AddToken(userId, accessToken, refreshToken string) {
// AddUserSession adds the user session to redis
func (c *RedisStore) AddUserSession(userId, accessToken, refreshToken string) {
err := c.store.HMSet(c.ctx, "authorizer_"+userId, map[string]string{
accessToken: refreshToken,
}).Err()
@ -22,20 +23,23 @@ func (c *RedisStore) AddToken(userId, accessToken, refreshToken string) {
}
}
func (c *RedisStore) DeleteUserSession(userId string) {
// DeleteAllUserSession deletes all the user session from redis
func (c *RedisStore) DeleteAllUserSession(userId string) {
err := c.store.Del(c.ctx, "authorizer_"+userId).Err()
if err != nil {
log.Fatalln("Error deleting redis token:", err)
}
}
func (c *RedisStore) DeleteVerificationRequest(userId, accessToken string) {
// DeleteUserSession deletes the particular user session from redis
func (c *RedisStore) DeleteUserSession(userId, accessToken string) {
err := c.store.HDel(c.ctx, "authorizer_"+userId, accessToken).Err()
if err != nil {
log.Fatalln("Error deleting redis token:", err)
}
}
// ClearStore clears the redis store for authorizer related tokens
func (c *RedisStore) ClearStore() {
err := c.store.Del(c.ctx, "authorizer_*").Err()
if err != nil {
@ -43,7 +47,8 @@ func (c *RedisStore) ClearStore() {
}
}
func (c *RedisStore) GetToken(userId, accessToken string) string {
// GetUserSession returns the user session token from the redis store.
func (c *RedisStore) GetUserSession(userId, accessToken string) string {
token := ""
res, err := c.store.HMGet(c.ctx, "authorizer_"+userId, accessToken).Result()
if err != nil {
@ -55,6 +60,7 @@ func (c *RedisStore) GetToken(userId, accessToken string) string {
return token
}
// SetSocialLoginState sets the social login state in redis store.
func (c *RedisStore) SetSocialLoginState(key, state string) {
err := c.store.Set(c.ctx, key, state, 0).Err()
if err != nil {
@ -62,6 +68,7 @@ func (c *RedisStore) SetSocialLoginState(key, state string) {
}
}
// GetSocialLoginState gets the social login state from redis store.
func (c *RedisStore) GetSocialLoginState(key string) string {
state := ""
state, err := c.store.Get(c.ctx, key).Result()
@ -72,6 +79,7 @@ func (c *RedisStore) GetSocialLoginState(key string) string {
return state
}
// RemoveSocialLoginState removes the social login state from redis store.
func (c *RedisStore) RemoveSocialLoginState(key string) {
err := c.store.Del(c.ctx, key).Err()
if err != nil {

View File

@ -5,57 +5,65 @@ import (
"log"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/go-redis/redis/v8"
)
// SessionStore is a struct that defines available session stores
// If redis store is available, higher preference is given to that store.
// Else in memory store is used.
type SessionStore struct {
InMemoryStoreObj *InMemoryStore
RedisMemoryStoreObj *RedisStore
}
// SessionStoreObj is a global variable that holds the
// reference to various session store instances
var SessionStoreObj SessionStore
func SetToken(userId, accessToken, refreshToken string) {
// TODO: Set session information in db for all the sessions that gets generated
// it should async go function
// SetUserSession sets the user session in the session store
func SetUserSession(userId, accessToken, refreshToken string) {
if SessionStoreObj.RedisMemoryStoreObj != nil {
SessionStoreObj.RedisMemoryStoreObj.AddToken(userId, accessToken, refreshToken)
SessionStoreObj.RedisMemoryStoreObj.AddUserSession(userId, accessToken, refreshToken)
}
if SessionStoreObj.InMemoryStoreObj != nil {
SessionStoreObj.InMemoryStoreObj.AddToken(userId, accessToken, refreshToken)
SessionStoreObj.InMemoryStoreObj.AddUserSession(userId, accessToken, refreshToken)
}
}
func DeleteVerificationRequest(userId, accessToken string) {
// DeleteUserSession deletes the particular user session from the session store
func DeleteUserSession(userId, accessToken string) {
if SessionStoreObj.RedisMemoryStoreObj != nil {
SessionStoreObj.RedisMemoryStoreObj.DeleteVerificationRequest(userId, accessToken)
SessionStoreObj.RedisMemoryStoreObj.DeleteUserSession(userId, accessToken)
}
if SessionStoreObj.InMemoryStoreObj != nil {
SessionStoreObj.InMemoryStoreObj.DeleteVerificationRequest(userId, accessToken)
SessionStoreObj.InMemoryStoreObj.DeleteUserSession(userId, accessToken)
}
}
func DeleteUserSession(userId string) {
// DeleteAllSessions deletes all the sessions from the session store
func DeleteAllUserSession(userId string) {
if SessionStoreObj.RedisMemoryStoreObj != nil {
SessionStoreObj.RedisMemoryStoreObj.DeleteUserSession(userId)
SessionStoreObj.RedisMemoryStoreObj.DeleteAllUserSession(userId)
}
if SessionStoreObj.InMemoryStoreObj != nil {
SessionStoreObj.InMemoryStoreObj.DeleteUserSession(userId)
SessionStoreObj.InMemoryStoreObj.DeleteAllUserSession(userId)
}
}
func GetToken(userId, accessToken string) string {
// GetUserSession returns the user session from the session store
func GetUserSession(userId, accessToken string) string {
if SessionStoreObj.RedisMemoryStoreObj != nil {
return SessionStoreObj.RedisMemoryStoreObj.GetToken(userId, accessToken)
return SessionStoreObj.RedisMemoryStoreObj.GetUserSession(userId, accessToken)
}
if SessionStoreObj.InMemoryStoreObj != nil {
return SessionStoreObj.InMemoryStoreObj.GetToken(userId, accessToken)
return SessionStoreObj.InMemoryStoreObj.GetUserSession(userId, accessToken)
}
return ""
}
// ClearStore clears the session store for authorizer tokens
func ClearStore() {
if SessionStoreObj.RedisMemoryStoreObj != nil {
SessionStoreObj.RedisMemoryStoreObj.ClearStore()
@ -65,6 +73,7 @@ func ClearStore() {
}
}
// SetSocialLoginState sets the social login state in the session store
func SetSocailLoginState(key, state string) {
if SessionStoreObj.RedisMemoryStoreObj != nil {
SessionStoreObj.RedisMemoryStoreObj.SetSocialLoginState(key, state)
@ -74,6 +83,7 @@ func SetSocailLoginState(key, state string) {
}
}
// GetSocialLoginState returns the social login state from the session store
func GetSocailLoginState(key string) string {
if SessionStoreObj.RedisMemoryStoreObj != nil {
return SessionStoreObj.RedisMemoryStoreObj.GetSocialLoginState(key)
@ -85,6 +95,7 @@ func GetSocailLoginState(key string) string {
return ""
}
// RemoveSocialLoginState removes the social login state from the session store
func RemoveSocialLoginState(key string) {
if SessionStoreObj.RedisMemoryStoreObj != nil {
SessionStoreObj.RedisMemoryStoreObj.RemoveSocialLoginState(key)
@ -94,10 +105,11 @@ func RemoveSocialLoginState(key string) {
}
}
// InitializeSessionStore initializes the SessionStoreObj based on environment variables
func InitSession() {
if constants.EnvData.REDIS_URL != "" {
if envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyRedisURL).(string) != "" {
log.Println("using redis store to save sessions")
opt, err := redis.ParseURL(constants.EnvData.REDIS_URL)
opt, err := redis.ParseURL(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyRedisURL).(string))
if err != nil {
log.Fatalln("Error parsing redis url:", err)
}

View File

@ -4,12 +4,14 @@ import (
"testing"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func adminLoginTests(s TestSetup, t *testing.T) {
func adminLoginTests(t *testing.T, s TestSetup) {
t.Helper()
t.Run(`should complete admin login`, func(t *testing.T) {
_, ctx := createContext(s)
_, err := resolvers.AdminLoginResolver(ctx, model.AdminLoginInput{
@ -19,7 +21,7 @@ func adminLoginTests(s TestSetup, t *testing.T) {
assert.NotNil(t, err)
_, err = resolvers.AdminLoginResolver(ctx, model.AdminLoginInput{
AdminSecret: constants.EnvData.ADMIN_SECRET,
AdminSecret: envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAdminSecret).(string),
})
assert.Nil(t, err)

View File

@ -0,0 +1,28 @@
package test
import (
"fmt"
"testing"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/stretchr/testify/assert"
)
func adminLogoutTests(t *testing.T, s TestSetup) {
t.Helper()
t.Run(`should get admin session`, func(t *testing.T) {
req, ctx := createContext(s)
_, err := resolvers.AdminLogoutResolver(ctx)
assert.NotNil(t, err)
h, err := utils.EncryptPassword(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAdminSecret).(string))
assert.Nil(t, err)
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAdminCookieName).(string), h))
_, err = resolvers.AdminLogoutResolver(ctx)
assert.Nil(t, err)
})
}

View File

@ -0,0 +1,30 @@
package test
import (
"fmt"
"log"
"testing"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/stretchr/testify/assert"
)
func adminSessionTests(t *testing.T, s TestSetup) {
t.Helper()
t.Run(`should get admin session`, func(t *testing.T) {
req, ctx := createContext(s)
_, err := resolvers.AdminSessionResolver(ctx)
log.Println("error:", err)
assert.NotNil(t, err)
h, err := utils.EncryptPassword(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAdminSecret).(string))
assert.Nil(t, err)
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAdminCookieName).(string), h))
_, err = resolvers.AdminSessionResolver(ctx)
assert.Nil(t, err)
})
}

View File

@ -1,26 +1,27 @@
package test
import (
"log"
"testing"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
)
func adminSignupTests(s TestSetup, t *testing.T) {
func adminSignupTests(t *testing.T, s TestSetup) {
t.Helper()
t.Run(`should complete admin login`, func(t *testing.T) {
_, ctx := createContext(s)
_, err := resolvers.AdminSignupResolver(ctx, model.AdminSignupInput{
AdminSecret: "admin",
})
log.Println("err", err)
assert.NotNil(t, err)
// reset env for test to pass
constants.EnvData.ADMIN_SECRET = ""
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.EnvKeyAdminSecret, "")
_, err = resolvers.AdminSignupResolver(ctx, model.AdminSignupInput{
AdminSecret: uuid.New().String(),

View File

@ -6,24 +6,26 @@ import (
"testing"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/stretchr/testify/assert"
)
func configTests(s TestSetup, t *testing.T) {
func configTests(t *testing.T, s TestSetup) {
t.Helper()
t.Run(`should get config`, func(t *testing.T) {
req, ctx := createContext(s)
_, err := resolvers.ConfigResolver(ctx)
log.Println("error:", err)
assert.NotNil(t, err)
h, err := utils.HashPassword(constants.EnvData.ADMIN_SECRET)
h, err := utils.EncryptPassword(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAdminSecret).(string))
assert.Nil(t, err)
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", constants.EnvData.ADMIN_COOKIE_NAME, h))
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAdminCookieName).(string), h))
res, err := resolvers.ConfigResolver(ctx)
assert.Nil(t, err)
assert.Equal(t, *res.AdminSecret, constants.EnvData.ADMIN_SECRET)
assert.Equal(t, *res.AdminSecret, envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAdminSecret).(string))
})
}

View File

@ -5,32 +5,34 @@ import (
"testing"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/stretchr/testify/assert"
)
func deleteUserTest(s TestSetup, t *testing.T) {
func deleteUserTest(t *testing.T, s TestSetup) {
t.Helper()
t.Run(`should delete users with admin secret only`, func(t *testing.T) {
req, ctx := createContext(s)
email := "delete_user." + s.TestInfo.Email
resolvers.Signup(ctx, model.SignUpInput{
resolvers.SignupResolver(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
_, err := resolvers.DeleteUser(ctx, model.DeleteUserInput{
_, err := resolvers.DeleteUserResolver(ctx, model.DeleteUserInput{
Email: email,
})
assert.NotNil(t, err, "unauthorized")
h, err := utils.HashPassword(constants.EnvData.ADMIN_SECRET)
h, err := utils.EncryptPassword(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAdminSecret).(string))
assert.Nil(t, err)
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", constants.EnvData.ADMIN_COOKIE_NAME, h))
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAdminCookieName).(string), h))
_, err = resolvers.DeleteUser(ctx, model.DeleteUserInput{
_, err = resolvers.DeleteUserResolver(ctx, model.DeleteUserInput{
Email: email,
})
assert.Nil(t, err)

29
server/test/env_test.go Normal file
View File

@ -0,0 +1,29 @@
package test
import (
"testing"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/env"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/stretchr/testify/assert"
)
func TestEnvs(t *testing.T) {
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.EnvKeyEnvPath, "../../.env.sample")
env.InitEnv()
store := envstore.EnvInMemoryStoreObj.GetEnvStoreClone()
assert.Equal(t, store[constants.EnvKeyAdminSecret].(string), "admin")
assert.Equal(t, store[constants.EnvKeyEnv].(string), "production")
assert.False(t, store[constants.EnvKeyDisableEmailVerification].(bool))
assert.False(t, store[constants.EnvKeyDisableMagicLinkLogin].(bool))
assert.False(t, store[constants.EnvKeyDisableBasicAuthentication].(bool))
assert.Equal(t, store[constants.EnvKeyJwtType].(string), "HS256")
assert.Equal(t, store[constants.EnvKeyJwtSecret].(string), "random_string")
assert.Equal(t, store[constants.EnvKeyJwtRoleClaim].(string), "role")
assert.EqualValues(t, store[constants.EnvKeyRoles].([]string), []string{"user"})
assert.EqualValues(t, store[constants.EnvKeyDefaultRoles].([]string), []string{"user"})
assert.EqualValues(t, store[constants.EnvKeyProtectedRoles].([]string), []string{"admin"})
assert.EqualValues(t, store[constants.EnvKeyAllowedOrigins].([]string), []string{"*"})
}

View File

@ -3,32 +3,33 @@ package test
import (
"testing"
"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"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func forgotPasswordTest(s TestSetup, t *testing.T) {
func forgotPasswordTest(t *testing.T, s TestSetup) {
t.Helper()
t.Run(`should run forgot password`, func(t *testing.T) {
_, ctx := createContext(s)
email := "forgot_password." + s.TestInfo.Email
_, err := resolvers.Signup(ctx, model.SignUpInput{
_, err := resolvers.SignupResolver(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
_, err = resolvers.ForgotPassword(ctx, model.ForgotPasswordInput{
_, err = resolvers.ForgotPasswordResolver(ctx, model.ForgotPasswordInput{
Email: email,
})
assert.Nil(t, err, "no errors for forgot password")
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.ForgotPassword.String())
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, constants.VerificationTypeForgotPassword)
assert.Nil(t, err)
assert.Equal(t, verificationRequest.Identifier, enum.ForgotPassword.String())
assert.Equal(t, verificationRequest.Identifier, constants.VerificationTypeForgotPassword)
cleanData(email)
})

View File

@ -3,49 +3,50 @@ package test
import (
"testing"
"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"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func loginTests(s TestSetup, t *testing.T) {
func loginTests(t *testing.T, s TestSetup) {
t.Helper()
t.Run(`should login`, func(t *testing.T) {
_, ctx := createContext(s)
email := "login." + s.TestInfo.Email
_, err := resolvers.Signup(ctx, model.SignUpInput{
_, err := resolvers.SignupResolver(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
_, err = resolvers.Login(ctx, model.LoginInput{
_, err = resolvers.LoginResolver(ctx, model.LoginInput{
Email: email,
Password: s.TestInfo.Password,
})
assert.NotNil(t, err, "should fail because email is not verified")
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.BasicAuthSignup.String())
resolvers.VerifyEmail(ctx, model.VerifyEmailInput{
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, constants.VerificationTypeBasicAuthSignup)
resolvers.VerifyEmailResolver(ctx, model.VerifyEmailInput{
Token: verificationRequest.Token,
})
_, err = resolvers.Login(ctx, model.LoginInput{
_, err = resolvers.LoginResolver(ctx, model.LoginInput{
Email: email,
Password: s.TestInfo.Password,
Roles: []string{"test"},
})
assert.NotNil(t, err, "invalid roles")
_, err = resolvers.Login(ctx, model.LoginInput{
_, err = resolvers.LoginResolver(ctx, model.LoginInput{
Email: email,
Password: s.TestInfo.Password + "s",
})
assert.NotNil(t, err, "invalid password")
loginRes, err := resolvers.Login(ctx, model.LoginInput{
loginRes, err := resolvers.LoginResolver(ctx, model.LoginInput{
Email: email,
Password: s.TestInfo.Password,
})

View File

@ -6,31 +6,32 @@ import (
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func logoutTests(s TestSetup, t *testing.T) {
func logoutTests(t *testing.T, s TestSetup) {
t.Helper()
t.Run(`should logout user`, func(t *testing.T) {
req, ctx := createContext(s)
email := "logout." + s.TestInfo.Email
_, err := resolvers.MagicLinkLogin(ctx, model.MagicLinkLoginInput{
_, err := resolvers.MagicLinkLoginResolver(ctx, model.MagicLinkLoginInput{
Email: email,
})
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.MagicLinkLogin.String())
verifyRes, err := resolvers.VerifyEmail(ctx, model.VerifyEmailInput{
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, constants.VerificationTypeMagicLinkLogin)
verifyRes, err := resolvers.VerifyEmailResolver(ctx, model.VerifyEmailInput{
Token: verificationRequest.Token,
})
token := *verifyRes.AccessToken
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", constants.EnvData.COOKIE_NAME, token))
_, err = resolvers.Logout(ctx)
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyCookieName).(string), token))
_, err = resolvers.LogoutResolver(ctx)
assert.Nil(t, err)
_, err = resolvers.Profile(ctx)
_, err = resolvers.ProfileResolver(ctx)
assert.NotNil(t, err, "unauthorized")
cleanData(email)
})

View File

@ -6,30 +6,31 @@ import (
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func magicLinkLoginTests(s TestSetup, t *testing.T) {
func magicLinkLoginTests(t *testing.T, s TestSetup) {
t.Helper()
t.Run(`should login with magic link`, func(t *testing.T) {
req, ctx := createContext(s)
email := "magic_link_login." + s.TestInfo.Email
_, err := resolvers.MagicLinkLogin(ctx, model.MagicLinkLoginInput{
_, err := resolvers.MagicLinkLoginResolver(ctx, model.MagicLinkLoginInput{
Email: email,
})
assert.Nil(t, err)
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.MagicLinkLogin.String())
verifyRes, err := resolvers.VerifyEmail(ctx, model.VerifyEmailInput{
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, constants.VerificationTypeMagicLinkLogin)
verifyRes, err := resolvers.VerifyEmailResolver(ctx, model.VerifyEmailInput{
Token: verificationRequest.Token,
})
token := *verifyRes.AccessToken
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", constants.EnvData.COOKIE_NAME, token))
_, err = resolvers.Profile(ctx)
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyCookieName).(string), token))
_, err = resolvers.ProfileResolver(ctx)
assert.Nil(t, err)
cleanData(email)

View File

@ -2,18 +2,17 @@ package test
import (
"context"
"log"
"testing"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func metaTests(s TestSetup, t *testing.T) {
func metaTests(t *testing.T, s TestSetup) {
t.Helper()
t.Run(`should get meta information`, func(t *testing.T) {
ctx := context.Background()
meta, err := resolvers.Meta(ctx)
log.Println("=> meta:", meta)
meta, err := resolvers.MetaResolver(ctx)
assert.Nil(t, err)
assert.False(t, meta.IsFacebookLoginEnabled)
assert.False(t, meta.IsGoogleLoginEnabled)

View File

@ -6,34 +6,35 @@ import (
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func profileTests(s TestSetup, t *testing.T) {
func profileTests(t *testing.T, s TestSetup) {
t.Helper()
t.Run(`should get profile only with token`, func(t *testing.T) {
req, ctx := createContext(s)
email := "profile." + s.TestInfo.Email
resolvers.Signup(ctx, model.SignUpInput{
resolvers.SignupResolver(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
_, err := resolvers.Profile(ctx)
_, err := resolvers.ProfileResolver(ctx)
assert.NotNil(t, err, "unauthorized")
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.BasicAuthSignup.String())
verifyRes, err := resolvers.VerifyEmail(ctx, model.VerifyEmailInput{
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, constants.VerificationTypeBasicAuthSignup)
verifyRes, err := resolvers.VerifyEmailResolver(ctx, model.VerifyEmailInput{
Token: verificationRequest.Token,
})
token := *verifyRes.AccessToken
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", constants.EnvData.COOKIE_NAME, token))
profileRes, err := resolvers.Profile(ctx)
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyCookieName).(string), token))
profileRes, err := resolvers.ProfileResolver(ctx)
assert.Nil(t, err)
newEmail := *&profileRes.Email

View File

@ -3,25 +3,26 @@ package test
import (
"testing"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func resendVerifyEmailTests(s TestSetup, t *testing.T) {
func resendVerifyEmailTests(t *testing.T, s TestSetup) {
t.Helper()
t.Run(`should resend verification email`, func(t *testing.T) {
_, ctx := createContext(s)
email := "resend_verify_email." + s.TestInfo.Email
_, err := resolvers.Signup(ctx, model.SignUpInput{
_, err := resolvers.SignupResolver(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
_, err = resolvers.ResendVerifyEmail(ctx, model.ResendVerifyEmailInput{
_, err = resolvers.ResendVerifyEmailResolver(ctx, model.ResendVerifyEmailInput{
Email: email,
Identifier: enum.BasicAuthSignup.String(),
Identifier: constants.VerificationTypeBasicAuthSignup,
})
assert.Nil(t, err)

View File

@ -3,32 +3,33 @@ package test
import (
"testing"
"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"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func resetPasswordTest(s TestSetup, t *testing.T) {
func resetPasswordTest(t *testing.T, s TestSetup) {
t.Helper()
t.Run(`should reset password`, func(t *testing.T) {
email := "reset_password." + s.TestInfo.Email
_, ctx := createContext(s)
_, err := resolvers.Signup(ctx, model.SignUpInput{
_, err := resolvers.SignupResolver(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
_, err = resolvers.ForgotPassword(ctx, model.ForgotPasswordInput{
_, err = resolvers.ForgotPasswordResolver(ctx, model.ForgotPasswordInput{
Email: email,
})
assert.Nil(t, err, "no errors for forgot password")
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.ForgotPassword.String())
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, constants.VerificationTypeForgotPassword)
assert.Nil(t, err, "should get forgot password request")
_, err = resolvers.ResetPassword(ctx, model.ResetPasswordInput{
_, err = resolvers.ResetPasswordResolver(ctx, model.ResetPasswordInput{
Token: verificationRequest.Token,
Password: "test1",
ConfirmPassword: "test",
@ -36,7 +37,7 @@ func resetPasswordTest(s TestSetup, t *testing.T) {
assert.NotNil(t, err, "passowrds don't match")
_, err = resolvers.ResetPassword(ctx, model.ResetPasswordInput{
_, err = resolvers.ResetPasswordResolver(ctx, model.ResetPasswordInput{
Token: verificationRequest.Token,
Password: "test1",
ConfirmPassword: "test1",

View File

@ -0,0 +1,65 @@
package test
import (
"testing"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/env"
"github.com/authorizerdev/authorizer/server/envstore"
)
func TestResolvers(t *testing.T) {
databases := map[string]string{
constants.DbTypeSqlite: "../../data.db",
// constants.DbTypeArangodb: "http://localhost:8529",
// constants.DbTypeMongodb: "mongodb://localhost:27017",
}
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.EnvKeyVersion, "test")
for dbType, dbURL := range databases {
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.EnvKeyDatabaseURL, dbURL)
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.EnvKeyDatabaseType, dbType)
env.InitEnv()
db.InitDB()
// clean the persisted config for test to use fresh config
config, err := db.Mgr.GetConfig()
if err == nil {
config.Config = []byte{}
db.Mgr.UpdateConfig(config)
}
env.PersistEnv()
s := testSetup()
defer s.Server.Close()
t.Run("should pass tests for "+dbType, func(t *testing.T) {
// admin tests
adminSignupTests(t, s)
verificationRequestsTest(t, s)
usersTest(t, s)
deleteUserTest(t, s)
updateUserTest(t, s)
adminLoginTests(t, s)
adminLogoutTests(t, s)
adminSessionTests(t, s)
updateConfigTests(t, s)
configTests(t, s)
// user tests
loginTests(t, s)
signupTests(t, s)
forgotPasswordTest(t, s)
resendVerifyEmailTests(t, s)
resetPasswordTest(t, s)
verifyEmailTest(t, s)
sessionTests(t, s)
profileTests(t, s)
updateProfileTests(t, s)
magicLinkLoginTests(t, s)
logoutTests(t, s)
metaTests(t, s)
})
}
}

View File

@ -6,35 +6,36 @@ import (
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func sessionTests(s TestSetup, t *testing.T) {
func sessionTests(t *testing.T, s TestSetup) {
t.Helper()
t.Run(`should allow access to profile with session only`, func(t *testing.T) {
req, ctx := createContext(s)
email := "session." + s.TestInfo.Email
resolvers.Signup(ctx, model.SignUpInput{
resolvers.SignupResolver(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
_, err := resolvers.Session(ctx, []string{})
_, err := resolvers.SessionResolver(ctx, []string{})
assert.NotNil(t, err, "unauthorized")
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.BasicAuthSignup.String())
verifyRes, err := resolvers.VerifyEmail(ctx, model.VerifyEmailInput{
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, constants.VerificationTypeBasicAuthSignup)
verifyRes, err := resolvers.VerifyEmailResolver(ctx, model.VerifyEmailInput{
Token: verificationRequest.Token,
})
token := *verifyRes.AccessToken
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", constants.EnvData.COOKIE_NAME, token))
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyCookieName).(string), token))
sessionRes, err := resolvers.Session(ctx, []string{})
sessionRes, err := resolvers.SessionResolver(ctx, []string{})
assert.Nil(t, err)
newToken := *sessionRes.AccessToken

View File

@ -3,25 +3,26 @@ package test
import (
"testing"
"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"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func signupTests(s TestSetup, t *testing.T) {
func signupTests(t *testing.T, s TestSetup) {
t.Helper()
t.Run(`should complete the signup and check duplicates`, func(t *testing.T) {
_, ctx := createContext(s)
email := "signup." + s.TestInfo.Email
res, err := resolvers.Signup(ctx, model.SignUpInput{
res, err := resolvers.SignupResolver(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password + "s",
})
assert.NotNil(t, err, "invalid password errors")
res, err = resolvers.Signup(ctx, model.SignUpInput{
res, err = resolvers.SignupResolver(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
@ -31,7 +32,7 @@ func signupTests(s TestSetup, t *testing.T) {
assert.Equal(t, email, user.Email)
assert.Nil(t, res.AccessToken, "access token should be nil")
res, err = resolvers.Signup(ctx, model.SignUpInput{
res, err = resolvers.SignupResolver(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
@ -39,7 +40,7 @@ func signupTests(s TestSetup, t *testing.T) {
assert.NotNil(t, err, "should throw duplicate email error")
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.BasicAuthSignup.String())
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, constants.VerificationTypeBasicAuthSignup)
assert.Nil(t, err)
assert.Equal(t, email, verificationRequest.Email)
cleanData(email)

View File

@ -9,8 +9,8 @@ import (
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/env"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/handlers"
"github.com/authorizerdev/authorizer/server/middlewares"
"github.com/authorizerdev/authorizer/server/session"
@ -32,17 +32,17 @@ type TestSetup struct {
}
func cleanData(email string) {
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.BasicAuthSignup.String())
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, constants.VerificationTypeBasicAuthSignup)
if err == nil {
err = db.Mgr.DeleteVerificationRequest(verificationRequest)
}
verificationRequest, err = db.Mgr.GetVerificationByEmail(email, enum.ForgotPassword.String())
verificationRequest, err = db.Mgr.GetVerificationByEmail(email, constants.VerificationTypeForgotPassword)
if err == nil {
err = db.Mgr.DeleteVerificationRequest(verificationRequest)
}
verificationRequest, err = db.Mgr.GetVerificationByEmail(email, enum.UpdateEmail.String())
verificationRequest, err = db.Mgr.GetVerificationByEmail(email, constants.VerificationTypeUpdateEmail)
if err == nil {
err = db.Mgr.DeleteVerificationRequest(verificationRequest)
}
@ -72,8 +72,7 @@ func testSetup() TestSetup {
Password: "test",
}
constants.EnvData.ENV_PATH = "../../.env.sample"
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.EnvKeyEnvPath, "../../.env.sample")
env.InitEnv()
session.InitSession()

View File

@ -6,26 +6,27 @@ import (
"testing"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/stretchr/testify/assert"
)
func updateConfigTests(s TestSetup, t *testing.T) {
func updateConfigTests(t *testing.T, s TestSetup) {
t.Helper()
t.Run(`should update configs`, func(t *testing.T) {
req, ctx := createContext(s)
originalAppURL := constants.EnvData.APP_URL
log.Println("=> originalAppURL:", constants.EnvData.APP_URL)
originalAppURL := envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAppURL).(string)
data := model.UpdateConfigInput{}
_, err := resolvers.UpdateConfigResolver(ctx, data)
log.Println("error:", err)
assert.NotNil(t, err)
h, _ := utils.HashPassword(constants.EnvData.ADMIN_SECRET)
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", constants.EnvData.ADMIN_COOKIE_NAME, h))
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", constants.EnvData.ADMIN_COOKIE_NAME, h))
h, err := utils.EncryptPassword(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAdminSecret).(string))
assert.Nil(t, err)
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAdminCookieName).(string), h))
newURL := "https://test.com"
data = model.UpdateConfigInput{
AppURL: &newURL,
@ -33,8 +34,7 @@ func updateConfigTests(s TestSetup, t *testing.T) {
_, err = resolvers.UpdateConfigResolver(ctx, data)
log.Println("error:", err)
assert.Nil(t, err)
assert.Equal(t, constants.EnvData.APP_URL, newURL)
assert.NotEqual(t, constants.EnvData.APP_URL, originalAppURL)
assert.Equal(t, envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAppURL).(string), newURL)
data = model.UpdateConfigInput{
AppURL: &originalAppURL,
}

View File

@ -6,47 +6,48 @@ import (
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func updateProfileTests(s TestSetup, t *testing.T) {
func updateProfileTests(t *testing.T, s TestSetup) {
t.Helper()
t.Run(`should update the profile with access token only`, func(t *testing.T) {
req, ctx := createContext(s)
email := "update_profile." + s.TestInfo.Email
resolvers.Signup(ctx, model.SignUpInput{
resolvers.SignupResolver(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
fName := "samani"
_, err := resolvers.UpdateProfile(ctx, model.UpdateProfileInput{
_, err := resolvers.UpdateProfileResolver(ctx, model.UpdateProfileInput{
FamilyName: &fName,
})
assert.NotNil(t, err, "unauthorized")
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.BasicAuthSignup.String())
verifyRes, err := resolvers.VerifyEmail(ctx, model.VerifyEmailInput{
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, constants.VerificationTypeBasicAuthSignup)
verifyRes, err := resolvers.VerifyEmailResolver(ctx, model.VerifyEmailInput{
Token: verificationRequest.Token,
})
token := *verifyRes.AccessToken
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", constants.EnvData.COOKIE_NAME, token))
_, err = resolvers.UpdateProfile(ctx, model.UpdateProfileInput{
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyCookieName).(string), token))
_, err = resolvers.UpdateProfileResolver(ctx, model.UpdateProfileInput{
FamilyName: &fName,
})
assert.Nil(t, err)
newEmail := "new_" + email
_, err = resolvers.UpdateProfile(ctx, model.UpdateProfileInput{
_, err = resolvers.UpdateProfileResolver(ctx, model.UpdateProfileInput{
Email: &newEmail,
})
assert.Nil(t, err)
_, err = resolvers.Profile(ctx)
_, err = resolvers.ProfileResolver(ctx)
assert.NotNil(t, err, "unauthorized")
cleanData(newEmail)

View File

@ -5,17 +5,19 @@ import (
"testing"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/stretchr/testify/assert"
)
func updateUserTest(s TestSetup, t *testing.T) {
func updateUserTest(t *testing.T, s TestSetup) {
t.Helper()
t.Run(`should update the user with admin secret only`, func(t *testing.T) {
req, ctx := createContext(s)
email := "update_user." + s.TestInfo.Email
signupRes, _ := resolvers.Signup(ctx, model.SignUpInput{
signupRes, _ := resolvers.SignupResolver(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
@ -25,16 +27,16 @@ func updateUserTest(s TestSetup, t *testing.T) {
adminRole := "admin"
userRole := "user"
newRoles := []*string{&adminRole, &userRole}
_, err := resolvers.UpdateUser(ctx, model.UpdateUserInput{
_, err := resolvers.UpdateUserResolver(ctx, model.UpdateUserInput{
ID: user.ID,
Roles: newRoles,
})
assert.NotNil(t, err, "unauthorized")
h, err := utils.HashPassword(constants.EnvData.ADMIN_SECRET)
h, err := utils.EncryptPassword(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAdminSecret).(string))
assert.Nil(t, err)
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", constants.EnvData.ADMIN_COOKIE_NAME, h))
_, err = resolvers.UpdateUser(ctx, model.UpdateUserInput{
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAdminCookieName).(string), h))
_, err = resolvers.UpdateUserResolver(ctx, model.UpdateUserInput{
ID: user.ID,
Roles: newRoles,
})

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