From 3ca5db464914a6f096f114293986700bd28909e4 Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 20 Oct 2023 16:08:37 +0000 Subject: [PATCH] Prepare databases --- .gitignore | 2 + cmd/root.go | 96 +++++++++++++++++++++++++++++++--- cmd/serve.go | 14 +++++ go.mod | 7 ++- go.sum | 13 +++++ internal/database/locker.go | 16 ++++++ internal/database/mongodb.go | 33 +++++++----- internal/database/redis.go | 40 ++++++++++++++ internal/entities/entity.go | 9 +++- internal/entities/user/db.go | 78 +++++++++++++++++++++++++-- internal/entities/user/user.go | 9 ++++ internal/logger/logger.go | 21 ++++++++ internal/machines/session.go | 29 +++++----- internal/server/server.go | 3 ++ 14 files changed, 330 insertions(+), 40 deletions(-) create mode 100644 internal/database/locker.go create mode 100644 internal/database/redis.go create mode 100644 internal/logger/logger.go diff --git a/.gitignore b/.gitignore index a7bc6fb..60048fc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +/config.yaml + ### VisualStudioCode ### .vscode/* !.vscode/settings.json diff --git a/cmd/root.go b/cmd/root.go index 6ea4827..5bded10 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -2,10 +2,16 @@ package cmd import ( "os" + "time" + "git.1in9.net/raider/wroofauth/internal/logger" "github.com/spf13/cobra" + "github.com/spf13/viper" + "go.uber.org/zap" ) +var cfgFile string + // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ Use: "wroofauth", @@ -31,13 +37,89 @@ func Execute() { } func init() { - // Here you will define your flags and configuration settings. - // Cobra supports persistent flags, which, if defined here, - // will be global for your application. + rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is ./config.yaml)") - // rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.wroofauth.yaml)") + viper.SetDefault("development", false) - // Cobra also supports local flags, which will only run - // when this action is called directly. - rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") + viper.BindEnv("development", "DEV") + + logger.StartLogger() + + viper.SetDefault("mongo.uri", "mongodb://localhost:27017") + viper.SetDefault("mongo.database", "wroofauth") + viper.SetDefault("mongo.collection.users", "users") + viper.SetDefault("mongo.collection.clients", "clients") + viper.SetDefault("mongo.collection.groups", "groups") + + viper.BindEnv("mongo.uri", "MONGO_URI", "MONGODB_URI") + viper.BindEnv("mongo.database", "MONGO_DATABASE", "MONGODB_DATABASE") + + viper.SetDefault("redis.addr", "localhost:6379") + viper.SetDefault("redis.username", "") + viper.SetDefault("redis.password", "") + viper.SetDefault("redis.db", 0) + + viper.BindEnv("redis.addr", "REDIS_ADDRESS", "REDIS_ADDR") + viper.BindEnv("redis.password", "REDIS_PASSWORD", "REDIS_PASS") + viper.BindEnv("redis.db", "REDIS_DB") + + /*viper.SetDefault("http.wyrd_url", "http://localhost:3001") + viper.SetDefault("http.frontend_url", "http://localhost:3000") + viper.BindEnv("http.wyrd_url", "WYRD_URL") + viper.BindEnv("http.frontend_url", "WYRD_FRONTEND_URL")*/ + + /*viper.SetDefault("crypto.keys", jwk.NewSet()) + viper.SetDefault("crypto.keyfile", nil) + viper.SetDefault("crypto.use_key", "") + viper.BindEnv("crypto.keyfile", "WYRD_KEYFILE")*/ + + viper.SetDefault("totp.issuer", "WroofAuth") // Used for 2fa issuer value + + cobra.OnInitialize(loadConfig) +} + +func loadConfig() { + if etcdUrl, found := os.LookupEnv("ETCD_URL"); found { + etcdPath, found := os.LookupEnv("ETCD_WROOF_CONFIG") + + if !found { + etcdPath = "/config/wroofauth.json" + } + viper.SetConfigType("json") + + viper.AddRemoteProvider("etcd3", etcdUrl, etcdPath) + err := viper.ReadRemoteConfig() + + if err != nil { + logger.Sugar.Fatal(err) + } + + go func() { + for { + time.Sleep(time.Second * 5) + + err := viper.WatchRemoteConfig() + if err != nil { + logger.Sugar.Fatal("unable to read remote config: %v", err) + continue + } + } + }() + } + + if cfgFile != "" { + viper.SetConfigFile(cfgFile) + } else { + viper.AddConfigPath(".") + viper.SetConfigName("config") + } + + err := viper.ReadInConfig() + if err != nil { + logger.Logger.Fatal("failed to load config", zap.Error(err)) + } + + logger.StartLogger() // Restart Logger, as we may have changed our loglevel + + logger.Sugar.Info("Using config file:", viper.ConfigFileUsed()) } diff --git a/cmd/serve.go b/cmd/serve.go index d7def5c..25bdf77 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -1,6 +1,10 @@ package cmd import ( + "context" + + "git.1in9.net/raider/wroofauth/internal/database" + "git.1in9.net/raider/wroofauth/internal/entities" "git.1in9.net/raider/wroofauth/internal/server" "github.com/spf13/cobra" ) @@ -16,7 +20,17 @@ Cobra is a CLI library for Go that empowers applications. This application is a tool to generate the needed files to quickly create a Cobra application.`, Run: func(cmd *cobra.Command, args []string) { + ctx := context.Background() + + database.MongoConnect(ctx) + database.RedisConnect(ctx) + + entities.SetupEntityDatabases() + server.Serve() + + database.MongoDisconnect(ctx) + database.RedisDisconnect() }, } diff --git a/go.mod b/go.mod index d1979a8..133669f 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,11 @@ go 1.18 require ( github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect + github.com/bsm/redislock v0.9.4 // indirect github.com/cenkalti/backoff/v4 v4.1.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.3 // indirect @@ -19,6 +22,7 @@ require ( github.com/pelletier/go-toml v1.9.4 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/pquerna/otp v1.4.0 // indirect + github.com/redis/go-redis/v9 v9.2.1 // indirect github.com/rs/cors v1.7.0 // indirect github.com/sagikazarmark/locafero v0.3.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect @@ -37,7 +41,8 @@ require ( github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect go.mongodb.org/mongo-driver v1.12.1 // indirect go.uber.org/atomic v1.9.0 // indirect - go.uber.org/multierr v1.9.0 // indirect + go.uber.org/multierr v1.10.0 // indirect + go.uber.org/zap v1.26.0 // indirect golang.org/x/crypto v0.14.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/net v0.15.0 // indirect diff --git a/go.sum b/go.sum index 628a1ce..8d77de1 100644 --- a/go.sum +++ b/go.sum @@ -81,6 +81,8 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/bsm/redislock v0.9.4 h1:X/Wse1DPpiQgHbVYRE9zv6m070UcKoOGekgvpNhiSvw= +github.com/bsm/redislock v0.9.4/go.mod h1:Epf7AJLiSFwLCiZcfi6pWFO/8eAYrYpQXFxEDPoDeAk= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= @@ -88,9 +90,12 @@ github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1P github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -124,6 +129,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= @@ -469,6 +476,8 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/redis/go-redis/v9 v9.2.1 h1:WlYJg71ODF0dVspZZCpYmoF1+U1Jjk9Rwd7pq6QmlCg= +github.com/redis/go-redis/v9 v9.2.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -591,10 +600,14 @@ go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+ go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= diff --git a/internal/database/locker.go b/internal/database/locker.go new file mode 100644 index 0000000..3e1426d --- /dev/null +++ b/internal/database/locker.go @@ -0,0 +1,16 @@ +package database + +import ( + "time" + + "github.com/bsm/redislock" +) + +var ( + Locker *redislock.Client + LockBackoff = redislock.LimitRetry(redislock.LinearBackoff(100*time.Millisecond), 5) +) + +func setupLocker() { + Locker = redislock.New(Redis) +} diff --git a/internal/database/mongodb.go b/internal/database/mongodb.go index f893d68..efecef4 100644 --- a/internal/database/mongodb.go +++ b/internal/database/mongodb.go @@ -3,48 +3,53 @@ package database import ( "context" + "git.1in9.net/raider/wroofauth/internal/logger" "github.com/spf13/viper" "go.mongodb.org/mongo-driver/event" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" "go.mongodb.org/mongo-driver/mongo/readpref" + "go.uber.org/zap" ) var dbClient *mongo.Client -var MongoDatabase *mongo.Database +var Mongo *mongo.Database + +func MongoConnect(ctx context.Context) { + logger.Logger.Info("establishing connection to mongoDB...") -func Connect() { cmdMonitor := &event.CommandMonitor{ Started: func(_ context.Context, evt *event.CommandStartedEvent) { - // TODO: Log + logger.Logger.Debug("mongoDB command running", zap.String("command", evt.Command.String())) }, } - client, err := mongo.Connect(context.TODO(), options.Client().ApplyURI(viper.GetString("mongo.uri")).SetMonitor(cmdMonitor)) + client, err := mongo.Connect(ctx, options.Client().ApplyURI(viper.GetString("mongo.uri")).SetMonitor(cmdMonitor)) if err != nil { - // TODO: Log - panic(err) + logger.Logger.Fatal("unable to connect to MongoDB", zap.Error(err)) } - err = client.Ping(context.TODO(), readpref.Nearest()) + err = client.Ping(ctx, readpref.Nearest()) if err != nil { - // TODO: Log - panic(err) + logger.Logger.Fatal("unable to ping to MongoDB", zap.Error(err)) } + logger.Logger.Info("mongoDB connection established") + dbClient = client - MongoDatabase = client.Database(viper.GetString("mongo.database")) + Mongo = client.Database(viper.GetString("mongo.database")) //UserCollection = Database.Collection(viper.GetString("mongo.collection.users")) //ClientCollection = Database.Collection(viper.GetString("mongo.collection.clients")) //GroupCollection = Database.Collection(viper.GetString("mongo.collection.groups")) } -func Disconnect() { - err := dbClient.Disconnect(context.TODO()) +func MongoDisconnect(ctx context.Context) { + err := dbClient.Disconnect(ctx) if err != nil { - // TODO: Log - panic(err) + logger.Logger.Warn("failed to gracefully disconnect MongoDB", zap.Error(err)) } + + logger.Logger.Info("mongoDB gracefully disconnected") } diff --git a/internal/database/redis.go b/internal/database/redis.go new file mode 100644 index 0000000..002e114 --- /dev/null +++ b/internal/database/redis.go @@ -0,0 +1,40 @@ +package database + +import ( + "context" + + "git.1in9.net/raider/wroofauth/internal/logger" + "github.com/redis/go-redis/v9" + "github.com/spf13/viper" + "go.uber.org/zap" +) + +var Redis *redis.Client + +func RedisConnect(ctx context.Context) { + logger.Logger.Info("establishing connection to redis...") + + Redis = redis.NewClient(&redis.Options{ + Addr: viper.GetString("redis.addr"), + Username: viper.GetString("redis.username"), + Password: viper.GetString("redis.password"), + DB: viper.GetInt("redis.db"), + }) + + err := Redis.Ping(ctx).Err() + if err != nil { + logger.Logger.Fatal("unable to ping to redis", zap.Error(err)) + } + + setupLocker() + + logger.Logger.Info("redis connection established") +} + +func RedisDisconnect() { + err := Redis.Close() + if err != nil { + logger.Logger.Warn("failed to gracefully disconnect redis", zap.Error(err)) + } + logger.Logger.Info("redis gracefully disconnected") +} diff --git a/internal/entities/entity.go b/internal/entities/entity.go index f8b7e9d..db55814 100644 --- a/internal/entities/entity.go +++ b/internal/entities/entity.go @@ -1,8 +1,15 @@ package entities -import "go.mongodb.org/mongo-driver/bson/primitive" +import ( + "git.1in9.net/raider/wroofauth/internal/entities/user" + "go.mongodb.org/mongo-driver/bson/primitive" +) type Entity interface { GetType() string GetID() primitive.ObjectID } + +func SetupEntityDatabases() { + user.SetupCollection() +} diff --git a/internal/entities/user/db.go b/internal/entities/user/db.go index b03dd2f..e4e28b1 100644 --- a/internal/entities/user/db.go +++ b/internal/entities/user/db.go @@ -1,5 +1,77 @@ package user -func GetByIdentification(identification string) (*User, error) { - return nil, nil -} \ No newline at end of file +import ( + "context" + + "git.1in9.net/raider/wroofauth/internal/database" + "git.1in9.net/raider/wroofauth/internal/logger" + "github.com/spf13/viper" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +var collection *mongo.Collection + +func SetupCollection() { + collection = database.Mongo.Collection(viper.GetString("mongo.collection.users")) +} + +func (u *User) Persist(ctx context.Context) error { + return Persist(ctx, u) +} + +func Persist(ctx context.Context, user *User) error { + if user == nil { + logger.Logger.Panic("trying to persist null-pointer") + } + + // TODO: Validate? + + upsert := true + + _, err := collection.ReplaceOne(ctx, bson.M{"_id": user.ID}, user, &options.ReplaceOptions{Upsert: &upsert}) + if err != nil { + return err + } + + return nil +} + +func GetById(ctx context.Context, id primitive.ObjectID) (*User, error) { + res := collection.FindOne(ctx, bson.M{"_id": id}) + if res.Err() != nil { + return nil, res.Err() + } + + var user User + err := res.Decode(&user) + if err != nil { + return nil, err + } + + return &user, nil +} + +func GetByIdentification(ctx context.Context, identification string) (*User, error) { + res := collection.FindOne(ctx, bson.D{ + {Key: "$or", + Value: bson.A{ + bson.D{{Key: "username", Value: identification}}, + bson.D{{Key: "email", Value: identification}}, + }, + }, + }) + if res.Err() != nil { + return nil, res.Err() + } + + var user User + err := res.Decode(&user) + if err != nil { + return nil, err + } + + return &user, nil +} diff --git a/internal/entities/user/user.go b/internal/entities/user/user.go index 1d6f971..e19a205 100644 --- a/internal/entities/user/user.go +++ b/internal/entities/user/user.go @@ -1,6 +1,7 @@ package user import ( + "context" "crypto/subtle" "encoding/base64" "errors" @@ -8,10 +9,12 @@ import ( "strings" "time" + "github.com/bsm/redislock" "github.com/pquerna/otp/totp" "github.com/spf13/viper" "golang.org/x/crypto/argon2" + "git.1in9.net/raider/wroofauth/internal/database" "git.1in9.net/raider/wroofauth/internal/parameters" "go.mongodb.org/mongo-driver/bson/primitive" ) @@ -50,6 +53,12 @@ func (u *User) GetID() primitive.ObjectID { return u.ID } +func (u *User) Lock(ctx context.Context) (*redislock.Lock, error) { + return database.Locker.Obtain(ctx, u.ID.Hex(), 100*time.Millisecond, &redislock.Options{ + RetryStrategy: database.LockBackoff, + }) +} + func (u *User) GetFriendlyIdentifier() string { if u.Username != nil { return *u.Username diff --git a/internal/logger/logger.go b/internal/logger/logger.go new file mode 100644 index 0000000..a857d62 --- /dev/null +++ b/internal/logger/logger.go @@ -0,0 +1,21 @@ +package logger + +import ( + "github.com/spf13/viper" + "go.uber.org/zap" +) + +var ( + Logger *zap.Logger + Sugar *zap.SugaredLogger +) + +func StartLogger() { + Logger, _ = zap.NewProduction() + + if viper.GetBool("development") { + Logger, _ = zap.NewDevelopment() + } + + Sugar = Logger.Sugar() +} diff --git a/internal/machines/session.go b/internal/machines/session.go index 6bd4d28..d6cf0a0 100644 --- a/internal/machines/session.go +++ b/internal/machines/session.go @@ -1,6 +1,7 @@ package machines import ( + "context" "errors" "strings" @@ -49,7 +50,7 @@ type Session struct { User *user.User } -func NewSession() *Session { +func NewSession(ctx context.Context) *Session { return &Session{ State: SessionState_EMPTY, AuthenticationMethod: AuthenticationMethod_NONE, @@ -58,7 +59,7 @@ func NewSession() *Session { } // s.Validate checks if the session is in a valid state -func (s *Session) Validate() error { +func (s *Session) Validate(ctx context.Context) error { if s.IsAnyAuthenticated() { if s.User == nil { // We can only be here if a user is set @@ -84,7 +85,7 @@ func (s *Session) IsAnyAuthenticated() bool { return strings.HasPrefix(string(s.State), "AUTHENTICATED_") } -func (s *Session) performPreflight() error { +func (s *Session) performPreflight(ctx context.Context) error { // TODO: Do Preflight Checks. // TODO: Do PASSWORD_EXPIRED check @@ -103,12 +104,12 @@ func (s *Session) performPreflight() error { return nil } -func (s *Session) HandleIdentification(identification string) error { +func (s *Session) HandleIdentification(ctx context.Context, identification string) error { if s.State != SessionState_EMPTY { return ErrIllegalStateAction // This step may only run on EMPTY sessions } - user, err := user.GetByIdentification(identification) + user, err := user.GetByIdentification(ctx, identification) if err != nil { return err } @@ -118,15 +119,15 @@ func (s *Session) HandleIdentification(identification string) error { } s.User = user - + //TODO: Check for SAML s.State = SessionState_UNAUTHENTICATED - + return nil } -func (s *Session) HandlePassword(password string) error { +func (s *Session) HandlePassword(ctx context.Context, password string) error { if s.State != SessionState_UNAUTHENTICATED { return ErrIllegalStateAction // This step may only run on UNAUTHENTICATED sessions } @@ -142,7 +143,7 @@ func (s *Session) HandlePassword(password string) error { if !s.User.Needs2FA() { // No 2fa, jump to AUTHENTICATED_PENDING for preflight s.State = SessionState_AUTHENTICATED_PENDING - return s.performPreflight() + return s.performPreflight(ctx) } s.State = SessionState_AWAITING_FACTOR @@ -151,7 +152,7 @@ func (s *Session) HandlePassword(password string) error { // TODO: Passkey action -func (s *Session) HandleTOTP(otp string) error { +func (s *Session) HandleTOTP(ctx context.Context, otp string) error { if s.State != SessionState_AWAITING_FACTOR { return ErrIllegalStateAction } @@ -166,10 +167,10 @@ func (s *Session) HandleTOTP(otp string) error { // Good to go for preflight. s.State = SessionState_AUTHENTICATED_PENDING - return s.performPreflight() + return s.performPreflight(ctx) } -func (s *Session) HandleLock() error { +func (s *Session) HandleLock(ctx context.Context) error { if !s.IsAnyAuthenticated() { return ErrIllegalStateAction } @@ -177,7 +178,7 @@ func (s *Session) HandleLock() error { return nil } -func (s *Session) HandleLogout() error { +func (s *Session) HandleLogout(ctx context.Context) error { if !s.IsAnyAuthenticated() { return ErrIllegalStateAction } @@ -185,7 +186,7 @@ func (s *Session) HandleLogout() error { return nil } -func (s *Session) Destroy() error { +func (s *Session) Destroy(ctx context.Context) error { // TODO: Destroy Session return nil } diff --git a/internal/server/server.go b/internal/server/server.go index 684e5f9..5975479 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -1,4 +1,7 @@ package server +import "log" + func Serve() { + log.Println("hello") }