feat: persist encrypted env

This commit is contained in:
Lakhan Samani 2021-12-31 13:52:10 +05:30
parent d9c40057e6
commit e35d0cbcd6
41 changed files with 751 additions and 298 deletions

View File

@ -24,7 +24,7 @@ func deleteUserTest(s TestSetup, t *testing.T) {
}) })
assert.NotNil(t, err, "unauthorized") assert.NotNil(t, err, "unauthorized")
req.Header.Add("x-authorizer-admin-secret", constants.ADMIN_SECRET) req.Header.Add("x-authorizer-admin-secret", constants.EnvData.ADMIN_SECRET)
_, err = resolvers.DeleteUser(ctx, model.DeleteUserInput{ _, err = resolvers.DeleteUser(ctx, model.DeleteUserInput{
Email: email, Email: email,
}) })

View File

@ -8,18 +8,18 @@ import (
) )
func TestEnvs(t *testing.T) { func TestEnvs(t *testing.T) {
constants.ENV_PATH = "../../.env.sample" constants.EnvData.ENV_PATH = "../../.env.sample"
assert.Equal(t, constants.ADMIN_SECRET, "admin") assert.Equal(t, constants.EnvData.ADMIN_SECRET, "admin")
assert.Equal(t, constants.ENV, "production") assert.Equal(t, constants.EnvData.ENV, "production")
assert.False(t, constants.DISABLE_EMAIL_VERIFICATION) assert.False(t, constants.EnvData.DISABLE_EMAIL_VERIFICATION)
assert.False(t, constants.DISABLE_MAGIC_LINK_LOGIN) assert.False(t, constants.EnvData.DISABLE_MAGIC_LINK_LOGIN)
assert.False(t, constants.DISABLE_BASIC_AUTHENTICATION) assert.False(t, constants.EnvData.DISABLE_BASIC_AUTHENTICATION)
assert.Equal(t, constants.JWT_TYPE, "HS256") assert.Equal(t, constants.EnvData.JWT_TYPE, "HS256")
assert.Equal(t, constants.JWT_SECRET, "random_string") assert.Equal(t, constants.EnvData.JWT_SECRET, "random_string")
assert.Equal(t, constants.JWT_ROLE_CLAIM, "role") assert.Equal(t, constants.EnvData.JWT_ROLE_CLAIM, "role")
assert.EqualValues(t, constants.ROLES, []string{"user"}) assert.EqualValues(t, constants.EnvData.ROLES, []string{"user"})
assert.EqualValues(t, constants.DEFAULT_ROLES, []string{"user"}) assert.EqualValues(t, constants.EnvData.DEFAULT_ROLES, []string{"user"})
assert.EqualValues(t, constants.PROTECTED_ROLES, []string{"admin"}) assert.EqualValues(t, constants.EnvData.PROTECTED_ROLES, []string{"admin"})
assert.EqualValues(t, constants.ALLOWED_ORIGINS, []string{"*"}) assert.EqualValues(t, constants.EnvData.ALLOWED_ORIGINS, []string{"*"})
} }

View File

