diff --git a/controller/config/config.go b/controller/config/config.go index 29f8a9f28b2405298559720e474b04132357c089..eace6314509c2d0af78427e3fcaaac08deafe104 100644 --- a/controller/config/config.go +++ b/controller/config/config.go @@ -19,6 +19,8 @@ const ( defaultJWTDuration = time.Hour * 24 jwtSecretKey = "jwtSecret" gNMISubscriptionsFilePathKey = "gNMISubscriptionsPath" + maxTokensPerUserKey = "maxTokensPerUser" + maxTokensPerUserDefault = 100 // RabbitMQ Broker. amqpPrefixKey = "amqpPrefix" @@ -81,6 +83,9 @@ var CertFilePath string // KeyFilePath si the path to the private key that the controller should use for TLS connections. var KeyFilePath string +// MaxTokensPerUser is the maximum number of tokens a user can have. This determiens the maximum of concurrent logged in sessions per user. +var MaxTokensPerUser int + // Init gets called on module import. func Init() { err := InitializeConfig() @@ -107,9 +112,9 @@ func InitializeConfig() error { setLogLevel() - DatabaseConnection = getStringFromViper(databaseConnectionKey) + DatabaseConnection = viper.GetString(databaseConnectionKey) - FilesystemPathToStores = getStringFromViper(filesystemPathToStores) + FilesystemPathToStores = viper.GetString(filesystemPathToStores) if FilesystemPathToStores == "" { FilesystemPathToStores = "stores" } @@ -121,15 +126,20 @@ func InitializeConfig() error { JWTSecret = viper.GetString(jwtSecretKey) - GNMISubscriptionsFilePath = getStringFromViper(gNMISubscriptionsFilePathKey) + MaxTokensPerUser = viper.GetInt(maxTokensPerUserKey) + if MaxTokensPerUser == 0 { + MaxTokensPerUser = maxTokensPerUserDefault + } + + GNMISubscriptionsFilePath = viper.GetString(gNMISubscriptionsFilePathKey) loadAMQPConfig() - CAFilePath = getStringFromViper(tlsCACertFileKey) + CAFilePath = viper.GetString(tlsCACertFileKey) - CertFilePath = getStringFromViper(tlsCertFileKey) + CertFilePath = viper.GetString(tlsCertFileKey) - KeyFilePath = getStringFromViper(tlsKeyFileKey) + KeyFilePath = viper.GetString(tlsKeyFileKey) if err := viper.WriteConfig(); err != nil { return err @@ -161,12 +171,6 @@ func getUUIDFromViper(viperKey string) (uuid.UUID, error) { return parsedUUID, nil } -func getStringFromViper(viperKey string) string { - stringFromViper := viper.GetString(viperKey) - - return stringFromViper -} - func setChangeTimeout() error { e := os.Getenv(changeTimeoutKey) if e != "" { @@ -202,9 +206,9 @@ func getDurationFromViper(viperKey, unit string) (time.Duration, error) { } func loadAMQPConfig() { - AMQPPrefix = getStringFromViper(amqpPrefixKey) - AMQPUser = getStringFromViper(amqpUserKey) - AMQPPassword = getStringFromViper(amqpPasswordKey) - AMQPHost = getStringFromViper(amqpHostKey) - AMQPPort = getStringFromViper(amqpPortKey) + AMQPPrefix = viper.GetString(amqpPrefixKey) + AMQPUser = viper.GetString(amqpUserKey) + AMQPPassword = viper.GetString(amqpPasswordKey) + AMQPHost = viper.GetString(amqpHostKey) + AMQPPort = viper.GetString(amqpPortKey) } diff --git a/controller/configs/basic-docker-compose.toml b/controller/configs/basic-docker-compose.toml index 2708de32e81c6101ffcf2da807de72ee86065d4d..d14da89b04f9d107f22f9e237f0e68cf36ed2df8 100644 --- a/controller/configs/basic-docker-compose.toml +++ b/controller/configs/basic-docker-compose.toml @@ -16,6 +16,7 @@ plugin-folder = 'plugins' plugin-registry = 'plugin-registry:55057' security = 'insecure' socket = ':55055' +maxTokensPerUser = 100 tlscacertfile = '/ssl/ca.crt' tlscertfile = '/ssl/certs/gosdn-selfsigned.crt' tlskeyfile = '/ssl/private/gosdn-selfsigned.key' diff --git a/controller/configs/containerlab-gosdn.toml.example b/controller/configs/containerlab-gosdn.toml.example index e5ed6652053ef3f64a929e863cf05a8f07758032..4b3e0668ae77b65fc85b0ff2681efa018f174f67 100644 --- a/controller/configs/containerlab-gosdn.toml.example +++ b/controller/configs/containerlab-gosdn.toml.example @@ -10,6 +10,7 @@ gNMISubscriptionsPath = "configs/gNMISubscriptions.txt" tlscertfile = '/ssl/certs/gosdn-selfsigned.crt' tlskeyfile = '/ssl/private/gosdn-selfsigned.key' tlscacertfile = '/ssl/ca.crt' +maxTokensPerUser = 100 amqpPrefix = "amqp://" amqpUser = "guest" diff --git a/controller/configs/development-gosdn.toml.example b/controller/configs/development-gosdn.toml.example index 4845c082c2acb6476c48b4e313ee8578a13d3c87..56bc0f26184b2fcd9fd3e5787afe56c0ce4664e0 100644 --- a/controller/configs/development-gosdn.toml.example +++ b/controller/configs/development-gosdn.toml.example @@ -15,6 +15,7 @@ log-level = 'debug' plugin-folder = 'plugins' security = 'insecure' socket = ':55055' +maxTokensPerUser = 100 tlscertfile = '/ssl/certs/gosdn-selfsigned.crt' tlskeyfile = '/ssl/private/gosdn-selfsigned.key' tlscacertfile = '/ssl/ca.crt' diff --git a/controller/configs/integration-test-gosdn.toml b/controller/configs/integration-test-gosdn.toml index 2708de32e81c6101ffcf2da807de72ee86065d4d..d14da89b04f9d107f22f9e237f0e68cf36ed2df8 100644 --- a/controller/configs/integration-test-gosdn.toml +++ b/controller/configs/integration-test-gosdn.toml @@ -16,6 +16,7 @@ plugin-folder = 'plugins' plugin-registry = 'plugin-registry:55057' security = 'insecure' socket = ':55055' +maxTokensPerUser = 100 tlscacertfile = '/ssl/ca.crt' tlscertfile = '/ssl/certs/gosdn-selfsigned.crt' tlskeyfile = '/ssl/private/gosdn-selfsigned.key' diff --git a/controller/controller.go b/controller/controller.go index 1ae253a72db34fb7e2353b1b64cad430e65d33aa..3be1d667180a715dcc138c5b135771b4691062d8 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -10,6 +10,7 @@ import ( "os/signal" "sync" "syscall" + "time" "github.com/google/uuid" "github.com/sethvargo/go-password/password" @@ -172,6 +173,10 @@ func initialize() error { return err } + if err := deletAllExpiredUserTokens(); err != nil { + return err + } + if err := startGrpc(); err != nil { return err } @@ -441,6 +446,40 @@ func ensureDefaultUserExists() error { return nil } +func deletAllExpiredUserTokens() error { + // Temporarly create JWT manager just to evaluate tokens here + jwtManager := rbacImpl.NewJWTManager(config.JWTSecret, config.JWTDuration) + + users, err := c.userService.GetAll() + if err != nil { + return fmt.Errorf("error getting all users while deleting expires user tokens: %w", err) + } + for i, user := range users { + tokens := user.GetTokens() + for _, token := range tokens { + claims, err := jwtManager.GetClaimsFromToken(token) + if err != nil { + return fmt.Errorf("error getting claims from token while deleting expired user tokens: %w", err) + } + if claims.ExpiresAt < time.Now().Unix() { + err := users[i].RemoveToken(token) + if err != nil { + return fmt.Errorf("error removing token while deleting expired user tokens: %w", err) + } + } + } + } + + for _, user := range users { + err := c.userService.Update(user) + if err != nil { + return fmt.Errorf("error updating user while deleting expired user tokens: %w", err) + } + } + return nil + +} + // Run calls initialize to start the controller. func Run(ctx context.Context) error { var initError error