diff --git a/server/__test__/delete_user_test.go b/server/__test__/delete_user_test.go index 4f306dc..14084f2 100644 --- a/server/__test__/delete_user_test.go +++ b/server/__test__/delete_user_test.go @@ -24,7 +24,7 @@ func deleteUserTest(s TestSetup, t *testing.T) { }) 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{ Email: email, }) diff --git a/server/__test__/env_test.go b/server/__test__/env_test.go index 690ad51..328d5f6 100644 --- a/server/__test__/env_test.go +++ b/server/__test__/env_test.go @@ -8,18 +8,18 @@ import ( ) 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.ENV, "production") - assert.False(t, constants.DISABLE_EMAIL_VERIFICATION) - assert.False(t, constants.DISABLE_MAGIC_LINK_LOGIN) - assert.False(t, constants.DISABLE_BASIC_AUTHENTICATION) - assert.Equal(t, constants.JWT_TYPE, "HS256") - assert.Equal(t, constants.JWT_SECRET, "random_string") - assert.Equal(t, constants.JWT_ROLE_CLAIM, "role") - assert.EqualValues(t, constants.ROLES, []string{"user"}) - assert.EqualValues(t, constants.DEFAULT_ROLES, []string{"user"}) - assert.EqualValues(t, constants.PROTECTED_ROLES, []string{"admin"}) - assert.EqualValues(t, constants.ALLOWED_ORIGINS, []string{"*"}) + 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{"*"}) } diff --git a/server/__test__/resolvers_test.go b/server/__test__/resolvers_test.go index 9fe05ec..28db423 100644 --- a/server/__test__/resolvers_test.go +++ b/server/__test__/resolvers_test.go @@ -16,8 +16,8 @@ func TestResolvers(t *testing.T) { } for dbType, dbURL := range databases { - constants.DATABASE_URL = dbURL - constants.DATABASE_TYPE = dbType + constants.EnvData.DATABASE_URL = dbURL + constants.EnvData.DATABASE_TYPE = dbType db.InitDB() s := testSetup() diff --git a/server/__test__/test.go b/server/__test__/test.go index 1abd9f5..5cddfe3 100644 --- a/server/__test__/test.go +++ b/server/__test__/test.go @@ -70,7 +70,7 @@ func testSetup() TestSetup { Password: "test", } - constants.ENV_PATH = "../../.env.sample" + constants.EnvData.ENV_PATH = "../../.env.sample" env.InitEnv() session.InitSession() diff --git a/server/__test__/update_user_test.go b/server/__test__/update_user_test.go index 45d614a..fd832e9 100644 --- a/server/__test__/update_user_test.go +++ b/server/__test__/update_user_test.go @@ -29,7 +29,7 @@ func updateUserTest(s TestSetup, t *testing.T) { }) 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{ ID: user.ID, Roles: newRoles, diff --git a/server/__test__/users_test.go b/server/__test__/users_test.go index 6386a8e..8251325 100644 --- a/server/__test__/users_test.go +++ b/server/__test__/users_test.go @@ -22,7 +22,7 @@ func usersTest(s TestSetup, t *testing.T) { users, err := resolvers.Users(ctx) 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) assert.Nil(t, err) rLen := len(users) diff --git a/server/__test__/validator_test.go b/server/__test__/validator_test.go index 4999716..7145d74 100644 --- a/server/__test__/validator_test.go +++ b/server/__test__/validator_test.go @@ -22,7 +22,7 @@ func TestIsValidEmail(t *testing.T) { func TestIsValidOrigin(t *testing.T) { // don't use portocal(http/https) for ALLOWED_ORIGINS while testing, // as we trim them off while running the main function - constants.ALLOWED_ORIGINS = []string{"localhost:8080", "*.google.com", "*.google.in", "*abc.*"} + 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://appgoogle.com"), "it should be invalid origin") diff --git a/server/__test__/verification_requests_test.go b/server/__test__/verification_requests_test.go index 22fc26e..a4c6f0b 100644 --- a/server/__test__/verification_requests_test.go +++ b/server/__test__/verification_requests_test.go @@ -23,7 +23,7 @@ func verificationRequestsTest(s TestSetup, t *testing.T) { requests, err := resolvers.VerificationRequests(ctx) 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) assert.Nil(t, err) diff --git a/server/constants/constants.go b/server/constants/constants.go index 23b66f9..b2ae221 100644 --- a/server/constants/constants.go +++ b/server/constants/constants.go @@ -1,50 +1,61 @@ package constants -// this constants are configured via env -var ( - ADMIN_SECRET = "" - ENV = "" - ENV_PATH = "" - VERSION = "" - DATABASE_TYPE = "" - DATABASE_URL = "" - DATABASE_NAME = "" - SMTP_HOST = "" - SMTP_PORT = "" - SENDER_EMAIL = "" - SENDER_PASSWORD = "" - JWT_TYPE = "" - JWT_SECRET = "" - ALLOWED_ORIGINS = []string{} - AUTHORIZER_URL = "" - APP_URL = "" - PORT = "" - REDIS_URL = "" - IS_PROD = false - COOKIE_NAME = "" - RESET_PASSWORD_URL = "" - DISABLE_EMAIL_VERIFICATION = false - DISABLE_BASIC_AUTHENTICATION = false - DISABLE_MAGIC_LINK_LOGIN = false - DISABLE_LOGIN_PAGE = false +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 + SENDER_EMAIL string + SENDER_PASSWORD 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 = "role" + ROLES []string + PROTECTED_ROLES []string + DEFAULT_ROLES []string + JWT_ROLE_CLAIM string // OAuth login - GOOGLE_CLIENT_ID = "" - GOOGLE_CLIENT_SECRET = "" - GITHUB_CLIENT_ID = "" - GITHUB_CLIENT_SECRET = "" - FACEBOOK_CLIENT_ID = "" - FACEBOOK_CLIENT_SECRET = "" - TWITTER_CLIENT_ID = "" - TWITTER_CLIENT_SECRET = "" + 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 = "Authorizer" - ORGANIZATION_LOGO = "https://authorizer.dev/images/logo.png" -) + 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, +} diff --git a/server/db/arangodb.go b/server/db/arangodb.go index 1d624ed..f6cdb06 100644 --- a/server/db/arangodb.go +++ b/server/db/arangodb.go @@ -17,7 +17,7 @@ import ( func initArangodb() (arangoDriver.Database, error) { ctx := context.Background() conn, err := http.NewConnection(http.ConnectionConfig{ - Endpoints: []string{constants.DATABASE_URL}, + Endpoints: []string{constants.EnvData.DATABASE_URL}, }) if err != nil { return nil, err @@ -32,16 +32,16 @@ func initArangodb() (arangoDriver.Database, error) { 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 { - log.Println(constants.DATABASE_NAME + " db exists already") - arangodb, err = arangoClient.Database(nil, constants.DATABASE_NAME) + log.Println(constants.EnvData.DATABASE_NAME + " db exists already") + arangodb, err = arangoClient.Database(nil, constants.EnvData.DATABASE_NAME) if err != nil { return nil, err } } else { - arangodb, err = arangoClient.CreateDatabase(nil, constants.DATABASE_NAME, nil) + arangodb, err = arangoClient.CreateDatabase(nil, constants.EnvData.DATABASE_NAME, nil) if err != nil { return nil, err } @@ -100,5 +100,15 @@ func initArangodb() (arangoDriver.Database, error) { 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 } diff --git a/server/db/config.go b/server/db/config.go new file mode 100644 index 0000000..d4f724a --- /dev/null +++ b/server/db/config.go @@ -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 +} diff --git a/server/db/db.go b/server/db/db.go index f8e7256..8727da5 100644 --- a/server/db/db.go +++ b/server/db/db.go @@ -29,6 +29,9 @@ type Manager interface { GetVerificationByEmail(email string, identifier string) (VerificationRequest, error) AddSession(session Session) error DeleteUserSession(userId string) error + AddConfig(config Config) (Config, error) + UpdateConfig(config Config) (Config, error) + GetConfig() (Config, error) } type manager struct { @@ -42,6 +45,7 @@ type CollectionList struct { User string VerificationRequest string Session string + Config string } var ( @@ -54,6 +58,7 @@ var ( User: Prefix + "users", VerificationRequest: Prefix + "verification_requests", Session: Prefix + "sessions", + Config: Prefix + "config", } ) @@ -61,9 +66,9 @@ func InitDB() { var sqlDB *gorm.DB var err error - IsORMSupported = constants.DATABASE_TYPE != enum.Arangodb.String() && constants.DATABASE_TYPE != enum.Mongodb.String() - IsArangoDB = constants.DATABASE_TYPE == enum.Arangodb.String() - IsMongoDB = constants.DATABASE_TYPE == enum.Mongodb.String() + 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() // sql db orm 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(): - sqlDB, err = gorm.Open(postgres.Open(constants.DATABASE_URL), ormConfig) + sqlDB, err = gorm.Open(postgres.Open(constants.EnvData.DATABASE_URL), ormConfig) break 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 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 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 case enum.Arangodb.String(): arangodb, err := initArangodb() @@ -118,7 +123,7 @@ func InitDB() { if err != nil { log.Fatal("Failed to init sqlDB:", err) } else { - sqlDB.AutoMigrate(&User{}, &VerificationRequest{}, &Session{}) + sqlDB.AutoMigrate(&User{}, &VerificationRequest{}, &Session{}, &Config{}) } Mgr = &manager{ sqlDB: sqlDB, diff --git a/server/db/mongodb.go b/server/db/mongodb.go index c69f5bc..21d9320 100644 --- a/server/db/mongodb.go +++ b/server/db/mongodb.go @@ -12,7 +12,7 @@ import ( ) 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) mongodbOptions.ConnectTimeout = &maxWait mongoClient, err := mongo.NewClient(mongodbOptions) @@ -30,7 +30,7 @@ func initMongodb() (*mongo.Database, error) { 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()) userCollection := mongodb.Collection(Collections.User, options.Collection()) @@ -73,5 +73,7 @@ func initMongodb() (*mongo.Database, error) { }, }, options.CreateIndexes()) + mongodb.CreateCollection(ctx, Collections.Config, options.CreateCollection()) + return mongodb, nil } diff --git a/server/db/user.go b/server/db/user.go index 60254fa..f020dcf 100644 --- a/server/db/user.go +++ b/server/db/user.go @@ -43,7 +43,7 @@ func (mgr *manager) AddUser(user User) (User, error) { } if user.Roles == "" { - user.Roles = constants.DEFAULT_ROLES[0] + user.Roles = constants.EnvData.DEFAULT_ROLES[0] } if IsORMSupported { diff --git a/server/email/email.go b/server/email/email.go index 3b2bf60..0136e1b 100644 --- a/server/email/email.go +++ b/server/email/email.go @@ -27,7 +27,7 @@ type Sender struct { } 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 { @@ -35,8 +35,8 @@ func (sender Sender) SendMail(Dest []string, Subject, bodyMessage string) error "To: " + strings.Join(Dest, ",") + "\n" + "Subject: " + Subject + "\n" + bodyMessage - err := smtp.SendMail(constants.SMTP_HOST+":"+constants.SMTP_PORT, - smtp.PlainAuth("", sender.User, sender.Password, constants.SMTP_HOST), + err := smtp.SendMail(constants.EnvData.SMTP_HOST+":"+constants.EnvData.SMTP_PORT, + smtp.PlainAuth("", sender.User, sender.Password, constants.EnvData.SMTP_HOST), sender.User, Dest, []byte(msg)) if err != nil { diff --git a/server/env/env.go b/server/env/env.go index 0ee0294..fa0f135 100644 --- a/server/env/env.go +++ b/server/env/env.go @@ -7,6 +7,7 @@ import ( "github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/utils" + "github.com/google/uuid" "github.com/joho/godotenv" ) @@ -20,163 +21,169 @@ var ( // InitEnv -> to initialize env and through error if required env are not present func InitEnv() { - if constants.ENV_PATH == "" { - constants.ENV_PATH = `.env` + if constants.EnvData.ENV_PATH == "" { + constants.EnvData.ENV_PATH = `.env` } 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 { - log.Printf("error loading %s file", constants.ENV_PATH) + log.Printf("error loading %s file", constants.EnvData.ENV_PATH) } - if constants.ADMIN_SECRET == "" { - constants.ADMIN_SECRET = os.Getenv("ADMIN_SECRET") - if constants.ADMIN_SECRET == "" { - panic("root admin secret is required") - } + if constants.EnvData.ADMIN_SECRET == "" { + constants.EnvData.ADMIN_SECRET = os.Getenv("ADMIN_SECRET") } - if constants.ENV == "" { - constants.ENV = os.Getenv("ENV") - if constants.ENV == "" { - 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 constants.EnvData.DATABASE_TYPE == "" { + constants.EnvData.DATABASE_TYPE = os.Getenv("DATABASE_TYPE") + log.Println(constants.EnvData.DATABASE_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") } } - if constants.DATABASE_URL == "" { - constants.DATABASE_URL = os.Getenv("DATABASE_URL") + if constants.EnvData.DATABASE_URL == "" { + constants.EnvData.DATABASE_URL = os.Getenv("DATABASE_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") } } - if constants.DATABASE_NAME == "" { - constants.DATABASE_NAME = os.Getenv("DATABASE_NAME") - if constants.DATABASE_NAME == "" { - constants.DATABASE_NAME = "authorizer" + if constants.EnvData.DATABASE_NAME == "" { + constants.EnvData.DATABASE_NAME = os.Getenv("DATABASE_NAME") + if constants.EnvData.DATABASE_NAME == "" { + constants.EnvData.DATABASE_NAME = "authorizer" } } - if constants.SMTP_HOST == "" { - constants.SMTP_HOST = os.Getenv("SMTP_HOST") - } + if constants.EnvData.ENV == "" { + constants.EnvData.ENV = os.Getenv("ENV") + if constants.EnvData.ENV == "" { + constants.EnvData.ENV = "production" + } - if constants.SMTP_PORT == "" { - constants.SMTP_PORT = os.Getenv("SMTP_PORT") - } - - if constants.SENDER_EMAIL == "" { - 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.EnvData.ENV == "production" { + constants.EnvData.IS_PROD = true + os.Setenv("GIN_MODE", "release") + } else { + constants.EnvData.IS_PROD = false } } - if constants.AUTHORIZER_URL == "" { - constants.AUTHORIZER_URL = strings.TrimSuffix(os.Getenv("AUTHORIZER_URL"), "/") + 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.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 != "" { - constants.AUTHORIZER_URL = *ARG_AUTHORIZER_URL + constants.EnvData.AUTHORIZER_URL = *ARG_AUTHORIZER_URL } } - if constants.PORT == "" { - constants.PORT = os.Getenv("PORT") - if constants.PORT == "" { - constants.PORT = "8080" + if constants.EnvData.PORT == "" { + constants.EnvData.PORT = os.Getenv("PORT") + if constants.EnvData.PORT == "" { + constants.EnvData.PORT = "8080" } } - if constants.REDIS_URL == "" { - constants.REDIS_URL = os.Getenv("REDIS_URL") + if constants.EnvData.REDIS_URL == "" { + constants.EnvData.REDIS_URL = os.Getenv("REDIS_URL") } - if constants.COOKIE_NAME == "" { - constants.COOKIE_NAME = os.Getenv("COOKIE_NAME") + if constants.EnvData.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 == "" { - constants.GOOGLE_CLIENT_ID = os.Getenv("GOOGLE_CLIENT_ID") + if constants.EnvData.GOOGLE_CLIENT_ID == "" { + constants.EnvData.GOOGLE_CLIENT_ID = os.Getenv("GOOGLE_CLIENT_ID") } - if constants.GOOGLE_CLIENT_SECRET == "" { - constants.GOOGLE_CLIENT_SECRET = os.Getenv("GOOGLE_CLIENT_SECRET") + if constants.EnvData.GOOGLE_CLIENT_SECRET == "" { + constants.EnvData.GOOGLE_CLIENT_SECRET = os.Getenv("GOOGLE_CLIENT_SECRET") } - if constants.GITHUB_CLIENT_ID == "" { - constants.GITHUB_CLIENT_ID = os.Getenv("GITHUB_CLIENT_ID") + if constants.EnvData.GITHUB_CLIENT_ID == "" { + constants.EnvData.GITHUB_CLIENT_ID = os.Getenv("GITHUB_CLIENT_ID") } - if constants.GITHUB_CLIENT_SECRET == "" { - constants.GITHUB_CLIENT_SECRET = os.Getenv("GITHUB_CLIENT_SECRET") + if constants.EnvData.GITHUB_CLIENT_SECRET == "" { + constants.EnvData.GITHUB_CLIENT_SECRET = os.Getenv("GITHUB_CLIENT_SECRET") } - if constants.FACEBOOK_CLIENT_ID == "" { - constants.FACEBOOK_CLIENT_ID = os.Getenv("FACEBOOK_CLIENT_ID") + if constants.EnvData.FACEBOOK_CLIENT_ID == "" { + constants.EnvData.FACEBOOK_CLIENT_ID = os.Getenv("FACEBOOK_CLIENT_ID") } - if constants.FACEBOOK_CLIENT_SECRET == "" { - constants.FACEBOOK_CLIENT_SECRET = os.Getenv("FACEBOOK_CLIENT_SECRET") + if constants.EnvData.FACEBOOK_CLIENT_SECRET == "" { + constants.EnvData.FACEBOOK_CLIENT_SECRET = os.Getenv("FACEBOOK_CLIENT_SECRET") } - if constants.RESET_PASSWORD_URL == "" { - constants.RESET_PASSWORD_URL = strings.TrimPrefix(os.Getenv("RESET_PASSWORD_URL"), "/") + if constants.EnvData.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.DISABLE_EMAIL_VERIFICATION = os.Getenv("DISABLE_EMAIL_VERIFICATION") == "true" - constants.DISABLE_MAGIC_LINK_LOGIN = os.Getenv("DISABLE_MAGIC_LINK_LOGIN") == "true" - constants.DISABLE_LOGIN_PAGE = os.Getenv("DISABLE_LOGIN_PAGE") == "true" + 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.SMTP_HOST == "" || constants.SENDER_EMAIL == "" || constants.SENDER_PASSWORD == "" { - constants.DISABLE_EMAIL_VERIFICATION = true - constants.DISABLE_MAGIC_LINK_LOGIN = true + if constants.EnvData.SMTP_HOST == "" || constants.EnvData.SENDER_EMAIL == "" || constants.EnvData.SENDER_PASSWORD == "" { + constants.EnvData.DISABLE_EMAIL_VERIFICATION = true + constants.EnvData.DISABLE_MAGIC_LINK_LOGIN = true } allowedOriginsSplit := strings.Split(os.Getenv("ALLOWED_ORIGINS"), ",") @@ -205,18 +212,10 @@ func InitEnv() { allowedOrigins = []string{"*"} } - constants.ALLOWED_ORIGINS = allowedOrigins + constants.EnvData.ALLOWED_ORIGINS = allowedOrigins - if constants.JWT_TYPE == "" { - constants.JWT_TYPE = "HS256" - } - - if constants.COOKIE_NAME == "" { - constants.COOKIE_NAME = "authorizer" - } - - if constants.DISABLE_EMAIL_VERIFICATION { - constants.DISABLE_MAGIC_LINK_LOGIN = true + if constants.EnvData.DISABLE_EMAIL_VERIFICATION { + constants.EnvData.DISABLE_MAGIC_LINK_LOGIN = true } 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`) } - constants.ROLES = roles - constants.DEFAULT_ROLES = defaultRoles - constants.PROTECTED_ROLES = protectedRoles + constants.EnvData.ROLES = roles + constants.EnvData.DEFAULT_ROLES = defaultRoles + constants.EnvData.PROTECTED_ROLES = protectedRoles 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") != "" { - constants.ORGANIZATION_LOGO = os.Getenv("ORGANIZATION_LOGO") + constants.EnvData.ORGANIZATION_LOGO = os.Getenv("ORGANIZATION_LOGO") } } diff --git a/server/env/persist_env.go b/server/env/persist_env.go new file mode 100644 index 0000000..9f1fdd5 --- /dev/null +++ b/server/env/persist_env.go @@ -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 +} diff --git a/server/handlers/app.go b/server/handlers/app.go index 4fb7070..e2569e4 100644 --- a/server/handlers/app.go +++ b/server/handlers/app.go @@ -1,7 +1,6 @@ package handlers import ( - "encoding/base64" "encoding/json" "log" "net/http" @@ -30,17 +29,17 @@ func AppHandler() gin.HandlerFunc { // return // } - stateObj.AuthorizerURL = constants.AUTHORIZER_URL - stateObj.RedirectURL = constants.AUTHORIZER_URL + "/app" + stateObj.AuthorizerURL = constants.EnvData.AUTHORIZER_URL + stateObj.RedirectURL = constants.EnvData.AUTHORIZER_URL + "/app" } else { - decodedState, err := base64.StdEncoding.DecodeString(state) + decodedState, err := utils.DecryptB64(state) if err != nil { c.JSON(400, gin.H{"error": "[unable to decode state] invalid state"}) return } - err = json.Unmarshal(decodedState, &stateObj) + err = json.Unmarshal([]byte(decodedState), &stateObj) if err != nil { c.JSON(400, gin.H{"error": "[unable to parse state] invalid state"}) return @@ -60,7 +59,7 @@ func AppHandler() gin.HandlerFunc { } // 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"}) return } @@ -77,8 +76,8 @@ func AppHandler() gin.HandlerFunc { "data": map[string]string{ "authorizerURL": stateObj.AuthorizerURL, "redirectURL": stateObj.RedirectURL, - "organizationName": constants.ORGANIZATION_NAME, - "organizationLogo": constants.ORGANIZATION_LOGO, + "organizationName": constants.EnvData.ORGANIZATION_NAME, + "organizationLogo": constants.EnvData.ORGANIZATION_LOGO, }, }) } diff --git a/server/handlers/dashboard.go b/server/handlers/dashboard.go index 9ad06ec..050ba3c 100644 --- a/server/handlers/dashboard.go +++ b/server/handlers/dashboard.go @@ -10,7 +10,7 @@ import ( func DashboardHandler() gin.HandlerFunc { return func(c *gin.Context) { 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 } diff --git a/server/handlers/oauth_callback.go b/server/handlers/oauth_callback.go index 4fd0364..a6c922a 100644 --- a/server/handlers/oauth_callback.go +++ b/server/handlers/oauth_callback.go @@ -195,7 +195,7 @@ func OAuthCallbackHandler() gin.HandlerFunc { // make sure inputRoles don't include protected roles hasProtectedRole := false for _, ir := range inputRoles { - if utils.StringSliceContains(constants.PROTECTED_ROLES, ir) { + if utils.StringSliceContains(constants.EnvData.PROTECTED_ROLES, ir) { hasProtectedRole = true } } @@ -238,7 +238,7 @@ func OAuthCallbackHandler() gin.HandlerFunc { // check if it contains protected unassigned role hasProtectedRole := false for _, ur := range unasignedRoles { - if utils.StringSliceContains(constants.PROTECTED_ROLES, ur) { + if utils.StringSliceContains(constants.EnvData.PROTECTED_ROLES, ur) { hasProtectedRole = true } } diff --git a/server/handlers/oauth_login.go b/server/handlers/oauth_login.go index 1f3cd4e..0363f70 100644 --- a/server/handlers/oauth_login.go +++ b/server/handlers/oauth_login.go @@ -34,14 +34,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.ROLES, constants.PROTECTED_ROLES...)...), rolesSplit) { + if !utils.IsValidRoles(append([]string{}, append(constants.EnvData.ROLES, constants.EnvData.PROTECTED_ROLES...)...), rolesSplit) { c.JSON(400, gin.H{ "error": "invalid role", }) return } } else { - roles = strings.Join(constants.DEFAULT_ROLES, ",") + roles = strings.Join(constants.EnvData.DEFAULT_ROLES, ",") } uuid := uuid.New() @@ -57,7 +57,7 @@ func OAuthLoginHandler() gin.HandlerFunc { } session.SetSocailLoginState(oauthStateString, enum.Google.String()) // 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) c.Redirect(http.StatusTemporaryRedirect, url) case enum.Github.String(): @@ -66,7 +66,7 @@ func OAuthLoginHandler() gin.HandlerFunc { break } 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) c.Redirect(http.StatusTemporaryRedirect, url) case enum.Facebook.String(): @@ -75,7 +75,7 @@ func OAuthLoginHandler() gin.HandlerFunc { break } 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) c.Redirect(http.StatusTemporaryRedirect, url) default: diff --git a/server/main.go b/server/main.go index 8d2af0d..7e7c88e 100644 --- a/server/main.go +++ b/server/main.go @@ -22,10 +22,12 @@ func main() { env.ARG_ENV_FILE = flag.String("env_file", "", "Env file path") flag.Parse() - constants.VERSION = VERSION + constants.EnvData.VERSION = VERSION env.InitEnv() db.InitDB() + env.PersistEnv() + session.InitSession() oauth.InitOAuth() utils.InitServer() @@ -35,7 +37,7 @@ func main() { 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.DISABLE_LOGIN_PAGE { + if !constants.EnvData.DISABLE_LOGIN_PAGE { app := router.Group("/app") { app.Static("/build", "app/build") @@ -50,5 +52,5 @@ func main() { app.GET("/", handlers.DashboardHandler()) } - router.Run(":" + constants.PORT) + router.Run(":" + constants.EnvData.PORT) } diff --git a/server/middlewares/context.go b/server/middlewares/context.go index 32e824b..fd205ac 100644 --- a/server/middlewares/context.go +++ b/server/middlewares/context.go @@ -11,10 +11,10 @@ import ( func GinContextToContextMiddleware() gin.HandlerFunc { return func(c *gin.Context) { - if constants.AUTHORIZER_URL == "" { + if constants.EnvData.AUTHORIZER_URL == "" { url := location.Get(c) - constants.AUTHORIZER_URL = url.Scheme + "://" + c.Request.Host - log.Println("authorizer url:", constants.AUTHORIZER_URL) + constants.EnvData.AUTHORIZER_URL = url.Scheme + "://" + c.Request.Host + log.Println("authorizer url:", constants.EnvData.AUTHORIZER_URL) } ctx := context.WithValue(c.Request.Context(), "GinContextKey", c) c.Request = c.Request.WithContext(ctx) diff --git a/server/middlewares/cors.go b/server/middlewares/cors.go index e0bb4a9..a5065e6 100644 --- a/server/middlewares/cors.go +++ b/server/middlewares/cors.go @@ -9,7 +9,7 @@ import ( func CORSMiddleware() gin.HandlerFunc { return func(c *gin.Context) { origin := c.Request.Header.Get("Origin") - constants.APP_URL = origin + constants.EnvData.APP_URL = origin if utils.IsValidOrigin(origin) { c.Writer.Header().Set("Access-Control-Allow-Origin", origin) diff --git a/server/oauth/oauth.go b/server/oauth/oauth.go index 64b6927..5c6d943 100644 --- a/server/oauth/oauth.go +++ b/server/oauth/oauth.go @@ -28,33 +28,33 @@ var ( func InitOAuth() { 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") if err != nil { log.Fatalln("error creating oidc provider for google:", err) } OIDCProviders.GoogleOIDC = p OAuthProviders.GoogleConfig = &oauth2.Config{ - ClientID: constants.GOOGLE_CLIENT_ID, - ClientSecret: constants.GOOGLE_CLIENT_SECRET, - RedirectURL: constants.AUTHORIZER_URL + "/oauth_callback/google", + ClientID: constants.EnvData.GOOGLE_CLIENT_ID, + ClientSecret: constants.EnvData.GOOGLE_CLIENT_SECRET, + RedirectURL: constants.EnvData.AUTHORIZER_URL + "/oauth_callback/google", Endpoint: OIDCProviders.GoogleOIDC.Endpoint(), 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{ - ClientID: constants.GITHUB_CLIENT_ID, - ClientSecret: constants.GITHUB_CLIENT_SECRET, - RedirectURL: constants.AUTHORIZER_URL + "/oauth_callback/github", + ClientID: constants.EnvData.GITHUB_CLIENT_ID, + ClientSecret: constants.EnvData.GITHUB_CLIENT_SECRET, + RedirectURL: constants.EnvData.AUTHORIZER_URL + "/oauth_callback/github", 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{ - ClientID: constants.FACEBOOK_CLIENT_ID, - ClientSecret: constants.FACEBOOK_CLIENT_SECRET, - RedirectURL: constants.AUTHORIZER_URL + "/oauth_callback/facebook", + ClientID: constants.EnvData.FACEBOOK_CLIENT_ID, + ClientSecret: constants.EnvData.FACEBOOK_CLIENT_SECRET, + RedirectURL: constants.EnvData.AUTHORIZER_URL + "/oauth_callback/facebook", Endpoint: facebookOAuth2.Endpoint, Scopes: []string{"public_profile", "email"}, } diff --git a/server/resolvers/admin_login.go b/server/resolvers/admin_login.go index e61bae7..bb5f44a 100644 --- a/server/resolvers/admin_login.go +++ b/server/resolvers/admin_login.go @@ -20,7 +20,7 @@ func AdminLoginResolver(ctx context.Context, params model.AdminLoginInput) (*mod log.Println("=> error:", err) return res, err } - if params.AdminSecret != constants.ADMIN_SECRET { + if params.AdminSecret != constants.EnvData.ADMIN_SECRET { return nil, fmt.Errorf(`invalid admin secret`) } diff --git a/server/resolvers/forgot_password.go b/server/resolvers/forgot_password.go index fc5ba16..8c5c583 100644 --- a/server/resolvers/forgot_password.go +++ b/server/resolvers/forgot_password.go @@ -20,7 +20,7 @@ func ForgotPassword(ctx context.Context, params model.ForgotPasswordInput) (*mod if err != nil { 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`) } host := gc.Request.Host diff --git a/server/resolvers/login.go b/server/resolvers/login.go index 00b444e..482cf4b 100644 --- a/server/resolvers/login.go +++ b/server/resolvers/login.go @@ -22,7 +22,7 @@ func Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, e 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`) } @@ -46,7 +46,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.DEFAULT_ROLES + roles := constants.EnvData.DEFAULT_ROLES currentRoles := strings.Split(user.Roles, ",") if len(params.Roles) > 0 { if !utils.IsValidRoles(currentRoles, params.Roles) { diff --git a/server/resolvers/magic_link_login.go b/server/resolvers/magic_link_login.go index 2bb3d9d..8ba726a 100644 --- a/server/resolvers/magic_link_login.go +++ b/server/resolvers/magic_link_login.go @@ -17,7 +17,7 @@ import ( func MagicLinkLogin(ctx context.Context, params model.MagicLinkLoginInput) (*model.Response, error) { 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`) } @@ -41,13 +41,13 @@ func MagicLinkLogin(ctx context.Context, params model.MagicLinkLoginInput) (*mod // define roles for new user if len(params.Roles) > 0 { // 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`) } else { inputRoles = params.Roles } } else { - inputRoles = constants.DEFAULT_ROLES + inputRoles = constants.EnvData.DEFAULT_ROLES } 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 hasProtectedRole := false for _, ur := range unasignedRoles { - if utils.StringSliceContains(constants.PROTECTED_ROLES, ur) { + if utils.StringSliceContains(constants.EnvData.PROTECTED_ROLES, ur) { 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 verificationType := enum.MagicLinkLogin.String() token, err := utils.CreateVerificationToken(params.Email, verificationType) diff --git a/server/resolvers/reset_password.go b/server/resolvers/reset_password.go index 4c914c0..d459a83 100644 --- a/server/resolvers/reset_password.go +++ b/server/resolvers/reset_password.go @@ -14,7 +14,7 @@ import ( func ResetPassword(ctx context.Context, params model.ResetPasswordInput) (*model.Response, error) { 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`) } diff --git a/server/resolvers/session.go b/server/resolvers/session.go index cecc0fe..a587deb 100644 --- a/server/resolvers/session.go +++ b/server/resolvers/session.go @@ -45,7 +45,7 @@ func Session(ctx context.Context, roles []string) (*model.AuthResponse, error) { expiresTimeObj := time.Unix(expiresAt, 0) currentTimeObj := time.Now() - claimRoleInterface := claim[constants.JWT_ROLE_CLAIM].([]interface{}) + claimRoleInterface := claim[constants.EnvData.JWT_ROLE_CLAIM].([]interface{}) claimRoles := make([]string, len(claimRoleInterface)) for i, v := range claimRoleInterface { claimRoles[i] = v.(string) diff --git a/server/resolvers/signup.go b/server/resolvers/signup.go index aa9a241..ba19b45 100644 --- a/server/resolvers/signup.go +++ b/server/resolvers/signup.go @@ -22,7 +22,7 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse, 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`) } if params.ConfirmPassword != params.Password { @@ -52,13 +52,13 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse, if len(params.Roles) > 0 { // 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`) } else { inputRoles = params.Roles } } else { - inputRoles = constants.DEFAULT_ROLES + inputRoles = constants.EnvData.DEFAULT_ROLES } user := db.User{ @@ -103,7 +103,7 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse, } user.SignupMethods = enum.BasicAuth.String() - if constants.DISABLE_EMAIL_VERIFICATION { + if constants.EnvData.DISABLE_EMAIL_VERIFICATION { now := time.Now().Unix() user.EmailVerifiedAt = &now } @@ -115,7 +115,7 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse, roles := strings.Split(user.Roles, ",") userToReturn := utils.GetResponseUserData(user) - if !constants.DISABLE_EMAIL_VERIFICATION { + if !constants.EnvData.DISABLE_EMAIL_VERIFICATION { // insert verification request verificationType := enum.BasicAuthSignup.String() token, err := utils.CreateVerificationToken(params.Email, verificationType) diff --git a/server/resolvers/update_user.go b/server/resolvers/update_user.go index 93222de..c67021b 100644 --- a/server/resolvers/update_user.go +++ b/server/resolvers/update_user.go @@ -112,7 +112,7 @@ func UpdateUser(ctx context.Context, params model.UpdateUserInput) (*model.User, 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") } diff --git a/server/session/session.go b/server/session/session.go index 08138b0..39727b3 100644 --- a/server/session/session.go +++ b/server/session/session.go @@ -95,9 +95,9 @@ func RemoveSocialLoginState(key string) { } func InitSession() { - if constants.REDIS_URL != "" { + if constants.EnvData.REDIS_URL != "" { 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 { log.Fatalln("Error parsing redis url:", err) } diff --git a/server/utils/auth_token.go b/server/utils/auth_token.go index 7d4a3ad..c3c6de1 100644 --- a/server/utils/auth_token.go +++ b/server/utils/auth_token.go @@ -17,7 +17,7 @@ import ( ) 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 if tokenType == enum.RefreshToken { // expires in 1 year @@ -32,11 +32,11 @@ func CreateAuthToken(user db.User, tokenType enum.TokenType, roles []string) (st json.Unmarshal(userBytes, &userMap) customClaims := jwt.MapClaims{ - "exp": expiresAt, - "iat": time.Now().Unix(), - "token_type": tokenType.String(), - "allowed_roles": strings.Split(user.Roles, ","), - constants.JWT_ROLE_CLAIM: roles, + "exp": expiresAt, + "iat": time.Now().Unix(), + "token_type": tokenType.String(), + "allowed_roles": strings.Split(user.Roles, ","), + constants.EnvData.JWT_ROLE_CLAIM: roles, } for k, v := range userMap { @@ -77,7 +77,7 @@ func CreateAuthToken(user db.User, tokenType enum.TokenType, roles []string) (st t.Claims = customClaims - token, err := t.SignedString([]byte(constants.JWT_SECRET)) + token, err := t.SignedString([]byte(constants.EnvData.JWT_SECRET)) if err != nil { return "", 0, err } @@ -89,7 +89,6 @@ func GetAuthToken(gc *gin.Context) (string, error) { token, err := GetCookie(gc) if err != nil || token == "" { // try to check in auth header for cookie - log.Println("cookie not found checking headers") auth := gc.Request.Header.Get("Authorization") if auth == "" { return "", fmt.Errorf(`unauthorized`) @@ -105,7 +104,7 @@ func VerifyAuthToken(token string) (map[string]interface{}, error) { claims := jwt.MapClaims{} _, 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 { 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) { - t := jwt.New(jwt.GetSigningMethod(constants.JWT_TYPE)) + t := jwt.New(jwt.GetSigningMethod(constants.EnvData.JWT_TYPE)) expiryBound := time.Hour if tokenType == enum.RefreshToken { // expires in 1 year @@ -146,9 +145,23 @@ func CreateAdminAuthToken(tokenType enum.TokenType, c *gin.Context) (string, int t.Claims = customClaims - token, err := t.SignedString([]byte(constants.JWT_SECRET)) + token, err := t.SignedString([]byte(constants.EnvData.JWT_SECRET)) if err != nil { return "", 0, err } 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 +} diff --git a/server/utils/cookie.go b/server/utils/cookie.go index c9323f7..996bee7 100644 --- a/server/utils/cookie.go +++ b/server/utils/cookie.go @@ -10,21 +10,21 @@ import ( func SetCookie(gc *gin.Context, token string) { secure := true httpOnly := true - host, _ := GetHostParts(constants.AUTHORIZER_URL) - domain := GetDomainName(constants.AUTHORIZER_URL) + host, _ := GetHostParts(constants.EnvData.AUTHORIZER_URL) + domain := GetDomainName(constants.EnvData.AUTHORIZER_URL) if domain != "localhost" { domain = "." + domain } gc.SetSameSite(http.SameSiteNoneMode) - gc.SetCookie(constants.COOKIE_NAME, token, 3600, "/", host, secure, httpOnly) - gc.SetCookie(constants.COOKIE_NAME+"-client", token, 3600, "/", domain, secure, httpOnly) + gc.SetCookie(constants.EnvData.COOKIE_NAME, token, 3600, "/", host, secure, httpOnly) + gc.SetCookie(constants.EnvData.COOKIE_NAME+"-client", token, 3600, "/", domain, secure, httpOnly) } 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 { - cookie, err = gc.Request.Cookie(constants.COOKIE_NAME + "-client") + cookie, err = gc.Request.Cookie(constants.EnvData.COOKIE_NAME + "-client") if err != nil { return "", err } @@ -37,29 +37,37 @@ func DeleteCookie(gc *gin.Context) { secure := true httpOnly := true - host, _ := GetHostParts(constants.AUTHORIZER_URL) - domain := GetDomainName(constants.AUTHORIZER_URL) + host, _ := GetHostParts(constants.EnvData.AUTHORIZER_URL) + domain := GetDomainName(constants.EnvData.AUTHORIZER_URL) if domain != "localhost" { domain = "." + domain } gc.SetSameSite(http.SameSiteNoneMode) - gc.SetCookie(constants.COOKIE_NAME, "", -1, "/", host, secure, httpOnly) - gc.SetCookie(constants.COOKIE_NAME+"-client", "", -1, "/", domain, secure, httpOnly) + gc.SetCookie(constants.EnvData.COOKIE_NAME, "", -1, "/", host, secure, httpOnly) + gc.SetCookie(constants.EnvData.COOKIE_NAME+"-client", "", -1, "/", domain, secure, httpOnly) } func SetAdminCookie(gc *gin.Context, token string) { secure := 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) { secure := 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) } diff --git a/server/utils/crypto.go b/server/utils/crypto.go new file mode 100644 index 0000000..de7ce39 --- /dev/null +++ b/server/utils/crypto.go @@ -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 +} diff --git a/server/utils/email.go b/server/utils/email.go index 29c1ebc..3e4b9b2 100644 --- a/server/utils/email.go +++ b/server/utils/email.go @@ -100,7 +100,7 @@ func SendVerificationMail(toEmail, token string) error {
- `, 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) return sender.SendMail(Receiver, Subject, bodyMessage) @@ -108,8 +108,8 @@ func SendVerificationMail(toEmail, token string) error { // SendForgotPasswordMail to send verification email func SendForgotPasswordMail(toEmail, token, host string) error { - if constants.RESET_PASSWORD_URL == "" { - constants.RESET_PASSWORD_URL = constants.AUTHORIZER_URL + "/app/reset-password" + if constants.EnvData.RESET_PASSWORD_URL == "" { + constants.EnvData.RESET_PASSWORD_URL = constants.EnvData.AUTHORIZER_URL + "/app/reset-password" } sender := email.NewSender() @@ -204,7 +204,7 @@ func SendForgotPasswordMail(toEmail, token, host string) error {
- `, 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) diff --git a/server/utils/meta.go b/server/utils/meta.go index e7c2e6e..115abc6 100644 --- a/server/utils/meta.go +++ b/server/utils/meta.go @@ -9,12 +9,12 @@ import ( // version, func GetMetaInfo() model.Meta { return model.Meta{ - Version: constants.VERSION, - IsGoogleLoginEnabled: constants.GOOGLE_CLIENT_ID != "" && constants.GOOGLE_CLIENT_SECRET != "", - IsGithubLoginEnabled: constants.GITHUB_CLIENT_ID != "" && constants.GOOGLE_CLIENT_SECRET != "", - IsFacebookLoginEnabled: constants.FACEBOOK_CLIENT_ID != "" && constants.FACEBOOK_CLIENT_SECRET != "", - IsBasicAuthenticationEnabled: !constants.DISABLE_BASIC_AUTHENTICATION, - IsEmailVerificationEnabled: !constants.DISABLE_EMAIL_VERIFICATION, - IsMagicLinkLoginEnabled: !constants.DISABLE_MAGIC_LINK_LOGIN, + Version: constants.EnvData.VERSION, + IsGoogleLoginEnabled: constants.EnvData.GOOGLE_CLIENT_ID != "" && constants.EnvData.GOOGLE_CLIENT_SECRET != "", + IsGithubLoginEnabled: constants.EnvData.GITHUB_CLIENT_ID != "" && constants.EnvData.GOOGLE_CLIENT_SECRET != "", + IsFacebookLoginEnabled: constants.EnvData.FACEBOOK_CLIENT_ID != "" && constants.EnvData.FACEBOOK_CLIENT_SECRET != "", + IsBasicAuthenticationEnabled: !constants.EnvData.DISABLE_BASIC_AUTHENTICATION, + IsEmailVerificationEnabled: !constants.EnvData.DISABLE_EMAIL_VERIFICATION, + IsMagicLinkLoginEnabled: !constants.EnvData.DISABLE_MAGIC_LINK_LOGIN, } } diff --git a/server/utils/validator.go b/server/utils/validator.go index 4c0c6ed..74b718a 100644 --- a/server/utils/validator.go +++ b/server/utils/validator.go @@ -16,7 +16,7 @@ func IsValidEmail(email 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 } @@ -24,7 +24,7 @@ func IsValidOrigin(url string) bool { hostName, port := GetHostParts(url) currentOrigin := hostName + ":" + port - for _, origin := range constants.ALLOWED_ORIGINS { + for _, origin := range constants.EnvData.ALLOWED_ORIGINS { replacedString := origin // if has regex whitelisted domains if strings.Contains(origin, "*") { @@ -50,12 +50,17 @@ func IsValidOrigin(url string) bool { } func IsSuperAdmin(gc *gin.Context) bool { - secret := gc.Request.Header.Get("x-authorizer-admin-secret") - if secret == "" { - return false + token, err := GetAdminAuthToken(gc) + if err != nil { + secret := gc.Request.Header.Get("x-authorizer-admin-secret") + if secret == "" { + return false + } + + return secret == constants.EnvData.ADMIN_SECRET } - return secret == constants.ADMIN_SECRET + return token != "" } func IsValidRoles(userRoles []string, roles []string) bool { diff --git a/server/utils/verification_token.go b/server/utils/verification_token.go index 7dbef67..254e8ac 100644 --- a/server/utils/verification_token.go +++ b/server/utils/verification_token.go @@ -20,23 +20,23 @@ type CustomClaim struct { } 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{ &jwt.StandardClaims{ ExpiresAt: time.Now().Add(time.Minute * 30).Unix(), }, 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) { claims := &CustomClaim{} _, 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 { return claims, err