@ -16,8 +16,8 @@ func TestResolvers(t *testing.T) {
} }
for dbType, dbURL := range databases { for dbType, dbURL := range databases {
constants.DATABASE_URL = dbURL constants.EnvData.DATABASE_URL = dbURL
constants.DATABASE_TYPE = dbType constants.EnvData.DATABASE_TYPE = dbType
db.InitDB() db.InitDB()
s := testSetup() s := testSetup()

View File

@ -70,7 +70,7 @@ func testSetup() TestSetup {
Password: "test", Password: "test",
} }
constants.ENV_PATH = "../../.env.sample" constants.EnvData.ENV_PATH = "../../.env.sample"
env.InitEnv() env.InitEnv()
session.InitSession() session.InitSession()

View File

@ -29,7 +29,7 @@ func updateUserTest(s TestSetup, t *testing.T) {
}) })
assert.NotNil(t, err, "unauthorized") assert.NotNil(t, err, "unauthorized")
req.Header.Add("x-authorizer-admin-secret", constants.ADMIN_SECRET) req.Header.Add("x-authorizer-admin-secret", constants.EnvData.ADMIN_SECRET)
_, err = resolvers.UpdateUser(ctx, model.UpdateUserInput{ _, err = resolvers.UpdateUser(ctx, model.UpdateUserInput{
ID: user.ID, ID: user.ID,
Roles: newRoles, Roles: newRoles,

View File

@ -22,7 +22,7 @@ func usersTest(s TestSetup, t *testing.T) {
users, err := resolvers.Users(ctx) users, err := resolvers.Users(ctx)
assert.NotNil(t, err, "unauthorized") assert.NotNil(t, err, "unauthorized")
req.Header.Add("x-authorizer-admin-secret", constants.ADMIN_SECRET) req.Header.Add("x-authorizer-admin-secret", constants.EnvData.ADMIN_SECRET)
users, err = resolvers.Users(ctx) users, err = resolvers.Users(ctx)
assert.Nil(t, err) assert.Nil(t, err)
rLen := len(users) rLen := len(users)

View File

@ -22,7 +22,7 @@ func TestIsValidEmail(t *testing.T) {
func TestIsValidOrigin(t *testing.T) { func TestIsValidOrigin(t *testing.T) {
// don't use portocal(http/https) for ALLOWED_ORIGINS while testing, // don't use portocal(http/https) for ALLOWED_ORIGINS while testing,
// as we trim them off while running the main function // as we trim them off while running the main function
constants.ALLOWED_ORIGINS = []string{"localhost:8080", "*.google.com", "*.google.in", "*abc.*"} constants.EnvData.ALLOWED_ORIGINS = []string{"localhost:8080", "*.google.com", "*.google.in", "*abc.*"}
assert.False(t, utils.IsValidOrigin("http://myapp.com"), "it should be invalid origin") assert.False(t, utils.IsValidOrigin("http://myapp.com"), "it should be invalid origin")
assert.False(t, utils.IsValidOrigin("http://appgoogle.com"), "it should be invalid origin") assert.False(t, utils.IsValidOrigin("http://appgoogle.com"), "it should be invalid origin")

View File

@ -23,7 +23,7 @@ func verificationRequestsTest(s TestSetup, t *testing.T) {
requests, err := resolvers.VerificationRequests(ctx) requests, err := resolvers.VerificationRequests(ctx)
assert.NotNil(t, err, "unauthorizer") assert.NotNil(t, err, "unauthorizer")
req.Header.Add("x-authorizer-admin-secret", constants.ADMIN_SECRET) req.Header.Add("x-authorizer-admin-secret", constants.EnvData.ADMIN_SECRET)
requests, err = resolvers.VerificationRequests(ctx) requests, err = resolvers.VerificationRequests(ctx)
assert.Nil(t, err) assert.Nil(t, err)

View File

@ -1,50 +1,61 @@
package constants package constants
// this constants are configured via env type EnvConst struct {
var ( ADMIN_SECRET string
ADMIN_SECRET = "" ENV string
ENV = "" ENV_PATH string
ENV_PATH = "" VERSION string
VERSION = "" DATABASE_TYPE string
DATABASE_TYPE = "" DATABASE_URL string
DATABASE_URL = "" DATABASE_NAME string
DATABASE_NAME = "" SMTP_HOST string
SMTP_HOST = "" SMTP_PORT string
SMTP_PORT = "" SENDER_EMAIL string
SENDER_EMAIL = "" SENDER_PASSWORD string
SENDER_PASSWORD = "" JWT_TYPE string
JWT_TYPE = "" JWT_SECRET string
JWT_SECRET = "" ALLOWED_ORIGINS []string
ALLOWED_ORIGINS = []string{} AUTHORIZER_URL string
AUTHORIZER_URL = "" APP_URL string
APP_URL = "" PORT string
PORT = "" REDIS_URL string
REDIS_URL = "" COOKIE_NAME string
IS_PROD = false ADMIN_COOKIE_NAME string
COOKIE_NAME = "" RESET_PASSWORD_URL string
RESET_PASSWORD_URL = "" ENCRYPTION_KEY string `json:"-"`
DISABLE_EMAIL_VERIFICATION = false IS_PROD bool
DISABLE_BASIC_AUTHENTICATION = false DISABLE_EMAIL_VERIFICATION bool
DISABLE_MAGIC_LINK_LOGIN = false DISABLE_BASIC_AUTHENTICATION bool
DISABLE_LOGIN_PAGE = false DISABLE_MAGIC_LINK_LOGIN bool
DISABLE_LOGIN_PAGE bool
// ROLES // ROLES
ROLES = []string{} ROLES []string
PROTECTED_ROLES = []string{} PROTECTED_ROLES []string
DEFAULT_ROLES = []string{} DEFAULT_ROLES []string
JWT_ROLE_CLAIM = "role" JWT_ROLE_CLAIM string
// OAuth login // OAuth login
GOOGLE_CLIENT_ID = "" GOOGLE_CLIENT_ID string
GOOGLE_CLIENT_SECRET = "" GOOGLE_CLIENT_SECRET string
GITHUB_CLIENT_ID = "" GITHUB_CLIENT_ID string
GITHUB_CLIENT_SECRET = "" GITHUB_CLIENT_SECRET string
FACEBOOK_CLIENT_ID = "" FACEBOOK_CLIENT_ID string
FACEBOOK_CLIENT_SECRET = "" FACEBOOK_CLIENT_SECRET string
TWITTER_CLIENT_ID = ""
TWITTER_CLIENT_SECRET = ""
// Org envs // Org envs
ORGANIZATION_NAME = "Authorizer" ORGANIZATION_NAME string
ORGANIZATION_LOGO = "https://authorizer.dev/images/logo.png" 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

@ -17,7 +17,7 @@ import (
func initArangodb() (arangoDriver.Database, error) { func initArangodb() (arangoDriver.Database, error) {
ctx := context.Background() ctx := context.Background()
conn, err := http.NewConnection(http.ConnectionConfig{ conn, err := http.NewConnection(http.ConnectionConfig{
Endpoints: []string{constants.DATABASE_URL}, Endpoints: []string{constants.EnvData.DATABASE_URL},
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@ -32,16 +32,16 @@ func initArangodb() (arangoDriver.Database, error) {
var arangodb driver.Database var arangodb driver.Database
arangodb_exists, err := arangoClient.DatabaseExists(nil, constants.DATABASE_NAME) arangodb_exists, err := arangoClient.DatabaseExists(nil, constants.EnvData.DATABASE_NAME)
if arangodb_exists { if arangodb_exists {
log.Println(constants.DATABASE_NAME + " db exists already") log.Println(constants.EnvData.DATABASE_NAME + " db exists already")
arangodb, err = arangoClient.Database(nil, constants.DATABASE_NAME) arangodb, err = arangoClient.Database(nil, constants.EnvData.DATABASE_NAME)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} else { } else {
arangodb, err = arangoClient.CreateDatabase(nil, constants.DATABASE_NAME, nil) arangodb, err = arangoClient.CreateDatabase(nil, constants.EnvData.DATABASE_NAME, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -100,5 +100,15 @@ func initArangodb() (arangoDriver.Database, error) {
Sparse: true, Sparse: true,
}) })
configCollectionExists, err := arangodb.CollectionExists(ctx, Collections.Config)
if configCollectionExists {
log.Println(Collections.Config + " collection exists already")
} else {
_, err = arangodb.CreateCollection(ctx, Collections.Config, nil)
if err != nil {
log.Println("error creating collection("+Collections.Config+"):", err)
}
}
return arangodb, err return arangodb, err
} }

161
server/db/config.go Normal file
View File

@ -0,0 +1,161 @@
package db
import (
"fmt"
"log"
"time"
arangoDriver "github.com/arangodb/go-driver"
"github.com/google/uuid"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
)
type Config struct {
Key string `json:"_key,omitempty" bson:"_key"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id"`
Config []byte `gorm:"type:text" json:"config" bson:"config"`
Hash string `gorm:"type:hash" json:"hash" bson:"hash"`
UpdatedAt int64 `gorm:"autoUpdateTime" json:"updated_at" bson:"updated_at"`
CreatedAt int64 `gorm:"autoCreateTime" json:"created_at" bson:"created_at"`
}
// AddConfig function to add config
func (mgr *manager) AddConfig(config Config) (Config, error) {
if config.ID == "" {
config.ID = uuid.New().String()
}
if IsORMSupported {
// copy id as value for fields required for mongodb & arangodb
config.Key = config.ID
result := mgr.sqlDB.Create(&config)
if result.Error != nil {
log.Println("error adding config:", result.Error)
return config, result.Error
}
}
if IsArangoDB {
config.CreatedAt = time.Now().Unix()
config.UpdatedAt = time.Now().Unix()
configCollection, _ := mgr.arangodb.Collection(nil, Collections.Config)
meta, err := configCollection.CreateDocument(arangoDriver.WithOverwrite(nil), config)
if err != nil {
log.Println("error adding config:", err)
return config, err
}
config.Key = meta.Key
config.ID = meta.ID.String()
}
if IsMongoDB {
config.CreatedAt = time.Now().Unix()
config.UpdatedAt = time.Now().Unix()
config.Key = config.ID
configCollection := mgr.mongodb.Collection(Collections.Config, options.Collection())
_, err := configCollection.InsertOne(nil, config)
if err != nil {
log.Println("error adding config:", err)
return config, err
}
}
return config, nil
}
// UpdateConfig function to update config
func (mgr *manager) UpdateConfig(config Config) (Config, error) {
config.UpdatedAt = time.Now().Unix()
if IsORMSupported {
result := mgr.sqlDB.Save(&config)
if result.Error != nil {
log.Println("error updating config:", result.Error)
return config, result.Error
}
}
if IsArangoDB {
collection, _ := mgr.arangodb.Collection(nil, Collections.Config)
meta, err := collection.UpdateDocument(nil, config.Key, config)
if err != nil {
log.Println("error updating config:", err)
return config, err
}
config.Key = meta.Key
config.ID = meta.ID.String()
}
if IsMongoDB {
configCollection := mgr.mongodb.Collection(Collections.Config, options.Collection())
_, err := configCollection.UpdateOne(nil, bson.M{"_id": bson.M{"$eq": config.ID}}, bson.M{"$set": config}, options.MergeUpdateOptions())
if err != nil {
log.Println("error updating config:", err)
return config, err
}
}
return config, nil
}
// GetConfig function to get config
func (mgr *manager) GetConfig() (Config, error) {
var config Config
if IsORMSupported {
result := mgr.sqlDB.First(&config)
if result.Error != nil {
return config, result.Error
}
}
if IsArangoDB {
query := fmt.Sprintf("FOR d in %s RETURN d", Collections.Config)
cursor, err := mgr.arangodb.Query(nil, query, nil)
if err != nil {
return config, err
}
defer cursor.Close()
for {
if !cursor.HasMore() {
if config.Key == "" {
return config, fmt.Errorf("config not found")
}
break
}
_, err := cursor.ReadDocument(nil, &config)
if err != nil {
return config, err
}
}
}
if IsMongoDB {
configCollection := mgr.mongodb.Collection(Collections.Config, options.Collection())
cursor, err := configCollection.Find(nil, bson.M{}, options.Find())
if err != nil {
return config, err
}
defer cursor.Close(nil)
for cursor.Next(nil) {
err := cursor.Decode(&config)
if err != nil {
return config, err
}
}
if config.ID == "" {
return config, fmt.Errorf("config not found")
}
}
return config, nil
}

View File

@ -29,6 +29,9 @@ type Manager interface {
GetVerificationByEmail(email string, identifier string) (VerificationRequest, error) GetVerificationByEmail(email string, identifier string) (VerificationRequest, error)
AddSession(session Session) error AddSession(session Session) error
DeleteUserSession(userId string) error DeleteUserSession(userId string) error
AddConfig(config Config) (Config, error)
UpdateConfig(config Config) (Config, error)
GetConfig() (Config, error)
} }
type manager struct { type manager struct {
@ -42,6 +45,7 @@ type CollectionList struct {
User string User string
VerificationRequest string VerificationRequest string
Session string Session string
Config string
} }
var ( var (
@ -54,6 +58,7 @@ var (
User: Prefix + "users", User: Prefix + "users",
VerificationRequest: Prefix + "verification_requests", VerificationRequest: Prefix + "verification_requests",
Session: Prefix + "sessions", Session: Prefix + "sessions",
Config: Prefix + "config",
} }
) )
@ -61,9 +66,9 @@ func InitDB() {
var sqlDB *gorm.DB var sqlDB *gorm.DB
var err error var err error
IsORMSupported = constants.DATABASE_TYPE != enum.Arangodb.String() && constants.DATABASE_TYPE != enum.Mongodb.String() IsORMSupported = constants.EnvData.DATABASE_TYPE != enum.Arangodb.String() && constants.EnvData.DATABASE_TYPE != enum.Mongodb.String()
IsArangoDB = constants.DATABASE_TYPE == enum.Arangodb.String() IsArangoDB = constants.EnvData.DATABASE_TYPE == enum.Arangodb.String()
IsMongoDB = constants.DATABASE_TYPE == enum.Mongodb.String() IsMongoDB = constants.EnvData.DATABASE_TYPE == enum.Mongodb.String()
// sql db orm config // sql db orm config
ormConfig := &gorm.Config{ ormConfig := &gorm.Config{
@ -72,20 +77,20 @@ func InitDB() {
}, },
} }
log.Println("db type:", constants.DATABASE_TYPE) log.Println("db type:", constants.EnvData.DATABASE_TYPE)
switch constants.DATABASE_TYPE { switch constants.EnvData.DATABASE_TYPE {
case enum.Postgres.String(): case enum.Postgres.String():
sqlDB, err = gorm.Open(postgres.Open(constants.DATABASE_URL), ormConfig) sqlDB, err = gorm.Open(postgres.Open(constants.EnvData.DATABASE_URL), ormConfig)
break break
case enum.Sqlite.String(): case enum.Sqlite.String():
sqlDB, err = gorm.Open(sqlite.Open(constants.DATABASE_URL), ormConfig) sqlDB, err = gorm.Open(sqlite.Open(constants.EnvData.DATABASE_URL), ormConfig)
break break
case enum.Mysql.String(): case enum.Mysql.String():
sqlDB, err = gorm.Open(mysql.Open(constants.DATABASE_URL), ormConfig) sqlDB, err = gorm.Open(mysql.Open(constants.EnvData.DATABASE_URL), ormConfig)
break break
case enum.SQLServer.String(): case enum.SQLServer.String():
sqlDB, err = gorm.Open(sqlserver.Open(constants.DATABASE_URL), ormConfig) sqlDB, err = gorm.Open(sqlserver.Open(constants.EnvData.DATABASE_URL), ormConfig)
break break
case enum.Arangodb.String(): case enum.Arangodb.String():
arangodb, err := initArangodb() arangodb, err := initArangodb()
@ -118,7 +123,7 @@ func InitDB() {
if err != nil { if err != nil {
log.Fatal("Failed to init sqlDB:", err) log.Fatal("Failed to init sqlDB:", err)
} else { } else {
sqlDB.AutoMigrate(&User{}, &VerificationRequest{}, &Session{}) sqlDB.AutoMigrate(&User{}, &VerificationRequest{}, &Session{}, &Config{})
} }
Mgr = &manager{ Mgr = &manager{
sqlDB: sqlDB, sqlDB: sqlDB,

View File

@ -12,7 +12,7 @@ import (
) )
func initMongodb() (*mongo.Database, error) { func initMongodb() (*mongo.Database, error) {
mongodbOptions := options.Client().ApplyURI(constants.DATABASE_URL) mongodbOptions := options.Client().ApplyURI(constants.EnvData.DATABASE_URL)
maxWait := time.Duration(5 * time.Second) maxWait := time.Duration(5 * time.Second)
mongodbOptions.ConnectTimeout = &maxWait mongodbOptions.ConnectTimeout = &maxWait
mongoClient, err := mongo.NewClient(mongodbOptions) mongoClient, err := mongo.NewClient(mongodbOptions)
@ -30,7 +30,7 @@ func initMongodb() (*mongo.Database, error) {
return nil, err return nil, err
} }
mongodb := mongoClient.Database(constants.DATABASE_NAME, options.Database()) mongodb := mongoClient.Database(constants.EnvData.DATABASE_NAME, options.Database())
mongodb.CreateCollection(ctx, Collections.User, options.CreateCollection()) mongodb.CreateCollection(ctx, Collections.User, options.CreateCollection())
userCollection := mongodb.Collection(Collections.User, options.Collection()) userCollection := mongodb.Collection(Collections.User, options.Collection())
@ -73,5 +73,7 @@ func initMongodb() (*mongo.Database, error) {
}, },
}, options.CreateIndexes()) }, options.CreateIndexes())
mongodb.CreateCollection(ctx, Collections.Config, options.CreateCollection())
return mongodb, nil return mongodb, nil
} }

View File

@ -43,7 +43,7 @@ func (mgr *manager) AddUser(user User) (User, error) {
} }
if user.Roles == "" { if user.Roles == "" {
user.Roles = constants.DEFAULT_ROLES[0] user.Roles = constants.EnvData.DEFAULT_ROLES[0]
} }
if IsORMSupported { if IsORMSupported {

View File

@ -27,7 +27,7 @@ type Sender struct {
} }
func NewSender() Sender { func NewSender() Sender {
return Sender{User: constants.SENDER_EMAIL, Password: constants.SENDER_PASSWORD} return Sender{User: constants.EnvData.SENDER_EMAIL, Password: constants.EnvData.SENDER_PASSWORD}
} }
func (sender Sender) SendMail(Dest []string, Subject, bodyMessage string) error { func (sender Sender) SendMail(Dest []string, Subject, bodyMessage string) error {
@ -35,8 +35,8 @@ func (sender Sender) SendMail(Dest []string, Subject, bodyMessage string) error
"To: " + strings.Join(Dest, ",") + "\n" + "To: " + strings.Join(Dest, ",") + "\n" +
"Subject: " + Subject + "\n" + bodyMessage "Subject: " + Subject + "\n" + bodyMessage
err := smtp.SendMail(constants.SMTP_HOST+":"+constants.SMTP_PORT, err := smtp.SendMail(constants.EnvData.SMTP_HOST+":"+constants.EnvData.SMTP_PORT,
smtp.PlainAuth("", sender.User, sender.Password, constants.SMTP_HOST), smtp.PlainAuth("", sender.User, sender.Password, constants.EnvData.SMTP_HOST),
sender.User, Dest, []byte(msg)) sender.User, Dest, []byte(msg))
if err != nil { if err != nil {

225
server/env/env.go vendored
View File

@ -7,6 +7,7 @@ import (
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/utils" "github.com/authorizerdev/authorizer/server/utils"
"github.com/google/uuid"
"github.com/joho/godotenv" "github.com/joho/godotenv"
) )
@ -20,163 +21,169 @@ var (
// InitEnv -> to initialize env and through error if required env are not present // InitEnv -> to initialize env and through error if required env are not present
func InitEnv() { func InitEnv() {
if constants.ENV_PATH == "" { if constants.EnvData.ENV_PATH == "" {
constants.ENV_PATH = `.env` constants.EnvData.ENV_PATH = `.env`
} }
if ARG_ENV_FILE != nil && *ARG_ENV_FILE != "" { if ARG_ENV_FILE != nil && *ARG_ENV_FILE != "" {
constants.ENV_PATH = *ARG_ENV_FILE constants.EnvData.ENV_PATH = *ARG_ENV_FILE
} }
err := godotenv.Load(constants.ENV_PATH) err := godotenv.Load(constants.EnvData.ENV_PATH)
if err != nil { if err != nil {
log.Printf("error loading %s file", constants.ENV_PATH) log.Printf("error loading %s file", constants.EnvData.ENV_PATH)
} }
if constants.ADMIN_SECRET == "" { if constants.EnvData.ADMIN_SECRET == "" {
constants.ADMIN_SECRET = os.Getenv("ADMIN_SECRET") constants.EnvData.ADMIN_SECRET = os.Getenv("ADMIN_SECRET")
if constants.ADMIN_SECRET == "" {
panic("root admin secret is required")
}
} }
if constants.ENV == "" { if constants.EnvData.DATABASE_TYPE == "" {
constants.ENV = os.Getenv("ENV") constants.EnvData.DATABASE_TYPE = os.Getenv("DATABASE_TYPE")
if constants.ENV == "" { log.Println(constants.EnvData.DATABASE_TYPE)
constants.ENV = "production"
}
if constants.ENV == "production" {
constants.IS_PROD = true
os.Setenv("GIN_MODE", "release")
} else {
constants.IS_PROD = false
}
}
if constants.DATABASE_TYPE == "" {
constants.DATABASE_TYPE = os.Getenv("DATABASE_TYPE")
log.Println(constants.DATABASE_TYPE)
if ARG_DB_TYPE != nil && *ARG_DB_TYPE != "" { if ARG_DB_TYPE != nil && *ARG_DB_TYPE != "" {
constants.DATABASE_TYPE = *ARG_DB_TYPE constants.EnvData.DATABASE_TYPE = *ARG_DB_TYPE
} }
if constants.DATABASE_TYPE == "" { if constants.EnvData.DATABASE_TYPE == "" {
panic("DATABASE_TYPE is required") panic("DATABASE_TYPE is required")
} }
} }
if constants.DATABASE_URL == "" { if constants.EnvData.DATABASE_URL == "" {
constants.DATABASE_URL = os.Getenv("DATABASE_URL") constants.EnvData.DATABASE_URL = os.Getenv("DATABASE_URL")
if ARG_DB_URL != nil && *ARG_DB_URL != "" { if ARG_DB_URL != nil && *ARG_DB_URL != "" {
constants.DATABASE_URL = *ARG_DB_URL constants.EnvData.DATABASE_URL = *ARG_DB_URL
} }
if constants.DATABASE_URL == "" { if constants.EnvData.DATABASE_URL == "" {
panic("DATABASE_URL is required") panic("DATABASE_URL is required")
} }
} }
if constants.DATABASE_NAME == "" { if constants.EnvData.DATABASE_NAME == "" {
constants.DATABASE_NAME = os.Getenv("DATABASE_NAME") constants.EnvData.DATABASE_NAME = os.Getenv("DATABASE_NAME")
if constants.DATABASE_NAME == "" { if constants.EnvData.DATABASE_NAME == "" {
constants.DATABASE_NAME = "authorizer" constants.EnvData.DATABASE_NAME = "authorizer"
} }
} }
if constants.SMTP_HOST == "" { if constants.EnvData.ENV == "" {
constants.SMTP_HOST = os.Getenv("SMTP_HOST") constants.EnvData.ENV = os.Getenv("ENV")
if constants.EnvData.ENV == "" {
constants.EnvData.ENV = "production"
} }
if constants.SMTP_PORT == "" { if constants.EnvData.ENV == "production" {
constants.SMTP_PORT = os.Getenv("SMTP_PORT") constants.EnvData.IS_PROD = true
} os.Setenv("GIN_MODE", "release")
} else {
if constants.SENDER_EMAIL == "" { constants.EnvData.IS_PROD = false
constants.SENDER_EMAIL = os.Getenv("SENDER_EMAIL")
}
if constants.SENDER_PASSWORD == "" {
constants.SENDER_PASSWORD = os.Getenv("SENDER_PASSWORD")
}
if constants.JWT_SECRET == "" {
constants.JWT_SECRET = os.Getenv("JWT_SECRET")
}
if constants.JWT_TYPE == "" {
constants.JWT_TYPE = os.Getenv("JWT_TYPE")
}
if constants.JWT_ROLE_CLAIM == "" {
constants.JWT_ROLE_CLAIM = os.Getenv("JWT_ROLE_CLAIM")
if constants.JWT_ROLE_CLAIM == "" {
constants.JWT_ROLE_CLAIM = "role"
} }
} }
if constants.AUTHORIZER_URL == "" { if constants.EnvData.SMTP_HOST == "" {
constants.AUTHORIZER_URL = strings.TrimSuffix(os.Getenv("AUTHORIZER_URL"), "/") constants.EnvData.SMTP_HOST = os.Getenv("SMTP_HOST")
}
if constants.EnvData.SMTP_PORT == "" {
constants.EnvData.SMTP_PORT = os.Getenv("SMTP_PORT")
}
if constants.EnvData.SENDER_EMAIL == "" {
constants.EnvData.SENDER_EMAIL = os.Getenv("SENDER_EMAIL")
}
if constants.EnvData.SENDER_PASSWORD == "" {
constants.EnvData.SENDER_PASSWORD = os.Getenv("SENDER_PASSWORD")
}
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 constants.EnvData.JWT_TYPE == "" {
constants.EnvData.JWT_TYPE = os.Getenv("JWT_TYPE")
if constants.EnvData.JWT_TYPE == "" {
constants.EnvData.JWT_TYPE = "HS256"
}
}
if constants.EnvData.JWT_ROLE_CLAIM == "" {
constants.EnvData.JWT_ROLE_CLAIM = os.Getenv("JWT_ROLE_CLAIM")
if constants.EnvData.JWT_ROLE_CLAIM == "" {
constants.EnvData.JWT_ROLE_CLAIM = "role"
}
}
if constants.EnvData.AUTHORIZER_URL == "" {
constants.EnvData.AUTHORIZER_URL = strings.TrimSuffix(os.Getenv("AUTHORIZER_URL"), "/")
if ARG_AUTHORIZER_URL != nil && *ARG_AUTHORIZER_URL != "" { if ARG_AUTHORIZER_URL != nil && *ARG_AUTHORIZER_URL != "" {
constants.AUTHORIZER_URL = *ARG_AUTHORIZER_URL constants.EnvData.AUTHORIZER_URL = *ARG_AUTHORIZER_URL
} }
} }
if constants.PORT == "" { if constants.EnvData.PORT == "" {
constants.PORT = os.Getenv("PORT") constants.EnvData.PORT = os.Getenv("PORT")
if constants.PORT == "" { if constants.EnvData.PORT == "" {
constants.PORT = "8080" constants.EnvData.PORT = "8080"
} }
} }
if constants.REDIS_URL == "" { if constants.EnvData.REDIS_URL == "" {
constants.REDIS_URL = os.Getenv("REDIS_URL") constants.EnvData.REDIS_URL = os.Getenv("REDIS_URL")
} }
if constants.COOKIE_NAME == "" { if constants.EnvData.COOKIE_NAME == "" {
constants.COOKIE_NAME = os.Getenv("COOKIE_NAME") constants.EnvData.COOKIE_NAME = os.Getenv("COOKIE_NAME")
if constants.EnvData.COOKIE_NAME == "" {
constants.EnvData.COOKIE_NAME = "authorizer"
}
} }
if constants.GOOGLE_CLIENT_ID == "" { if constants.EnvData.GOOGLE_CLIENT_ID == "" {
constants.GOOGLE_CLIENT_ID = os.Getenv("GOOGLE_CLIENT_ID") constants.EnvData.GOOGLE_CLIENT_ID = os.Getenv("GOOGLE_CLIENT_ID")
} }
if constants.GOOGLE_CLIENT_SECRET == "" { if constants.EnvData.GOOGLE_CLIENT_SECRET == "" {
constants.GOOGLE_CLIENT_SECRET = os.Getenv("GOOGLE_CLIENT_SECRET") constants.EnvData.GOOGLE_CLIENT_SECRET = os.Getenv("GOOGLE_CLIENT_SECRET")
} }
if constants.GITHUB_CLIENT_ID == "" { if constants.EnvData.GITHUB_CLIENT_ID == "" {
constants.GITHUB_CLIENT_ID = os.Getenv("GITHUB_CLIENT_ID") constants.EnvData.GITHUB_CLIENT_ID = os.Getenv("GITHUB_CLIENT_ID")
} }
if constants.GITHUB_CLIENT_SECRET == "" { if constants.EnvData.GITHUB_CLIENT_SECRET == "" {
constants.GITHUB_CLIENT_SECRET = os.Getenv("GITHUB_CLIENT_SECRET") constants.EnvData.GITHUB_CLIENT_SECRET = os.Getenv("GITHUB_CLIENT_SECRET")
} }
if constants.FACEBOOK_CLIENT_ID == "" { if constants.EnvData.FACEBOOK_CLIENT_ID == "" {
constants.FACEBOOK_CLIENT_ID = os.Getenv("FACEBOOK_CLIENT_ID") constants.EnvData.FACEBOOK_CLIENT_ID = os.Getenv("FACEBOOK_CLIENT_ID")
} }
if constants.FACEBOOK_CLIENT_SECRET == "" { if constants.EnvData.FACEBOOK_CLIENT_SECRET == "" {
constants.FACEBOOK_CLIENT_SECRET = os.Getenv("FACEBOOK_CLIENT_SECRET") constants.EnvData.FACEBOOK_CLIENT_SECRET = os.Getenv("FACEBOOK_CLIENT_SECRET")
} }
if constants.RESET_PASSWORD_URL == "" { if constants.EnvData.RESET_PASSWORD_URL == "" {
constants.RESET_PASSWORD_URL = strings.TrimPrefix(os.Getenv("RESET_PASSWORD_URL"), "/") constants.EnvData.RESET_PASSWORD_URL = strings.TrimPrefix(os.Getenv("RESET_PASSWORD_URL"), "/")
} }
constants.DISABLE_BASIC_AUTHENTICATION = os.Getenv("DISABLE_BASIC_AUTHENTICATION") == "true" constants.EnvData.DISABLE_BASIC_AUTHENTICATION = os.Getenv("DISABLE_BASIC_AUTHENTICATION") == "true"
constants.DISABLE_EMAIL_VERIFICATION = os.Getenv("DISABLE_EMAIL_VERIFICATION") == "true" constants.EnvData.DISABLE_EMAIL_VERIFICATION = os.Getenv("DISABLE_EMAIL_VERIFICATION") == "true"
constants.DISABLE_MAGIC_LINK_LOGIN = os.Getenv("DISABLE_MAGIC_LINK_LOGIN") == "true" constants.EnvData.DISABLE_MAGIC_LINK_LOGIN = os.Getenv("DISABLE_MAGIC_LINK_LOGIN") == "true"
constants.DISABLE_LOGIN_PAGE = os.Getenv("DISABLE_LOGIN_PAGE") == "true" constants.EnvData.DISABLE_LOGIN_PAGE = os.Getenv("DISABLE_LOGIN_PAGE") == "true"
if constants.SMTP_HOST == "" || constants.SENDER_EMAIL == "" || constants.SENDER_PASSWORD == "" { if constants.EnvData.SMTP_HOST == "" || constants.EnvData.SENDER_EMAIL == "" || constants.EnvData.SENDER_PASSWORD == "" {
constants.DISABLE_EMAIL_VERIFICATION = true constants.EnvData.DISABLE_EMAIL_VERIFICATION = true
constants.DISABLE_MAGIC_LINK_LOGIN = true constants.EnvData.DISABLE_MAGIC_LINK_LOGIN = true
} }
allowedOriginsSplit := strings.Split(os.Getenv("ALLOWED_ORIGINS"), ",") allowedOriginsSplit := strings.Split(os.Getenv("ALLOWED_ORIGINS"), ",")
@ -205,18 +212,10 @@ func InitEnv() {
allowedOrigins = []string{"*"} allowedOrigins = []string{"*"}
} }
constants.ALLOWED_ORIGINS = allowedOrigins constants.EnvData.ALLOWED_ORIGINS = allowedOrigins
if constants.JWT_TYPE == "" { if constants.EnvData.DISABLE_EMAIL_VERIFICATION {
constants.JWT_TYPE = "HS256" constants.EnvData.DISABLE_MAGIC_LINK_LOGIN = true
}
if constants.COOKIE_NAME == "" {
constants.COOKIE_NAME = "authorizer"
}
if constants.DISABLE_EMAIL_VERIFICATION {
constants.DISABLE_MAGIC_LINK_LOGIN = true
} }
rolesEnv := strings.TrimSpace(os.Getenv("ROLES")) rolesEnv := strings.TrimSpace(os.Getenv("ROLES"))
@ -256,19 +255,19 @@ func InitEnv() {
} }
} }
if len(roles) > 0 && len(defaultRoles) == 0 && len(defaultRoleSplit) > 0 { if len(roles) > 0 && len(defaultRoles) == 0 && len(defaultRolesEnv) > 0 {
panic(`Invalid DEFAULT_ROLE environment variable. It can be one from give ROLES environment variable value`) panic(`Invalid DEFAULT_ROLE environment variable. It can be one from give ROLES environment variable value`)
} }
constants.ROLES = roles constants.EnvData.ROLES = roles
constants.DEFAULT_ROLES = defaultRoles constants.EnvData.DEFAULT_ROLES = defaultRoles
constants.PROTECTED_ROLES = protectedRoles constants.EnvData.PROTECTED_ROLES = protectedRoles
if os.Getenv("ORGANIZATION_NAME") != "" { if os.Getenv("ORGANIZATION_NAME") != "" {
constants.ORGANIZATION_NAME = os.Getenv("ORGANIZATION_NAME") constants.EnvData.ORGANIZATION_NAME = os.Getenv("ORGANIZATION_NAME")
} }
if os.Getenv("ORGANIZATION_LOGO") != "" { if os.Getenv("ORGANIZATION_LOGO") != "" {
constants.ORGANIZATION_LOGO = os.Getenv("ORGANIZATION_LOGO") constants.EnvData.ORGANIZATION_LOGO = os.Getenv("ORGANIZATION_LOGO")
} }
} }

155
server/env/persist_env.go vendored Normal file
View File

@ -0,0 +1,155 @@
package env
import (
"encoding/json"
"log"
"os"
"reflect"
"strings"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/google/uuid"
)
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
encodedHash := utils.EncryptB64(hash)
configData, err := json.Marshal(constants.EnvData)
if err != nil {
return err
}
encryptedConfig, err := utils.EncryptAES(configData)
if err != nil {
return err
}
config = db.Config{
Hash: encodedHash,
Config: encryptedConfig,
}
db.Mgr.AddConfig(config)
} else {
// decrypt the config data from db
// decryption can be done using the hash stored in db
encryptionKey := config.Hash
decryptedEncryptionKey, err := utils.DecryptB64(encryptionKey)
if err != nil {
return err
}
constants.EnvData.ENCRYPTION_KEY = decryptedEncryptionKey
decryptedConfigs, err := utils.DecryptAES(config.Config)
if err != nil {
return err
}
// temp json to validate with env
var jsonData map[string]interface{}
err = json.Unmarshal(decryptedConfigs, &jsonData)
if err != nil {
return err
}
log.Println("=> persisted data:", jsonData)
// if env is changed via env file or OS env
// give that higher preference and update db, but we don't recommend it
hasChanged := false
for key, value := range jsonData {
fieldType := reflect.TypeOf(value).String()
// check only for derivative keys
// No need to check for ENCRYPTION_KEY which special key we use for encrypting config data
// as we have removed it from json
envValue := strings.TrimSpace(os.Getenv(key))
// env is not empty
if envValue != "" {
// check the type
// currently we have 3 types of env vars: string, bool, []string{}
if fieldType == "string" {
if value != envValue {
jsonData[key] = envValue
hasChanged = true
}
}
if fieldType == "bool" {
newValue := envValue == "true"
if value != newValue {
jsonData[key] = newValue
hasChanged = true
}
}
if fieldType == "[]interface {}" {
stringArr := []string{}
envStringArr := strings.Split(envValue, ",")
for _, v := range value.([]interface{}) {
stringArr = append(stringArr, v.(string))
}
if !utils.IsStringArrayEqual(stringArr, envStringArr) {
jsonData[key] = envStringArr
}
}
}
}
// handle derivative cases like disabling email verification & magic login
// in case SMTP is off but env is set to true
if jsonData["SMTP_HOST"] == "" || jsonData["SENDER_EMAIL"] == "" || jsonData["SENDER_PASSWORD"] == "" {
if !jsonData["DISABLE_EMAIL_VERIFICATION"].(bool) {
jsonData["DISABLE_EMAIL_VERIFICATION"] = true
hasChanged = true
}
if !jsonData["DISABLE_MAGIC_LINK_LOGIN"].(bool) {
jsonData["DISABLE_MAGIC_LINK_LOGIN"] = true
hasChanged = true
}
}
log.Println("has changed:", hasChanged)
if hasChanged {
jsonBytes, err := json.Marshal(jsonData)
if err != nil {
return err
}
err = json.Unmarshal(jsonBytes, &constants.EnvData)
if err != nil {
return err
}
}
configData, err := json.Marshal(constants.EnvData)
if err != nil {
return err
}
encryptedConfig, err := utils.EncryptAES(configData)
if err != nil {
return err
}
config.Config = encryptedConfig
_, err = db.Mgr.UpdateConfig(config)
if err != nil {
log.Println("error updating config:", err)
return err
}
}
return nil
}

View File

@ -1,7 +1,6 @@
package handlers package handlers
import ( import (
"encoding/base64"
"encoding/json" "encoding/json"
"log" "log"
"net/http" "net/http"
@ -30,17 +29,17 @@ func AppHandler() gin.HandlerFunc {
// return // return
// } // }
stateObj.AuthorizerURL = constants.AUTHORIZER_URL stateObj.AuthorizerURL = constants.EnvData.AUTHORIZER_URL
stateObj.RedirectURL = constants.AUTHORIZER_URL + "/app" stateObj.RedirectURL = constants.EnvData.AUTHORIZER_URL + "/app"
} else { } else {
decodedState, err := base64.StdEncoding.DecodeString(state) decodedState, err := utils.DecryptB64(state)
if err != nil { if err != nil {
c.JSON(400, gin.H{"error": "[unable to decode state] invalid state"}) c.JSON(400, gin.H{"error": "[unable to decode state] invalid state"})
return return
} }
err = json.Unmarshal(decodedState, &stateObj) err = json.Unmarshal([]byte(decodedState), &stateObj)
if err != nil { if err != nil {
c.JSON(400, gin.H{"error": "[unable to parse state] invalid state"}) c.JSON(400, gin.H{"error": "[unable to parse state] invalid state"})
return return
@ -60,7 +59,7 @@ func AppHandler() gin.HandlerFunc {
} }
// validate host and domain of authorizer url // validate host and domain of authorizer url
if strings.TrimSuffix(stateObj.AuthorizerURL, "/") != constants.AUTHORIZER_URL { if strings.TrimSuffix(stateObj.AuthorizerURL, "/") != constants.EnvData.AUTHORIZER_URL {
c.JSON(400, gin.H{"error": "invalid host url"}) c.JSON(400, gin.H{"error": "invalid host url"})
return return
} }
@ -77,8 +76,8 @@ func AppHandler() gin.HandlerFunc {
"data": map[string]string{ "data": map[string]string{
"authorizerURL": stateObj.AuthorizerURL, "authorizerURL": stateObj.AuthorizerURL,
"redirectURL": stateObj.RedirectURL, "redirectURL": stateObj.RedirectURL,
"organizationName": constants.ORGANIZATION_NAME, "organizationName": constants.EnvData.ORGANIZATION_NAME,
"organizationLogo": constants.ORGANIZATION_LOGO, "organizationLogo": constants.EnvData.ORGANIZATION_LOGO,
}, },
}) })
} }

View File

@ -10,7 +10,7 @@ import (
func DashboardHandler() gin.HandlerFunc { func DashboardHandler() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
isOnboardingCompleted := false isOnboardingCompleted := false
if constants.ADMIN_SECRET != "" && constants.DATABASE_TYPE != "" && constants.DATABASE_URL != "" { if constants.EnvData.ADMIN_SECRET != "" && constants.EnvData.DATABASE_TYPE != "" && constants.EnvData.DATABASE_URL != "" {
isOnboardingCompleted = true isOnboardingCompleted = true
} }

View File

@ -195,7 +195,7 @@ func OAuthCallbackHandler() gin.HandlerFunc {
// make sure inputRoles don't include protected roles // make sure inputRoles don't include protected roles
hasProtectedRole := false hasProtectedRole := false
for _, ir := range inputRoles { for _, ir := range inputRoles {
if utils.StringSliceContains(constants.PROTECTED_ROLES, ir) { if utils.StringSliceContains(constants.EnvData.PROTECTED_ROLES, ir) {
hasProtectedRole = true hasProtectedRole = true
} }
} }
@ -238,7 +238,7 @@ func OAuthCallbackHandler() gin.HandlerFunc {
// check if it contains protected unassigned role // check if it contains protected unassigned role
hasProtectedRole := false hasProtectedRole := false
for _, ur := range unasignedRoles { for _, ur := range unasignedRoles {
if utils.StringSliceContains(constants.PROTECTED_ROLES, ur) { if utils.StringSliceContains(constants.EnvData.PROTECTED_ROLES, ur) {
hasProtectedRole = true hasProtectedRole = true
} }
} }

View File

@ -34,14 +34,14 @@ func OAuthLoginHandler() gin.HandlerFunc {
// use protected roles verification for admin login only. // use protected roles verification for admin login only.
// though if not associated with user, it will be rejected from oauth_callback // though if not associated with user, it will be rejected from oauth_callback
if !utils.IsValidRoles(append([]string{}, append(constants.ROLES, constants.PROTECTED_ROLES...)...), rolesSplit) { if !utils.IsValidRoles(append([]string{}, append(constants.EnvData.ROLES, constants.EnvData.PROTECTED_ROLES...)...), rolesSplit) {
c.JSON(400, gin.H{ c.JSON(400, gin.H{
"error": "invalid role", "error": "invalid role",
}) })
return return
} }
} else { } else {
roles = strings.Join(constants.DEFAULT_ROLES, ",") roles = strings.Join(constants.EnvData.DEFAULT_ROLES, ",")
} }
uuid := uuid.New() uuid := uuid.New()
@ -57,7 +57,7 @@ func OAuthLoginHandler() gin.HandlerFunc {
} }
session.SetSocailLoginState(oauthStateString, enum.Google.String()) session.SetSocailLoginState(oauthStateString, enum.Google.String())
// during the init of OAuthProvider authorizer url might be empty // during the init of OAuthProvider authorizer url might be empty
oauth.OAuthProviders.GoogleConfig.RedirectURL = constants.AUTHORIZER_URL + "/oauth_callback/google" oauth.OAuthProviders.GoogleConfig.RedirectURL = constants.EnvData.AUTHORIZER_URL + "/oauth_callback/google"
url := oauth.OAuthProviders.GoogleConfig.AuthCodeURL(oauthStateString) url := oauth.OAuthProviders.GoogleConfig.AuthCodeURL(oauthStateString)
c.Redirect(http.StatusTemporaryRedirect, url) c.Redirect(http.StatusTemporaryRedirect, url)
case enum.Github.String(): case enum.Github.String():
@ -66,7 +66,7 @@ func OAuthLoginHandler() gin.HandlerFunc {
break break
} }
session.SetSocailLoginState(oauthStateString, enum.Github.String()) session.SetSocailLoginState(oauthStateString, enum.Github.String())
oauth.OAuthProviders.GithubConfig.RedirectURL = constants.AUTHORIZER_URL + "/oauth_callback/github" oauth.OAuthProviders.GithubConfig.RedirectURL = constants.EnvData.AUTHORIZER_URL + "/oauth_callback/github"
url := oauth.OAuthProviders.GithubConfig.AuthCodeURL(oauthStateString) url := oauth.OAuthProviders.GithubConfig.AuthCodeURL(oauthStateString)
c.Redirect(http.StatusTemporaryRedirect, url) c.Redirect(http.StatusTemporaryRedirect, url)
case enum.Facebook.String(): case enum.Facebook.String():
@ -75,7 +75,7 @@ func OAuthLoginHandler() gin.HandlerFunc {
break break
} }
session.SetSocailLoginState(oauthStateString, enum.Facebook.String()) session.SetSocailLoginState(oauthStateString, enum.Facebook.String())
oauth.OAuthProviders.FacebookConfig.RedirectURL = constants.AUTHORIZER_URL + "/oauth_callback/facebook" oauth.OAuthProviders.FacebookConfig.RedirectURL = constants.EnvData.AUTHORIZER_URL + "/oauth_callback/facebook"
url := oauth.OAuthProviders.FacebookConfig.AuthCodeURL(oauthStateString) url := oauth.OAuthProviders.FacebookConfig.AuthCodeURL(oauthStateString)
c.Redirect(http.StatusTemporaryRedirect, url) c.Redirect(http.StatusTemporaryRedirect, url)
default: default:

View File

@ -22,10 +22,12 @@ func main() {
env.ARG_ENV_FILE = flag.String("env_file", "", "Env file path") env.ARG_ENV_FILE = flag.String("env_file", "", "Env file path")
flag.Parse() flag.Parse()
constants.VERSION = VERSION constants.EnvData.VERSION = VERSION
env.InitEnv() env.InitEnv()
db.InitDB() db.InitDB()
env.PersistEnv()
session.InitSession() session.InitSession()
oauth.InitOAuth() oauth.InitOAuth()
utils.InitServer() utils.InitServer()
@ -35,7 +37,7 @@ func main() {
router.LoadHTMLGlob("templates/*") router.LoadHTMLGlob("templates/*")
// login page app related routes. // 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 we put them in router file then tests would fail as templates or build path will be different
if !constants.DISABLE_LOGIN_PAGE { if !constants.EnvData.DISABLE_LOGIN_PAGE {
app := router.Group("/app") app := router.Group("/app")
{ {
app.Static("/build", "app/build") app.Static("/build", "app/build")
@ -50,5 +52,5 @@ func main() {
app.GET("/", handlers.DashboardHandler()) app.GET("/", handlers.DashboardHandler())
} }
router.Run(":" + constants.PORT) router.Run(":" + constants.EnvData.PORT)
} }

View File

@ -11,10 +11,10 @@ import (
func GinContextToContextMiddleware() gin.HandlerFunc { func GinContextToContextMiddleware() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
if constants.AUTHORIZER_URL == "" { if constants.EnvData.AUTHORIZER_URL == "" {
url := location.Get(c) url := location.Get(c)
constants.AUTHORIZER_URL = url.Scheme + "://" + c.Request.Host constants.EnvData.AUTHORIZER_URL = url.Scheme + "://" + c.Request.Host
log.Println("authorizer url:", constants.AUTHORIZER_URL) log.Println("authorizer url:", constants.EnvData.AUTHORIZER_URL)
} }
ctx := context.WithValue(c.Request.Context(), "GinContextKey", c) ctx := context.WithValue(c.Request.Context(), "GinContextKey", c)
c.Request = c.Request.WithContext(ctx) c.Request = c.Request.WithContext(ctx)

View File

@ -9,7 +9,7 @@ import (
func CORSMiddleware() gin.HandlerFunc { func CORSMiddleware() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
origin := c.Request.Header.Get("Origin") origin := c.Request.Header.Get("Origin")
constants.APP_URL = origin constants.EnvData.APP_URL = origin
if utils.IsValidOrigin(origin) { if utils.IsValidOrigin(origin) {
c.Writer.Header().Set("Access-Control-Allow-Origin", origin) c.Writer.Header().Set("Access-Control-Allow-Origin", origin)

View File

@ -28,33 +28,33 @@ var (
func InitOAuth() { func InitOAuth() {
ctx := context.Background() ctx := context.Background()
if constants.GOOGLE_CLIENT_ID != "" && constants.GOOGLE_CLIENT_SECRET != "" { if constants.EnvData.GOOGLE_CLIENT_ID != "" && constants.EnvData.GOOGLE_CLIENT_SECRET != "" {
p, err := oidc.NewProvider(ctx, "https://accounts.google.com") p, err := oidc.NewProvider(ctx, "https://accounts.google.com")
if err != nil { if err != nil {
log.Fatalln("error creating oidc provider for google:", err) log.Fatalln("error creating oidc provider for google:", err)
} }
OIDCProviders.GoogleOIDC = p OIDCProviders.GoogleOIDC = p
OAuthProviders.GoogleConfig = &oauth2.Config{ OAuthProviders.GoogleConfig = &oauth2.Config{
ClientID: constants.GOOGLE_CLIENT_ID, ClientID: constants.EnvData.GOOGLE_CLIENT_ID,
ClientSecret: constants.GOOGLE_CLIENT_SECRET, ClientSecret: constants.EnvData.GOOGLE_CLIENT_SECRET,
RedirectURL: constants.AUTHORIZER_URL + "/oauth_callback/google", RedirectURL: constants.EnvData.AUTHORIZER_URL + "/oauth_callback/google",
Endpoint: OIDCProviders.GoogleOIDC.Endpoint(), Endpoint: OIDCProviders.GoogleOIDC.Endpoint(),
Scopes: []string{oidc.ScopeOpenID, "profile", "email"}, Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
} }
} }
if constants.GITHUB_CLIENT_ID != "" && constants.GITHUB_CLIENT_SECRET != "" { if constants.EnvData.GITHUB_CLIENT_ID != "" && constants.EnvData.GITHUB_CLIENT_SECRET != "" {
OAuthProviders.GithubConfig = &oauth2.Config{ OAuthProviders.GithubConfig = &oauth2.Config{
ClientID: constants.GITHUB_CLIENT_ID, ClientID: constants.EnvData.GITHUB_CLIENT_ID,
ClientSecret: constants.GITHUB_CLIENT_SECRET, ClientSecret: constants.EnvData.GITHUB_CLIENT_SECRET,
RedirectURL: constants.AUTHORIZER_URL + "/oauth_callback/github", RedirectURL: constants.EnvData.AUTHORIZER_URL + "/oauth_callback/github",
Endpoint: githubOAuth2.Endpoint, Endpoint: githubOAuth2.Endpoint,
} }
} }
if constants.FACEBOOK_CLIENT_ID != "" && constants.FACEBOOK_CLIENT_SECRET != "" { if constants.EnvData.FACEBOOK_CLIENT_ID != "" && constants.EnvData.FACEBOOK_CLIENT_SECRET != "" {
OAuthProviders.FacebookConfig = &oauth2.Config{ OAuthProviders.FacebookConfig = &oauth2.Config{
ClientID: constants.FACEBOOK_CLIENT_ID, ClientID: constants.EnvData.FACEBOOK_CLIENT_ID,
ClientSecret: constants.FACEBOOK_CLIENT_SECRET, ClientSecret: constants.EnvData.FACEBOOK_CLIENT_SECRET,
RedirectURL: constants.AUTHORIZER_URL + "/oauth_callback/facebook", RedirectURL: constants.EnvData.AUTHORIZER_URL + "/oauth_callback/facebook",
Endpoint: facebookOAuth2.Endpoint, Endpoint: facebookOAuth2.Endpoint,
Scopes: []string{"public_profile", "email"}, Scopes: []string{"public_profile", "email"},
} }

View File

@ -20,7 +20,7 @@ func AdminLoginResolver(ctx context.Context, params model.AdminLoginInput) (*mod
log.Println("=> error:", err) log.Println("=> error:", err)
return res, err return res, err
} }
if params.AdminSecret != constants.ADMIN_SECRET { if params.AdminSecret != constants.EnvData.ADMIN_SECRET {
return nil, fmt.Errorf(`invalid admin secret`) return nil, fmt.Errorf(`invalid admin secret`)
} }

View File

@ -20,7 +20,7 @@ func ForgotPassword(ctx context.Context, params model.ForgotPasswordInput) (*mod
if err != nil { if err != nil {
return res, err return res, err
} }
if constants.DISABLE_BASIC_AUTHENTICATION { if constants.EnvData.DISABLE_BASIC_AUTHENTICATION {
return res, fmt.Errorf(`basic authentication is disabled for this instance`) return res, fmt.Errorf(`basic authentication is disabled for this instance`)
} }
host := gc.Request.Host host := gc.Request.Host

View File

@ -22,7 +22,7 @@ func Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, e
return res, err return res, err
} }
if constants.DISABLE_BASIC_AUTHENTICATION { if constants.EnvData.DISABLE_BASIC_AUTHENTICATION {
return res, fmt.Errorf(`basic authentication is disabled for this instance`) return res, fmt.Errorf(`basic authentication is disabled for this instance`)
} }
@ -46,7 +46,7 @@ func Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, e
log.Println("compare password error:", err) log.Println("compare password error:", err)
return res, fmt.Errorf(`invalid password`) return res, fmt.Errorf(`invalid password`)
} }
roles := constants.DEFAULT_ROLES roles := constants.EnvData.DEFAULT_ROLES
currentRoles := strings.Split(user.Roles, ",") currentRoles := strings.Split(user.Roles, ",")
if len(params.Roles) > 0 { if len(params.Roles) > 0 {
if !utils.IsValidRoles(currentRoles, params.Roles) { if !utils.IsValidRoles(currentRoles, params.Roles) {

View File

@ -17,7 +17,7 @@ import (
func MagicLinkLogin(ctx context.Context, params model.MagicLinkLoginInput) (*model.Response, error) { func MagicLinkLogin(ctx context.Context, params model.MagicLinkLoginInput) (*model.Response, error) {
var res *model.Response var res *model.Response
if constants.DISABLE_MAGIC_LINK_LOGIN { if constants.EnvData.DISABLE_MAGIC_LINK_LOGIN {
return res, fmt.Errorf(`magic link login is disabled for this instance`) return res, fmt.Errorf(`magic link login is disabled for this instance`)
} }
@ -41,13 +41,13 @@ func MagicLinkLogin(ctx context.Context, params model.MagicLinkLoginInput) (*mod
// define roles for new user // define roles for new user
if len(params.Roles) > 0 { if len(params.Roles) > 0 {
// check if roles exists // check if roles exists
if !utils.IsValidRoles(constants.ROLES, params.Roles) { if !utils.IsValidRoles(constants.EnvData.ROLES, params.Roles) {
return res, fmt.Errorf(`invalid roles`) return res, fmt.Errorf(`invalid roles`)
} else { } else {
inputRoles = params.Roles inputRoles = params.Roles
} }
} else { } else {
inputRoles = constants.DEFAULT_ROLES inputRoles = constants.EnvData.DEFAULT_ROLES
} }
user.Roles = strings.Join(inputRoles, ",") user.Roles = strings.Join(inputRoles, ",")
@ -72,7 +72,7 @@ func MagicLinkLogin(ctx context.Context, params model.MagicLinkLoginInput) (*mod
// check if it contains protected unassigned role // check if it contains protected unassigned role
hasProtectedRole := false hasProtectedRole := false
for _, ur := range unasignedRoles { for _, ur := range unasignedRoles {
if utils.StringSliceContains(constants.PROTECTED_ROLES, ur) { if utils.StringSliceContains(constants.EnvData.PROTECTED_ROLES, ur) {
hasProtectedRole = true hasProtectedRole = true
} }
} }
@ -98,7 +98,7 @@ func MagicLinkLogin(ctx context.Context, params model.MagicLinkLoginInput) (*mod
} }
} }
if !constants.DISABLE_EMAIL_VERIFICATION { if !constants.EnvData.DISABLE_EMAIL_VERIFICATION {
// insert verification request // insert verification request
verificationType := enum.MagicLinkLogin.String() verificationType := enum.MagicLinkLogin.String()
token, err := utils.CreateVerificationToken(params.Email, verificationType) token, err := utils.CreateVerificationToken(params.Email, verificationType)

View File

@ -14,7 +14,7 @@ import (
func ResetPassword(ctx context.Context, params model.ResetPasswordInput) (*model.Response, error) { func ResetPassword(ctx context.Context, params model.ResetPasswordInput) (*model.Response, error) {
var res *model.Response var res *model.Response
if constants.DISABLE_BASIC_AUTHENTICATION { if constants.EnvData.DISABLE_BASIC_AUTHENTICATION {
return res, fmt.Errorf(`basic authentication is disabled for this instance`) return res, fmt.Errorf(`basic authentication is disabled for this instance`)
} }

View File

@ -45,7 +45,7 @@ func Session(ctx context.Context, roles []string) (*model.AuthResponse, error) {
expiresTimeObj := time.Unix(expiresAt, 0) expiresTimeObj := time.Unix(expiresAt, 0)
currentTimeObj := time.Now() currentTimeObj := time.Now()
claimRoleInterface := claim[constants.JWT_ROLE_CLAIM].([]interface{}) claimRoleInterface := claim[constants.EnvData.JWT_ROLE_CLAIM].([]interface{})
claimRoles := make([]string, len(claimRoleInterface)) claimRoles := make([]string, len(claimRoleInterface))
for i, v := range claimRoleInterface { for i, v := range claimRoleInterface {
claimRoles[i] = v.(string) claimRoles[i] = v.(string)

View File

@ -22,7 +22,7 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
return res, err return res, err
} }
if constants.DISABLE_BASIC_AUTHENTICATION { if constants.EnvData.DISABLE_BASIC_AUTHENTICATION {
return res, fmt.Errorf(`basic authentication is disabled for this instance`) return res, fmt.Errorf(`basic authentication is disabled for this instance`)
} }
if params.ConfirmPassword != params.Password { if params.ConfirmPassword != params.Password {
@ -52,13 +52,13 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
if len(params.Roles) > 0 { if len(params.Roles) > 0 {
// check if roles exists // check if roles exists
if !utils.IsValidRoles(constants.ROLES, params.Roles) { if !utils.IsValidRoles(constants.EnvData.ROLES, params.Roles) {
return res, fmt.Errorf(`invalid roles`) return res, fmt.Errorf(`invalid roles`)
} else { } else {
inputRoles = params.Roles inputRoles = params.Roles
} }
} else { } else {
inputRoles = constants.DEFAULT_ROLES inputRoles = constants.EnvData.DEFAULT_ROLES
} }
user := db.User{ user := db.User{
@ -103,7 +103,7 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
} }
user.SignupMethods = enum.BasicAuth.String() user.SignupMethods = enum.BasicAuth.String()
if constants.DISABLE_EMAIL_VERIFICATION { if constants.EnvData.DISABLE_EMAIL_VERIFICATION {
now := time.Now().Unix() now := time.Now().Unix()
user.EmailVerifiedAt = &now user.EmailVerifiedAt = &now
} }
@ -115,7 +115,7 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
roles := strings.Split(user.Roles, ",") roles := strings.Split(user.Roles, ",")
userToReturn := utils.GetResponseUserData(user) userToReturn := utils.GetResponseUserData(user)
if !constants.DISABLE_EMAIL_VERIFICATION { if !constants.EnvData.DISABLE_EMAIL_VERIFICATION {
// insert verification request // insert verification request
verificationType := enum.BasicAuthSignup.String() verificationType := enum.BasicAuthSignup.String()
token, err := utils.CreateVerificationToken(params.Email, verificationType) token, err := utils.CreateVerificationToken(params.Email, verificationType)

View File

@ -112,7 +112,7 @@ func UpdateUser(ctx context.Context, params model.UpdateUserInput) (*model.User,
inputRoles = append(inputRoles, *item) inputRoles = append(inputRoles, *item)
} }
if !utils.IsValidRoles(append([]string{}, append(constants.ROLES, constants.PROTECTED_ROLES...)...), inputRoles) { if !utils.IsValidRoles(append([]string{}, append(constants.EnvData.ROLES, constants.EnvData.PROTECTED_ROLES...)...), inputRoles) {
return res, fmt.Errorf("invalid list of roles") return res, fmt.Errorf("invalid list of roles")
} }

View File

@ -95,9 +95,9 @@ func RemoveSocialLoginState(key string) {
} }
func InitSession() { func InitSession() {
if constants.REDIS_URL != "" { if constants.EnvData.REDIS_URL != "" {
log.Println("using redis store to save sessions") log.Println("using redis store to save sessions")
opt, err := redis.ParseURL(constants.REDIS_URL) opt, err := redis.ParseURL(constants.EnvData.REDIS_URL)
if err != nil { if err != nil {
log.Fatalln("Error parsing redis url:", err) log.Fatalln("Error parsing redis url:", err)
} }

View File

@ -17,7 +17,7 @@ import (
) )
func CreateAuthToken(user db.User, tokenType enum.TokenType, roles []string) (string, int64, error) { func CreateAuthToken(user db.User, tokenType enum.TokenType, roles []string) (string, int64, error) {
t := jwt.New(jwt.GetSigningMethod(constants.JWT_TYPE)) t := jwt.New(jwt.GetSigningMethod(constants.EnvData.JWT_TYPE))
expiryBound := time.Hour expiryBound := time.Hour
if tokenType == enum.RefreshToken { if tokenType == enum.RefreshToken {
// expires in 1 year // expires in 1 year
@ -36,7 +36,7 @@ func CreateAuthToken(user db.User, tokenType enum.TokenType, roles []string) (st
"iat": time.Now().Unix(), "iat": time.Now().Unix(),
"token_type": tokenType.String(), "token_type": tokenType.String(),
"allowed_roles": strings.Split(user.Roles, ","), "allowed_roles": strings.Split(user.Roles, ","),
constants.JWT_ROLE_CLAIM: roles, constants.EnvData.JWT_ROLE_CLAIM: roles,
} }
for k, v := range userMap { for k, v := range userMap {
@ -77,7 +77,7 @@ func CreateAuthToken(user db.User, tokenType enum.TokenType, roles []string) (st
t.Claims = customClaims t.Claims = customClaims
token, err := t.SignedString([]byte(constants.JWT_SECRET)) token, err := t.SignedString([]byte(constants.EnvData.JWT_SECRET))
if err != nil { if err != nil {
return "", 0, err return "", 0, err
} }
@ -89,7 +89,6 @@ func GetAuthToken(gc *gin.Context) (string, error) {
token, err := GetCookie(gc) token, err := GetCookie(gc)
if err != nil || token == "" { if err != nil || token == "" {
// try to check in auth header for cookie // try to check in auth header for cookie
log.Println("cookie not found checking headers")
auth := gc.Request.Header.Get("Authorization") auth := gc.Request.Header.Get("Authorization")
if auth == "" { if auth == "" {
return "", fmt.Errorf(`unauthorized`) return "", fmt.Errorf(`unauthorized`)
@ -105,7 +104,7 @@ func VerifyAuthToken(token string) (map[string]interface{}, error) {
claims := jwt.MapClaims{} claims := jwt.MapClaims{}
_, err := jwt.ParseWithClaims(token, claims, func(token *jwt.Token) (interface{}, error) { _, err := jwt.ParseWithClaims(token, claims, func(token *jwt.Token) (interface{}, error) {
return []byte(constants.JWT_SECRET), nil return []byte(constants.EnvData.JWT_SECRET), nil
}) })
if err != nil { if err != nil {
return res, err return res, err
@ -126,7 +125,7 @@ func VerifyAuthToken(token string) (map[string]interface{}, error) {
} }
func CreateAdminAuthToken(tokenType enum.TokenType, c *gin.Context) (string, int64, error) { func CreateAdminAuthToken(tokenType enum.TokenType, c *gin.Context) (string, int64, error) {
t := jwt.New(jwt.GetSigningMethod(constants.JWT_TYPE)) t := jwt.New(jwt.GetSigningMethod(constants.EnvData.JWT_TYPE))
expiryBound := time.Hour expiryBound := time.Hour
if tokenType == enum.RefreshToken { if tokenType == enum.RefreshToken {
// expires in 1 year // expires in 1 year
@ -146,9 +145,23 @@ func CreateAdminAuthToken(tokenType enum.TokenType, c *gin.Context) (string, int
t.Claims = customClaims t.Claims = customClaims
token, err := t.SignedString([]byte(constants.JWT_SECRET)) token, err := t.SignedString([]byte(constants.EnvData.JWT_SECRET))
if err != nil { if err != nil {
return "", 0, err return "", 0, err
} }
return token, expiresAt, nil return token, expiresAt, nil
} }
func GetAdminAuthToken(gc *gin.Context) (string, error) {
token, err := GetAdminCookie(gc)
if err != nil || token == "" {
// try to check in auth header for cookie
auth := gc.Request.Header.Get("Authorization")
if auth == "" {
return "", fmt.Errorf(`unauthorized`)
}
token = strings.TrimPrefix(auth, "Bearer ")
}
return token, nil
}

View File

@ -10,21 +10,21 @@ import (
func SetCookie(gc *gin.Context, token string) { func SetCookie(gc *gin.Context, token string) {
secure := true secure := true
httpOnly := true httpOnly := true
host, _ := GetHostParts(constants.AUTHORIZER_URL) host, _ := GetHostParts(constants.EnvData.AUTHORIZER_URL)
domain := GetDomainName(constants.AUTHORIZER_URL) domain := GetDomainName(constants.EnvData.AUTHORIZER_URL)
if domain != "localhost" { if domain != "localhost" {
domain = "." + domain domain = "." + domain
} }
gc.SetSameSite(http.SameSiteNoneMode) gc.SetSameSite(http.SameSiteNoneMode)
gc.SetCookie(constants.COOKIE_NAME, token, 3600, "/", host, secure, httpOnly) gc.SetCookie(constants.EnvData.COOKIE_NAME, token, 3600, "/", host, secure, httpOnly)
gc.SetCookie(constants.COOKIE_NAME+"-client", token, 3600, "/", domain, secure, httpOnly) gc.SetCookie(constants.EnvData.COOKIE_NAME+"-client", token, 3600, "/", domain, secure, httpOnly)
} }
func GetCookie(gc *gin.Context) (string, error) { func GetCookie(gc *gin.Context) (string, error) {
cookie, err := gc.Request.Cookie(constants.COOKIE_NAME) cookie, err := gc.Request.Cookie(constants.EnvData.COOKIE_NAME)
if err != nil { if err != nil {
cookie, err = gc.Request.Cookie(constants.COOKIE_NAME + "-client") cookie, err = gc.Request.Cookie(constants.EnvData.COOKIE_NAME + "-client")
if err != nil { if err != nil {
return "", err return "", err
} }
@ -37,29 +37,37 @@ func DeleteCookie(gc *gin.Context) {
secure := true secure := true
httpOnly := true httpOnly := true
host, _ := GetHostParts(constants.AUTHORIZER_URL) host, _ := GetHostParts(constants.EnvData.AUTHORIZER_URL)
domain := GetDomainName(constants.AUTHORIZER_URL) domain := GetDomainName(constants.EnvData.AUTHORIZER_URL)
if domain != "localhost" { if domain != "localhost" {
domain = "." + domain domain = "." + domain
} }
gc.SetSameSite(http.SameSiteNoneMode) gc.SetSameSite(http.SameSiteNoneMode)
gc.SetCookie(constants.COOKIE_NAME, "", -1, "/", host, secure, httpOnly) gc.SetCookie(constants.EnvData.COOKIE_NAME, "", -1, "/", host, secure, httpOnly)
gc.SetCookie(constants.COOKIE_NAME+"-client", "", -1, "/", domain, secure, httpOnly) gc.SetCookie(constants.EnvData.COOKIE_NAME+"-client", "", -1, "/", domain, secure, httpOnly)
} }
func SetAdminCookie(gc *gin.Context, token string) { func SetAdminCookie(gc *gin.Context, token string) {
secure := true secure := true
httpOnly := true httpOnly := true
host, _ := GetHostParts(constants.AUTHORIZER_URL) host, _ := GetHostParts(constants.EnvData.AUTHORIZER_URL)
gc.SetCookie("authorizer-admin", token, 3600, "/", host, secure, httpOnly) gc.SetCookie(constants.EnvData.ADMIN_COOKIE_NAME, token, 3600, "/", host, secure, httpOnly)
}
func GetAdminCookie(gc *gin.Context) (string, error) {
cookie, err := gc.Request.Cookie(constants.EnvData.ADMIN_COOKIE_NAME)
if err != nil {
return "", err
}
return cookie.Value, nil
} }
func DeleteAdminCookie(gc *gin.Context, token string) { func DeleteAdminCookie(gc *gin.Context, token string) {
secure := true secure := true
httpOnly := true httpOnly := true
host, _ := GetHostParts(constants.AUTHORIZER_URL) host, _ := GetHostParts(constants.EnvData.AUTHORIZER_URL)
gc.SetCookie("authorizer-admin", "", -1, "/", host, secure, httpOnly) gc.SetCookie(constants.EnvData.ADMIN_COOKIE_NAME, "", -1, "/", host, secure, httpOnly)
} }

83
server/utils/crypto.go Normal file
View File

@ -0,0 +1,83 @@
package utils
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"io"
"github.com/authorizerdev/authorizer/server/constants"
)
func EncryptB64(text string) string {
return base64.StdEncoding.EncodeToString([]byte(text))
}
func DecryptB64(s string) (string, error) {
data, err := base64.StdEncoding.DecodeString(s)
if err != nil {
return "", err
}
return string(data), nil
}
func EncryptAES(text []byte) ([]byte, error) {
key := []byte(constants.EnvData.ENCRYPTION_KEY)
c, err := aes.NewCipher(key)
var res []byte
if err != nil {
return res, err
}
// gcm or Galois/Counter Mode, is a mode of operation
// for symmetric key cryptographic block ciphers
// - https://en.wikipedia.org/wiki/Galois/Counter_Mode
gcm, err := cipher.NewGCM(c)
if err != nil {
return res, err
}
// creates a new byte array the size of the nonce
// which must be passed to Seal
nonce := make([]byte, gcm.NonceSize())
// populates our nonce with a cryptographically secure
// random sequence
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
return res, err
}
// here we encrypt our text using the Seal function
// Seal encrypts and authenticates plaintext, authenticates the
// additional data and appends the result to dst, returning the updated
// slice. The nonce must be NonceSize() bytes long and unique for all
// time, for a given key.
return gcm.Seal(nonce, nonce, text, nil), nil
}
func DecryptAES(ciphertext []byte) ([]byte, error) {
key := []byte(constants.EnvData.ENCRYPTION_KEY)
c, err := aes.NewCipher(key)
var res []byte
if err != nil {
return res, err
}
gcm, err := cipher.NewGCM(c)
if err != nil {
return res, err
}
nonceSize := gcm.NonceSize()
if len(ciphertext) < nonceSize {
return res, err
}
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return res, err
}
return plaintext, nil
}

View File

@ -100,7 +100,7 @@ func SendVerificationMail(toEmail, token string) error {
<div style="position: absolute; left: -9999px; top: -9999px; margin: 0px;"></div> <div style="position: absolute; left: -9999px; top: -9999px; margin: 0px;"></div>
</body> </body>
</html> </html>
`, constants.ORGANIZATION_LOGO, constants.ORGANIZATION_NAME, constants.AUTHORIZER_URL+"/verify_email"+"?token="+token) `, constants.EnvData.ORGANIZATION_LOGO, constants.EnvData.ORGANIZATION_NAME, constants.EnvData.AUTHORIZER_URL+"/verify_email"+"?token="+token)
bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message) bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message)
return sender.SendMail(Receiver, Subject, bodyMessage) return sender.SendMail(Receiver, Subject, bodyMessage)
@ -108,8 +108,8 @@ func SendVerificationMail(toEmail, token string) error {
// SendForgotPasswordMail to send verification email // SendForgotPasswordMail to send verification email
func SendForgotPasswordMail(toEmail, token, host string) error { func SendForgotPasswordMail(toEmail, token, host string) error {
if constants.RESET_PASSWORD_URL == "" { if constants.EnvData.RESET_PASSWORD_URL == "" {
constants.RESET_PASSWORD_URL = constants.AUTHORIZER_URL + "/app/reset-password" constants.EnvData.RESET_PASSWORD_URL = constants.EnvData.AUTHORIZER_URL + "/app/reset-password"
} }
sender := email.NewSender() sender := email.NewSender()
@ -204,7 +204,7 @@ func SendForgotPasswordMail(toEmail, token, host string) error {
<div style="position: absolute; left: -9999px; top: -9999px; margin: 0px;"></div> <div style="position: absolute; left: -9999px; top: -9999px; margin: 0px;"></div>
</body> </body>
</html> </html>
`, constants.ORGANIZATION_LOGO, toEmail, constants.RESET_PASSWORD_URL+"?token="+token) `, constants.EnvData.ORGANIZATION_LOGO, toEmail, constants.EnvData.RESET_PASSWORD_URL+"?token="+token)
bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message) bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message)

View File

@ -9,12 +9,12 @@ import (
// version, // version,
func GetMetaInfo() model.Meta { func GetMetaInfo() model.Meta {
return model.Meta{ return model.Meta{
Version: constants.VERSION, Version: constants.EnvData.VERSION,
IsGoogleLoginEnabled: constants.GOOGLE_CLIENT_ID != "" && constants.GOOGLE_CLIENT_SECRET != "", IsGoogleLoginEnabled: constants.EnvData.GOOGLE_CLIENT_ID != "" && constants.EnvData.GOOGLE_CLIENT_SECRET != "",
IsGithubLoginEnabled: constants.GITHUB_CLIENT_ID != "" && constants.GOOGLE_CLIENT_SECRET != "", IsGithubLoginEnabled: constants.EnvData.GITHUB_CLIENT_ID != "" && constants.EnvData.GOOGLE_CLIENT_SECRET != "",
IsFacebookLoginEnabled: constants.FACEBOOK_CLIENT_ID != "" && constants.FACEBOOK_CLIENT_SECRET != "", IsFacebookLoginEnabled: constants.EnvData.FACEBOOK_CLIENT_ID != "" && constants.EnvData.FACEBOOK_CLIENT_SECRET != "",
IsBasicAuthenticationEnabled: !constants.DISABLE_BASIC_AUTHENTICATION, IsBasicAuthenticationEnabled: !constants.EnvData.DISABLE_BASIC_AUTHENTICATION,
IsEmailVerificationEnabled: !constants.DISABLE_EMAIL_VERIFICATION, IsEmailVerificationEnabled: !constants.EnvData.DISABLE_EMAIL_VERIFICATION,
IsMagicLinkLoginEnabled: !constants.DISABLE_MAGIC_LINK_LOGIN, IsMagicLinkLoginEnabled: !constants.EnvData.DISABLE_MAGIC_LINK_LOGIN,
} }
} }

View File

@ -16,7 +16,7 @@ func IsValidEmail(email string) bool {
} }
func IsValidOrigin(url string) bool { func IsValidOrigin(url string) bool {
if len(constants.ALLOWED_ORIGINS) == 1 && constants.ALLOWED_ORIGINS[0] == "*" { if len(constants.EnvData.ALLOWED_ORIGINS) == 1 && constants.EnvData.ALLOWED_ORIGINS[0] == "*" {
return true return true
} }
@ -24,7 +24,7 @@ func IsValidOrigin(url string) bool {
hostName, port := GetHostParts(url) hostName, port := GetHostParts(url)
currentOrigin := hostName + ":" + port currentOrigin := hostName + ":" + port
for _, origin := range constants.ALLOWED_ORIGINS { for _, origin := range constants.EnvData.ALLOWED_ORIGINS {
replacedString := origin replacedString := origin
// if has regex whitelisted domains // if has regex whitelisted domains
if strings.Contains(origin, "*") { if strings.Contains(origin, "*") {
@ -50,12 +50,17 @@ func IsValidOrigin(url string) bool {
} }
func IsSuperAdmin(gc *gin.Context) bool { func IsSuperAdmin(gc *gin.Context) bool {
token, err := GetAdminAuthToken(gc)
if err != nil {
secret := gc.Request.Header.Get("x-authorizer-admin-secret") secret := gc.Request.Header.Get("x-authorizer-admin-secret")
if secret == "" { if secret == "" {
return false return false
} }
return secret == constants.ADMIN_SECRET return secret == constants.EnvData.ADMIN_SECRET
}
return token != ""
} }
func IsValidRoles(userRoles []string, roles []string) bool { func IsValidRoles(userRoles []string, roles []string) bool {

View File

@ -20,23 +20,23 @@ type CustomClaim struct {
} }
func CreateVerificationToken(email string, tokenType string) (string, error) { func CreateVerificationToken(email string, tokenType string) (string, error) {
t := jwt.New(jwt.GetSigningMethod(constants.JWT_TYPE)) t := jwt.New(jwt.GetSigningMethod(constants.EnvData.JWT_TYPE))
t.Claims = &CustomClaim{ t.Claims = &CustomClaim{
&jwt.StandardClaims{ &jwt.StandardClaims{
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(), ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
}, },
tokenType, tokenType,
UserInfo{Email: email, Host: constants.AUTHORIZER_URL, RedirectURL: constants.APP_URL}, UserInfo{Email: email, Host: constants.EnvData.AUTHORIZER_URL, RedirectURL: constants.EnvData.APP_URL},
} }
return t.SignedString([]byte(constants.JWT_SECRET)) return t.SignedString([]byte(constants.EnvData.JWT_SECRET))
} }
func VerifyVerificationToken(token string) (*CustomClaim, error) { func VerifyVerificationToken(token string) (*CustomClaim, error) {
claims := &CustomClaim{} claims := &CustomClaim{}
_, err := jwt.ParseWithClaims(token, claims, func(token *jwt.Token) (interface{}, error) { _, err := jwt.ParseWithClaims(token, claims, func(token *jwt.Token) (interface{}, error) {
return []byte(constants.JWT_SECRET), nil return []byte(constants.EnvData.JWT_SECRET), nil
}) })
if err != nil { if err != nil {
return claims, err return claims, err