diff --git a/go.mod b/go.mod index 25a08e2..aa77cde 100644 --- a/go.mod +++ b/go.mod @@ -71,7 +71,7 @@ require ( github.com/google/go-github/v28 v28.1.1 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/gorilla/mux v1.8.1 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect github.com/hashicorp/go-version v1.7.0 // indirect github.com/http-wasm/http-wasm-host-go v0.7.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect @@ -115,7 +115,7 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect google.golang.org/grpc v1.73.0 // indirect gotest.tools/v3 v3.5.2 // indirect - modernc.org/libc v1.66.0 // indirect + modernc.org/libc v1.66.1 // indirect modernc.org/mathutil v1.7.1 // indirect modernc.org/memory v1.11.0 // indirect ) diff --git a/go.sum b/go.sum index 9cdfca8..1a08b53 100644 --- a/go.sum +++ b/go.sum @@ -130,8 +130,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.0 h1:+epNPbD5EqgpEMm5wrl4Hqts3jZt8+kYaqUisuuIGTk= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.0/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90= github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/http-wasm/http-wasm-host-go v0.7.0 h1:+1KrRyOO6tWiDB24QrtSYyDmzFLBBs3jioKaUT0mq1c= @@ -323,8 +323,8 @@ modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= modernc.org/goabi0 v0.0.3 h1:y81b9r3asCh6Xtse6Nz85aYGB0cG3M3U6222yap1KWI= modernc.org/goabi0 v0.0.3/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI= -modernc.org/libc v1.66.0 h1:eoFuDb1ozurUY5WSWlgvxHp0FuL+AncMwNjFqGYMJPQ= -modernc.org/libc v1.66.0/go.mod h1:AiZxInURfEJx516LqEaFcrC+X38rt9G7+8ojIXQKHbo= +modernc.org/libc v1.66.1 h1:4uQsntXbVyAgrV+j6NhKvDiUypoJL48BWQx6sy9y8ok= +modernc.org/libc v1.66.1/go.mod h1:AiZxInURfEJx516LqEaFcrC+X38rt9G7+8ojIXQKHbo= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= diff --git a/internal/api/handler/oidc.go b/internal/api/handler/oidc.go index e66bc98..f8a3066 100644 --- a/internal/api/handler/oidc.go +++ b/internal/api/handler/oidc.go @@ -4,11 +4,9 @@ import ( "context" "crypto/rand" "encoding/base64" - "encoding/json" + "errors" "fmt" - "log/slog" "net/http" - "net/url" "strings" "time" @@ -16,7 +14,7 @@ import ( "github.com/mizuchilabs/mantrae/internal/config" "github.com/mizuchilabs/mantrae/internal/settings" "github.com/mizuchilabs/mantrae/internal/store/db" - "github.com/mizuchilabs/mantrae/internal/util" + "github.com/mizuchilabs/mantrae/pkg/meta" "golang.org/x/oauth2" ) @@ -41,17 +39,12 @@ type OIDCUserInfo struct { func OIDCLogin(a *config.App) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - oidcConfig, oauth2Config, _, err := setupOIDCConfig(r.Context(), a) + oauth2Config, _, err := getOIDCConfig(r.Context(), r, a) if err != nil { http.Error(w, "OIDC not configured: "+err.Error(), http.StatusServiceUnavailable) return } - if !oidcConfig.Enabled { - http.Error(w, "OIDC disabled", http.StatusServiceUnavailable) - return - } - // Generate state for CSRF protection state, err := generateRandomState() if err != nil { @@ -73,7 +66,7 @@ func OIDCLogin(a *config.App) http.HandlerFunc { opts := []oauth2.AuthCodeOption{oauth2.AccessTypeOffline} // Add PKCE if enabled - if oidcConfig.UsePKCE { + if oauth2Config.ClientSecret == "" { verifier := oauth2.GenerateVerifier() http.SetCookie(w, &http.Cookie{ Name: "pkce_verifier", @@ -94,7 +87,7 @@ func OIDCLogin(a *config.App) http.HandlerFunc { func OIDCCallback(a *config.App) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - oidcConfig, oauth2Config, verifier, err := setupOIDCConfig(r.Context(), a) + oauth2Config, verifier, err := getOIDCConfig(r.Context(), r, a) if err != nil { http.Error(w, "OIDC not configured: "+err.Error(), http.StatusServiceUnavailable) return @@ -125,7 +118,7 @@ func OIDCCallback(a *config.App) http.HandlerFunc { opts := []oauth2.AuthCodeOption{} // Handle PKCE - if oidcConfig.UsePKCE { + if oauth2Config.ClientSecret == "" { verifierCookie, err := r.Cookie("pkce_verifier") if err != nil { http.Error(w, "PKCE verifier not found", http.StatusBadRequest) @@ -194,9 +187,9 @@ func OIDCCallback(a *config.App) http.HandlerFunc { return } - // Generate JWT and set cookie + // Generate JWT expirationTime := time.Now().Add(24 * time.Hour) - jwtToken, err := util.EncodeUserJWT(user.Username, a.Secret, expirationTime) + jwtToken, err := meta.EncodeUserToken(user.ID, a.Secret, expirationTime) if err != nil { http.Error(w, "Failed to generate JWT", http.StatusInternalServerError) return @@ -207,7 +200,7 @@ func OIDCCallback(a *config.App) http.HandlerFunc { } http.SetCookie(w, &http.Cookie{ - Name: util.CookieName, + Name: meta.CookieName, Value: jwtToken, Path: "/", MaxAge: int(expirationTime.Unix() - time.Now().Unix()), @@ -215,106 +208,49 @@ func OIDCCallback(a *config.App) http.HandlerFunc { Secure: r.TLS != nil, SameSite: http.SameSiteLaxMode, }) - http.Redirect(w, r, "/", http.StatusTemporaryRedirect) } } -func OIDCStatus(a *config.App) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - oidcConfig, _, _, err := setupOIDCConfig(r.Context(), a) - if err != nil { - slog.Error("Failed to get OIDC config", "error", err) - } - - response := map[string]interface{}{ - "enabled": false, - "provider": "", - "loginDisabled": false, - } - - if err == nil && oidcConfig != nil { - providerName, _ := a.SM.Get(settings.KeyOIDCProviderName) - pwLogin, _ := a.SM.Get(settings.KeyPasswordLoginEnabled) - response["enabled"] = oidcConfig.Enabled - response["provider"] = providerName - response["loginDisabled"] = settings.AsBool(pwLogin) - } - - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(response) - } -} - -// Helper function that handles both config and OIDC setup -func setupOIDCConfig( +func getOIDCConfig( ctx context.Context, + r *http.Request, a *config.App, -) (*OIDCConfig, *oauth2.Config, *oidc.IDTokenVerifier, error) { - config, err := getOIDCConfig(ctx, a) - if err != nil { - return nil, nil, nil, err - } - - // Create OIDC provider - provider, err := oidc.NewProvider(ctx, strings.TrimSuffix(config.IssuerURL, "/")) - if err != nil { - return nil, nil, nil, fmt.Errorf("failed to create OIDC provider: %w", err) - } - - // Create OAuth2 config - oauth2Config := &oauth2.Config{ - ClientID: config.ClientID, - ClientSecret: config.ClientSecret, - RedirectURL: config.RedirectURL, - Endpoint: provider.Endpoint(), - Scopes: config.Scopes, - } - - // For PKCE, don't include client secret - if config.UsePKCE { - oauth2Config.ClientSecret = "" - } - - // Create ID token verifier - verifier := provider.Verifier(&oidc.Config{ - ClientID: config.ClientID, - }) - - return config, oauth2Config, verifier, nil -} - -func getOIDCConfig(ctx context.Context, a *config.App) (*OIDCConfig, error) { - config := &OIDCConfig{Scopes: []string{"openid", "email", "profile"}} +) (*oauth2.Config, *oidc.IDTokenVerifier, error) { sets := a.SM.GetAll() // Parse settings (same as before but simplified validation) - if enabled, exists := sets[settings.KeyOIDCEnabled]; exists { - config.Enabled = settings.AsBool(enabled) + if enabled, ok := sets[settings.KeyOIDCEnabled]; ok { + if !settings.AsBool(enabled) { + return nil, nil, errors.New("oidc disabled") + } } - // Return early if disabled - if !config.Enabled { - return config, nil - } - if pkce, exists := sets[settings.KeyOIDCPKCE]; exists { - config.UsePKCE = settings.AsBool(pkce) - } - if clientID, exists := sets[settings.KeyOIDCClientID]; exists { + + config := &oauth2.Config{} + if clientID, ok := sets[settings.KeyOIDCClientID]; ok { config.ClientID = clientID } - if !config.UsePKCE { - if clientSecret, exists := sets[settings.KeyOIDCClientSecret]; exists { - config.ClientSecret = clientSecret + if clientSecret, ok := sets[settings.KeyOIDCClientSecret]; ok { + config.ClientSecret = clientSecret + } + if pkce, ok := sets[settings.KeyOIDCPKCE]; ok { + if settings.AsBool(pkce) { + config.ClientSecret = "" } } - if serverURL, exists := sets[settings.KeyServerURL]; exists { - if parsed, err := url.Parse(serverURL); err == nil { - config.RedirectURL = strings.TrimSuffix(parsed.String(), "/") + "/api/oidc/callback" - } + + config.RedirectURL = getRedirectURL(r) + issuerURL, ok := sets[settings.KeyOIDCIssuerURL] + if !ok { + return nil, nil, errors.New("oidc issuer url not set") } - if issuerURL, exists := sets[settings.KeyOIDCIssuerURL]; exists { - config.IssuerURL = issuerURL + provider, err := oidc.NewProvider(ctx, strings.TrimSuffix(issuerURL, "/")) + if err != nil { + return nil, nil, fmt.Errorf("failed to create OIDC provider: %w", err) } + config.Endpoint = provider.Endpoint() + + config.Scopes = []string{"openid", "email", "profile"} if scopes, exists := sets["oauth_scopes"]; exists && scopes != "" { config.Scopes = strings.Split(scopes, ",") for i := range config.Scopes { @@ -322,7 +258,32 @@ func getOIDCConfig(ctx context.Context, a *config.App) (*OIDCConfig, error) { } } - return config, nil + // Create ID token verifier + verifier := provider.Verifier(&oidc.Config{ + ClientID: config.ClientID, + }) + + return config, verifier, nil +} + +func getRedirectURL(r *http.Request) string { + proto := "http" + if r.TLS != nil { + proto = "https" + } else if forwarded := r.Header.Get("X-Forwarded-Proto"); forwarded != "" { + proto = forwarded + } + + // check url for redirect url + if redirectURL := r.URL.Query().Get("redirect"); redirectURL != "" { + return redirectURL + } + + host := r.Host + if forwardedHost := r.Header.Get("X-Forwarded-Host"); forwardedHost != "" { + host = forwardedHost + } + return fmt.Sprintf("%s://%s/oidc/callback", proto, host) } func findOrCreateOIDCUser( diff --git a/internal/api/handler/upload.go b/internal/api/handler/upload.go index e8b3fb6..d5e69b8 100644 --- a/internal/api/handler/upload.go +++ b/internal/api/handler/upload.go @@ -5,11 +5,67 @@ import ( "fmt" "net/http" "path/filepath" + "slices" "time" "github.com/mizuchilabs/mantrae/internal/config" + "github.com/mizuchilabs/mantrae/internal/storage" ) +func UploadAvatar(a *config.App) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + // Limit request size to prevent memory issues + r.Body = http.MaxBytesReader(w, r.Body, 10<<20) // 10MB limit + + if err := r.ParseMultipartForm(10 << 20); err != nil { + http.Error(w, "File too large or invalid form data", http.StatusBadRequest) + return + } + defer r.MultipartForm.RemoveAll() + + file, header, err := r.FormFile("file") + if err != nil { + http.Error(w, "Failed to get uploaded file", http.StatusBadRequest) + return + } + defer file.Close() + + extension := filepath.Ext(header.Filename) + allowedExtensions := []string{".png", ".jpg", ".jpeg"} + if !slices.Contains(allowedExtensions, extension) { + http.Error(w, "Invalid file type", http.StatusBadRequest) + return + } + + username := r.URL.Query().Get("username") + if username == "" { + http.Error(w, "Username not provided", http.StatusBadRequest) + return + } + // Generate unique filename + filename := fmt.Sprintf( + "avatar_%s_%s%s", + username, + time.Now().UTC().Format("20060102_150405"), + filepath.Ext(header.Filename), + ) + + storePath, err := storage.GetBackend(r.Context(), a.SM, "uploads") + if err != nil { + http.Error(w, "Failed to get storage backend", http.StatusInternalServerError) + return + } + storePath.Store(r.Context(), filename, file) + + response := map[string]string{"message": "Avatar updated successfully"} + w.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(w).Encode(response); err != nil { + http.Error(w, "Failed to encode response", http.StatusInternalServerError) + return + } + } +} + func UploadBackup(a *config.App) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // Limit request size to prevent memory issues diff --git a/internal/api/middlewares/auth.go b/internal/api/middlewares/auth.go index a382fd3..b7caeb8 100644 --- a/internal/api/middlewares/auth.go +++ b/internal/api/middlewares/auth.go @@ -9,7 +9,6 @@ import ( "connectrpc.com/connect" "github.com/mizuchilabs/mantrae/internal/config" - "github.com/mizuchilabs/mantrae/internal/util" "github.com/mizuchilabs/mantrae/pkg/meta" "github.com/mizuchilabs/mantrae/proto/gen/mantrae/v1/mantraev1connect" "golang.org/x/crypto/bcrypt" @@ -61,7 +60,7 @@ func (h *MiddlewareHandler) JWTAuth(next http.Handler) http.Handler { } token = strings.TrimPrefix(token, "Bearer ") - claims, err := util.DecodeUserJWT(token, h.app.Secret) + claims, err := meta.DecodeUserToken(token, h.app.Secret) if err != nil { http.Error(w, "Invalid token", http.StatusUnauthorized) return @@ -95,6 +94,34 @@ func Authentication(app *config.App) connect.UnaryInterceptorFunc { return next(ctx, req) } + if cookieHeader := req.Header().Get("Cookie"); cookieHeader != "" { + cookies, err := http.ParseCookie(cookieHeader) + if err != nil { + return nil, connect.NewError( + connect.CodeUnauthenticated, + errors.New("invalid cookie"), + ) + } + var sessionID string + for _, cookie := range cookies { + if cookie.Name == meta.CookieName { + sessionID = cookie.Value + } + } + // Validate sessionID as needed and inject into context + if sessionID != "" { + claims, err := meta.DecodeUserToken(sessionID, app.Secret) + if err != nil { + return nil, connect.NewError( + connect.CodeUnauthenticated, + errors.New("invalid token"), + ) + } + ctx = context.WithValue(ctx, AuthUserIDKey, claims.UserID) + return next(ctx, req) + } + } + authHeader := req.Header().Get("Authorization") if authHeader == "" { return nil, connect.NewError( @@ -132,7 +159,7 @@ func Authentication(app *config.App) connect.UnaryInterceptorFunc { } // Parse and validate the token - claims, err := util.DecodeUserJWT(tokenString, app.Secret) + claims, err := meta.DecodeUserToken(tokenString, app.Secret) if err != nil { return nil, connect.NewError( connect.CodeUnauthenticated, diff --git a/internal/api/middlewares/cors.go b/internal/api/middlewares/cors.go index 1c23cb6..f8d37f7 100644 --- a/internal/api/middlewares/cors.go +++ b/internal/api/middlewares/cors.go @@ -28,9 +28,15 @@ func WithCORS(h http.Handler, app *config.App, port string) http.Handler { return cors.New(cors.Options{ AllowedOrigins: allowedOrigins, AllowedMethods: connectcors.AllowedMethods(), - // AllowedHeaders: connectcors.AllowedHeaders(), - AllowedHeaders: []string{"*"}, ExposedHeaders: connectcors.ExposedHeaders(), - MaxAge: int(2 * time.Hour / time.Second), + AllowedHeaders: append( + connectcors.AllowedHeaders(), + "Authorization", + "Access-Control-Allow-Origin", + "Access-Control-Allow-Credentials", + "Access-Control-Allow-Headers", + ), + AllowCredentials: true, + MaxAge: int(2 * time.Hour / time.Second), }).Handler(h) } diff --git a/internal/api/server/server.go b/internal/api/server/server.go index 319edef..422040a 100644 --- a/internal/api/server/server.go +++ b/internal/api/server/server.go @@ -238,7 +238,11 @@ func (s *Server) registerServices() { } // Upload handler (HTTP) -------------------------------------------------- - s.mux.Handle("POST /api/backup", jwtChain(handler.UploadBackup(s.app))) + s.mux.Handle("POST /upload/avatar", jwtChain(handler.UploadAvatar(s.app))) + s.mux.Handle("POST /upload/backup", jwtChain(handler.UploadBackup(s.app))) + // s.mux.Handle("POST /upload/dynamic", jwtChain(handler.UploadBackup(s.app))) - // TODO: OIDC + // OIDC handlers (HTTP) --------------------------------------------------- + s.mux.Handle("GET /oidc/login", logChain(handler.OIDCLogin(s.app))) + s.mux.Handle("GET /oidc/callback", logChain(handler.OIDCCallback(s.app))) } diff --git a/internal/api/service/user.go b/internal/api/service/user.go index 279c33a..d86d622 100644 --- a/internal/api/service/user.go +++ b/internal/api/service/user.go @@ -3,6 +3,7 @@ package service import ( "context" "errors" + "net/http" "time" "connectrpc.com/connect" @@ -16,6 +17,7 @@ import ( "github.com/mizuchilabs/mantrae/internal/settings" "github.com/mizuchilabs/mantrae/internal/store/db" "github.com/mizuchilabs/mantrae/internal/util" + "github.com/mizuchilabs/mantrae/pkg/meta" mantraev1 "github.com/mizuchilabs/mantrae/proto/gen/mantrae/v1" ) @@ -50,10 +52,7 @@ func (s *UserService) LoginUser( } expirationTime := time.Now().Add(24 * time.Hour) - if req.Msg.Remember { - expirationTime = time.Now().Add(30 * 24 * time.Hour) - } - token, err := util.EncodeUserJWT(user.ID, s.app.Secret, expirationTime) + token, err := meta.EncodeUserToken(user.ID, s.app.Secret, expirationTime) if err != nil { return nil, connect.NewError(connect.CodeInternal, err) } @@ -62,7 +61,36 @@ func (s *UserService) LoginUser( return nil, connect.NewError(connect.CodeInternal, err) } - return connect.NewResponse(&mantraev1.LoginUserResponse{Token: token}), nil + cookie := http.Cookie{ + Name: meta.CookieName, + Value: token, + Path: "/", + HttpOnly: true, + MaxAge: int(expirationTime.Unix() - time.Now().Unix()), + Secure: req.Header().Get("X-Forwarded-Proto") == "https", + SameSite: http.SameSiteLaxMode, + } + res := connect.NewResponse(&mantraev1.LoginUserResponse{Token: token}) + res.Header().Set("Set-Cookie", cookie.String()) + return res, nil +} + +func (s *UserService) LogoutUser( + ctx context.Context, + req *connect.Request[mantraev1.LogoutUserRequest], +) (*connect.Response[mantraev1.LogoutUserResponse], error) { + cookie := http.Cookie{ + Name: meta.CookieName, + Value: "", + Path: "/", + HttpOnly: true, + MaxAge: -1, + Secure: req.Header().Get("X-Forwarded-Proto") == "https", + SameSite: http.SameSiteLaxMode, + } + res := connect.NewResponse(&mantraev1.LogoutUserResponse{}) + res.Header().Set("Set-Cookie", cookie.String()) + return res, nil } func (s *UserService) VerifyJWT( @@ -115,7 +143,7 @@ func (s *UserService) VerifyOTP( } expirationTime := time.Now().Add(1 * time.Hour) - token, err := util.EncodeUserJWT(user.ID, s.app.Secret, expirationTime) + token, err := meta.EncodeUserToken(user.ID, s.app.Secret, expirationTime) if err != nil { return nil, connect.NewError(connect.CodeInternal, err) } diff --git a/internal/backup/backup.go b/internal/backup/backup.go index 19b1696..76cab72 100644 --- a/internal/backup/backup.go +++ b/internal/backup/backup.go @@ -18,6 +18,8 @@ import ( "github.com/mizuchilabs/mantrae/internal/util" ) +const BackupPath = "backups" + type BackupManager struct { Conn *store.Connection SM *settings.SettingsManager @@ -58,42 +60,11 @@ func (m *BackupManager) Stop() { } func (m *BackupManager) SetStorage(ctx context.Context) error { - backupStorage, ok := m.SM.Get(settings.KeyBackupStorage) - if !ok { - return fmt.Errorf("failed to get backup storage setting") - } - storageType := storage.BackendType(backupStorage) - if !storageType.Valid() { - return fmt.Errorf("storage backend not configured") - } - var err error - var newStorage storage.Backend - switch storageType { - case storage.BackendTypeLocal: - path, ok := m.SM.Get(settings.KeyBackupPath) - if !ok { - return fmt.Errorf("failed to get backup path") - } - - newStorage, err = storage.NewLocalStorage(path) - if err != nil { - return fmt.Errorf("failed to create local storage: %w", err) - } - slog.Debug("backup storage set to local", "path", path) - - case storage.BackendTypeS3: - newStorage, err = storage.NewS3Storage(ctx, m.SM) - if err != nil { - return fmt.Errorf("failed to create s3 storage: %w", err) - } - slog.Debug("backup storage set to S3") - - default: - return fmt.Errorf("unsupported backend type: %s", storageType) + m.Storage, err = storage.GetBackend(ctx, m.SM, BackupPath) + if err != nil { + return fmt.Errorf("failed to get storage backend: %w", err) } - - m.Storage = newStorage return nil } diff --git a/internal/settings/constants.go b/internal/settings/constants.go index 6ec5283..bbf304a 100644 --- a/internal/settings/constants.go +++ b/internal/settings/constants.go @@ -4,13 +4,12 @@ package settings const ( // General settings KeyServerURL = "server_url" + KeyStorage = "storage_select" // Backup settings KeyBackupEnabled = "backup_enabled" KeyBackupInterval = "backup_interval" KeyBackupKeep = "backup_keep" - KeyBackupStorage = "backup_storage_select" - KeyBackupPath = "backup_path" // S3 settings KeyS3Endpoint = "s3_endpoint" diff --git a/internal/settings/settings.go b/internal/settings/settings.go index 892af88..d9446de 100644 --- a/internal/settings/settings.go +++ b/internal/settings/settings.go @@ -19,11 +19,10 @@ import ( // Settings defines all application settings type Settings struct { ServerURL string `setting:"server_url" default:""` + Storage string `setting:"storage_select" default:"local"` BackupEnabled bool `setting:"backup_enabled" default:"true"` BackupInterval time.Duration `setting:"backup_interval" default:"24h"` BackupKeep int `setting:"backup_keep" default:"3"` - BackupStorage string `setting:"backup_storage_select" default:"local"` - BackupPath string `setting:"backup_path" default:"backups"` S3Endpoint string `setting:"s3_endpoint" default:""` S3Bucket string `setting:"s3_bucket" default:"mantrae"` S3Region string `setting:"s3_region" default:"us-east-1"` diff --git a/internal/storage/types.go b/internal/storage/types.go index d9c406f..e2b4292 100644 --- a/internal/storage/types.go +++ b/internal/storage/types.go @@ -2,8 +2,11 @@ package storage import ( "context" + "errors" "io" "time" + + "github.com/mizuchilabs/mantrae/internal/settings" ) type BackendType string @@ -11,7 +14,6 @@ type BackendType string const ( BackendTypeLocal BackendType = "local" BackendTypeS3 BackendType = "s3" - // BackendTypeGit BackendType = "git" //TODO: For future implementation ) // Backend defines interface for different storage solutions @@ -28,11 +30,22 @@ type StoredFile struct { Timestamp time.Time `json:"timestamp,omitempty"` } -func (t BackendType) Valid() bool { - switch t { - case BackendTypeLocal, BackendTypeS3: - return true +func GetBackend( + ctx context.Context, + sm *settings.SettingsManager, + path string, +) (Backend, error) { + backendSetting, ok := sm.Get(settings.KeyStorage) + if !ok { + return nil, errors.New("failed to get storage backend") + } + + switch BackendType(backendSetting) { + case BackendTypeLocal: + return NewLocalStorage(path) + case BackendTypeS3: + return NewS3Storage(ctx, sm) default: - return false + return nil, errors.New("invalid storage backend") } } diff --git a/internal/util/jwt.go b/internal/util/jwt.go deleted file mode 100644 index 2d6611a..0000000 --- a/internal/util/jwt.go +++ /dev/null @@ -1,52 +0,0 @@ -package util - -import ( - "errors" - "time" - - "github.com/golang-jwt/jwt/v5" -) - -const CookieName = "auth_token" - -type UserClaims struct { - UserID string `json:"user_id,omitempty"` - jwt.RegisteredClaims -} - -// EncodeUserJWT generates a JWT for user login -func EncodeUserJWT(userID, secret string, expirationTime time.Time) (string, error) { - if userID == "" { - return "", errors.New("username cannot be empty") - } - if expirationTime.IsZero() { - expirationTime = time.Now().Add(24 * time.Hour) - } - claims := &UserClaims{ - UserID: userID, - RegisteredClaims: jwt.RegisteredClaims{ - ExpiresAt: jwt.NewNumericDate(expirationTime), - IssuedAt: jwt.NewNumericDate(time.Now()), - }, - } - - token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) - return token.SignedString([]byte(secret)) -} - -// DecodeUserJWT decodes the user token and returns claims if valid -func DecodeUserJWT(tokenString, secret string) (*UserClaims, error) { - claims := &UserClaims{} - token, err := jwt.ParseWithClaims( - tokenString, - claims, - func(token *jwt.Token) (any, error) { - return []byte(secret), nil - }, - ) - - if err != nil || !token.Valid { - return nil, err - } - return claims, nil -} diff --git a/pkg/meta/claims.go b/pkg/meta/claims.go index d14b544..cdd6db3 100644 --- a/pkg/meta/claims.go +++ b/pkg/meta/claims.go @@ -20,6 +20,60 @@ type AgentClaims struct { jwt.RegisteredClaims } +func (u *UserClaims) Valid() error { + if u.UserID == "" { + return errors.New("user id is required") + } + return nil +} + +func (u *UserClaims) IsExpired() bool { + return u.ExpiresAt.Before(time.Now()) +} + +func DecodeUserToken(tokenStr string, secret string) (*UserClaims, error) { + token, err := jwt.ParseWithClaims( + tokenStr, + &UserClaims{}, + func(token *jwt.Token) (any, error) { + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) + } + return []byte(secret), nil + }, + ) + if err != nil { + return nil, err + } + if !token.Valid { + return nil, errors.New("invalid token") + } + + claims, ok := token.Claims.(*UserClaims) + if !ok || !token.Valid { + return nil, fmt.Errorf("invalid token") + } + return claims, nil +} + +func EncodeUserToken( + userID, secret string, + expirationTime time.Time, +) (string, error) { + claims := &UserClaims{ + UserID: userID, + RegisteredClaims: jwt.RegisteredClaims{ + ExpiresAt: jwt.NewNumericDate(expirationTime), + IssuedAt: jwt.NewNumericDate(time.Now()), + }, + } + if err := claims.Valid(); err != nil { + return "", err + } + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + return token.SignedString([]byte(secret)) +} + func (a *AgentClaims) Valid() error { if a.AgentID == "" { return errors.New("agent id is required") @@ -92,57 +146,3 @@ func EncodeAgentToken( token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString([]byte(secret)) } - -func (u *UserClaims) Valid() error { - if u.UserID == "" { - return errors.New("user id is required") - } - return nil -} - -func (u *UserClaims) IsExpired() bool { - return u.ExpiresAt.Before(time.Now()) -} - -func DecodeUserToken(tokenStr string, secret string) (*UserClaims, error) { - token, err := jwt.ParseWithClaims( - tokenStr, - &UserClaims{}, - func(token *jwt.Token) (any, error) { - if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { - return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) - } - return []byte(secret), nil - }, - ) - if err != nil { - return nil, err - } - if !token.Valid { - return nil, errors.New("invalid token") - } - - claims, ok := token.Claims.(*UserClaims) - if !ok || !token.Valid { - return nil, fmt.Errorf("invalid token") - } - return claims, nil -} - -func EncodeUserToken( - userID, secret string, - expirationTime time.Time, -) (string, error) { - claims := &UserClaims{ - UserID: userID, - RegisteredClaims: jwt.RegisteredClaims{ - ExpiresAt: jwt.NewNumericDate(expirationTime), - IssuedAt: jwt.NewNumericDate(time.Now()), - }, - } - if err := claims.Valid(); err != nil { - return "", err - } - token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) - return token.SignedString([]byte(secret)) -} diff --git a/pkg/meta/constants.go b/pkg/meta/constants.go new file mode 100644 index 0000000..1431896 --- /dev/null +++ b/pkg/meta/constants.go @@ -0,0 +1,6 @@ +package meta + +const ( + CookieName = "auth_token" + HeaderAgentID = "Mantrae-Agent-Id" +) diff --git a/pkg/meta/headers.go b/pkg/meta/headers.go deleted file mode 100644 index 1ca2c1c..0000000 --- a/pkg/meta/headers.go +++ /dev/null @@ -1,3 +0,0 @@ -package meta - -const HeaderAgentID = "Mantrae-Agent-Id" diff --git a/proto/gen/mantrae/v1/mantraev1connect/user.connect.go b/proto/gen/mantrae/v1/mantraev1connect/user.connect.go index 5e48054..aa7d4ab 100644 --- a/proto/gen/mantrae/v1/mantraev1connect/user.connect.go +++ b/proto/gen/mantrae/v1/mantraev1connect/user.connect.go @@ -35,6 +35,8 @@ const ( const ( // UserServiceLoginUserProcedure is the fully-qualified name of the UserService's LoginUser RPC. UserServiceLoginUserProcedure = "/mantrae.v1.UserService/LoginUser" + // UserServiceLogoutUserProcedure is the fully-qualified name of the UserService's LogoutUser RPC. + UserServiceLogoutUserProcedure = "/mantrae.v1.UserService/LogoutUser" // UserServiceVerifyJWTProcedure is the fully-qualified name of the UserService's VerifyJWT RPC. UserServiceVerifyJWTProcedure = "/mantrae.v1.UserService/VerifyJWT" // UserServiceVerifyOTPProcedure is the fully-qualified name of the UserService's VerifyOTP RPC. @@ -59,6 +61,7 @@ const ( // UserServiceClient is a client for the mantrae.v1.UserService service. type UserServiceClient interface { LoginUser(context.Context, *connect.Request[v1.LoginUserRequest]) (*connect.Response[v1.LoginUserResponse], error) + LogoutUser(context.Context, *connect.Request[v1.LogoutUserRequest]) (*connect.Response[v1.LogoutUserResponse], error) VerifyJWT(context.Context, *connect.Request[v1.VerifyJWTRequest]) (*connect.Response[v1.VerifyJWTResponse], error) VerifyOTP(context.Context, *connect.Request[v1.VerifyOTPRequest]) (*connect.Response[v1.VerifyOTPResponse], error) SendOTP(context.Context, *connect.Request[v1.SendOTPRequest]) (*connect.Response[v1.SendOTPResponse], error) @@ -87,6 +90,12 @@ func NewUserServiceClient(httpClient connect.HTTPClient, baseURL string, opts .. connect.WithSchema(userServiceMethods.ByName("LoginUser")), connect.WithClientOptions(opts...), ), + logoutUser: connect.NewClient[v1.LogoutUserRequest, v1.LogoutUserResponse]( + httpClient, + baseURL+UserServiceLogoutUserProcedure, + connect.WithSchema(userServiceMethods.ByName("LogoutUser")), + connect.WithClientOptions(opts...), + ), verifyJWT: connect.NewClient[v1.VerifyJWTRequest, v1.VerifyJWTResponse]( httpClient, baseURL+UserServiceVerifyJWTProcedure, @@ -149,6 +158,7 @@ func NewUserServiceClient(httpClient connect.HTTPClient, baseURL string, opts .. // userServiceClient implements UserServiceClient. type userServiceClient struct { loginUser *connect.Client[v1.LoginUserRequest, v1.LoginUserResponse] + logoutUser *connect.Client[v1.LogoutUserRequest, v1.LogoutUserResponse] verifyJWT *connect.Client[v1.VerifyJWTRequest, v1.VerifyJWTResponse] verifyOTP *connect.Client[v1.VerifyOTPRequest, v1.VerifyOTPResponse] sendOTP *connect.Client[v1.SendOTPRequest, v1.SendOTPResponse] @@ -165,6 +175,11 @@ func (c *userServiceClient) LoginUser(ctx context.Context, req *connect.Request[ return c.loginUser.CallUnary(ctx, req) } +// LogoutUser calls mantrae.v1.UserService.LogoutUser. +func (c *userServiceClient) LogoutUser(ctx context.Context, req *connect.Request[v1.LogoutUserRequest]) (*connect.Response[v1.LogoutUserResponse], error) { + return c.logoutUser.CallUnary(ctx, req) +} + // VerifyJWT calls mantrae.v1.UserService.VerifyJWT. func (c *userServiceClient) VerifyJWT(ctx context.Context, req *connect.Request[v1.VerifyJWTRequest]) (*connect.Response[v1.VerifyJWTResponse], error) { return c.verifyJWT.CallUnary(ctx, req) @@ -213,6 +228,7 @@ func (c *userServiceClient) GetOIDCStatus(ctx context.Context, req *connect.Requ // UserServiceHandler is an implementation of the mantrae.v1.UserService service. type UserServiceHandler interface { LoginUser(context.Context, *connect.Request[v1.LoginUserRequest]) (*connect.Response[v1.LoginUserResponse], error) + LogoutUser(context.Context, *connect.Request[v1.LogoutUserRequest]) (*connect.Response[v1.LogoutUserResponse], error) VerifyJWT(context.Context, *connect.Request[v1.VerifyJWTRequest]) (*connect.Response[v1.VerifyJWTResponse], error) VerifyOTP(context.Context, *connect.Request[v1.VerifyOTPRequest]) (*connect.Response[v1.VerifyOTPResponse], error) SendOTP(context.Context, *connect.Request[v1.SendOTPRequest]) (*connect.Response[v1.SendOTPResponse], error) @@ -237,6 +253,12 @@ func NewUserServiceHandler(svc UserServiceHandler, opts ...connect.HandlerOption connect.WithSchema(userServiceMethods.ByName("LoginUser")), connect.WithHandlerOptions(opts...), ) + userServiceLogoutUserHandler := connect.NewUnaryHandler( + UserServiceLogoutUserProcedure, + svc.LogoutUser, + connect.WithSchema(userServiceMethods.ByName("LogoutUser")), + connect.WithHandlerOptions(opts...), + ) userServiceVerifyJWTHandler := connect.NewUnaryHandler( UserServiceVerifyJWTProcedure, svc.VerifyJWT, @@ -297,6 +319,8 @@ func NewUserServiceHandler(svc UserServiceHandler, opts ...connect.HandlerOption switch r.URL.Path { case UserServiceLoginUserProcedure: userServiceLoginUserHandler.ServeHTTP(w, r) + case UserServiceLogoutUserProcedure: + userServiceLogoutUserHandler.ServeHTTP(w, r) case UserServiceVerifyJWTProcedure: userServiceVerifyJWTHandler.ServeHTTP(w, r) case UserServiceVerifyOTPProcedure: @@ -328,6 +352,10 @@ func (UnimplementedUserServiceHandler) LoginUser(context.Context, *connect.Reque return nil, connect.NewError(connect.CodeUnimplemented, errors.New("mantrae.v1.UserService.LoginUser is not implemented")) } +func (UnimplementedUserServiceHandler) LogoutUser(context.Context, *connect.Request[v1.LogoutUserRequest]) (*connect.Response[v1.LogoutUserResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("mantrae.v1.UserService.LogoutUser is not implemented")) +} + func (UnimplementedUserServiceHandler) VerifyJWT(context.Context, *connect.Request[v1.VerifyJWTRequest]) (*connect.Response[v1.VerifyJWTResponse], error) { return nil, connect.NewError(connect.CodeUnimplemented, errors.New("mantrae.v1.UserService.VerifyJWT is not implemented")) } diff --git a/proto/gen/mantrae/v1/user.pb.go b/proto/gen/mantrae/v1/user.pb.go index 52cee32..8c33082 100644 --- a/proto/gen/mantrae/v1/user.pb.go +++ b/proto/gen/mantrae/v1/user.pb.go @@ -147,7 +147,6 @@ type LoginUserRequest struct { // *LoginUserRequest_Email Identifier isLoginUserRequest_Identifier `protobuf_oneof:"identifier"` Password string `protobuf:"bytes,3,opt,name=password,proto3" json:"password,omitempty"` - Remember bool `protobuf:"varint,4,opt,name=remember,proto3" json:"remember,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -214,13 +213,6 @@ func (x *LoginUserRequest) GetPassword() string { return "" } -func (x *LoginUserRequest) GetRemember() bool { - if x != nil { - return x.Remember - } - return false -} - type isLoginUserRequest_Identifier interface { isLoginUserRequest_Identifier() } @@ -281,16 +273,87 @@ func (x *LoginUserResponse) GetToken() string { return "" } +type LogoutUserRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *LogoutUserRequest) Reset() { + *x = LogoutUserRequest{} + mi := &file_mantrae_v1_user_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *LogoutUserRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LogoutUserRequest) ProtoMessage() {} + +func (x *LogoutUserRequest) ProtoReflect() protoreflect.Message { + mi := &file_mantrae_v1_user_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LogoutUserRequest.ProtoReflect.Descriptor instead. +func (*LogoutUserRequest) Descriptor() ([]byte, []int) { + return file_mantrae_v1_user_proto_rawDescGZIP(), []int{3} +} + +type LogoutUserResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *LogoutUserResponse) Reset() { + *x = LogoutUserResponse{} + mi := &file_mantrae_v1_user_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *LogoutUserResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LogoutUserResponse) ProtoMessage() {} + +func (x *LogoutUserResponse) ProtoReflect() protoreflect.Message { + mi := &file_mantrae_v1_user_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LogoutUserResponse.ProtoReflect.Descriptor instead. +func (*LogoutUserResponse) Descriptor() ([]byte, []int) { + return file_mantrae_v1_user_proto_rawDescGZIP(), []int{4} +} + type VerifyJWTRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *VerifyJWTRequest) Reset() { *x = VerifyJWTRequest{} - mi := &file_mantrae_v1_user_proto_msgTypes[3] + mi := &file_mantrae_v1_user_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -302,7 +365,7 @@ func (x *VerifyJWTRequest) String() string { func (*VerifyJWTRequest) ProtoMessage() {} func (x *VerifyJWTRequest) ProtoReflect() protoreflect.Message { - mi := &file_mantrae_v1_user_proto_msgTypes[3] + mi := &file_mantrae_v1_user_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -315,14 +378,7 @@ func (x *VerifyJWTRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use VerifyJWTRequest.ProtoReflect.Descriptor instead. func (*VerifyJWTRequest) Descriptor() ([]byte, []int) { - return file_mantrae_v1_user_proto_rawDescGZIP(), []int{3} -} - -func (x *VerifyJWTRequest) GetToken() string { - if x != nil { - return x.Token - } - return "" + return file_mantrae_v1_user_proto_rawDescGZIP(), []int{5} } type VerifyJWTResponse struct { @@ -334,7 +390,7 @@ type VerifyJWTResponse struct { func (x *VerifyJWTResponse) Reset() { *x = VerifyJWTResponse{} - mi := &file_mantrae_v1_user_proto_msgTypes[4] + mi := &file_mantrae_v1_user_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -346,7 +402,7 @@ func (x *VerifyJWTResponse) String() string { func (*VerifyJWTResponse) ProtoMessage() {} func (x *VerifyJWTResponse) ProtoReflect() protoreflect.Message { - mi := &file_mantrae_v1_user_proto_msgTypes[4] + mi := &file_mantrae_v1_user_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -359,7 +415,7 @@ func (x *VerifyJWTResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use VerifyJWTResponse.ProtoReflect.Descriptor instead. func (*VerifyJWTResponse) Descriptor() ([]byte, []int) { - return file_mantrae_v1_user_proto_rawDescGZIP(), []int{4} + return file_mantrae_v1_user_proto_rawDescGZIP(), []int{6} } func (x *VerifyJWTResponse) GetUser() *User { @@ -383,7 +439,7 @@ type VerifyOTPRequest struct { func (x *VerifyOTPRequest) Reset() { *x = VerifyOTPRequest{} - mi := &file_mantrae_v1_user_proto_msgTypes[5] + mi := &file_mantrae_v1_user_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -395,7 +451,7 @@ func (x *VerifyOTPRequest) String() string { func (*VerifyOTPRequest) ProtoMessage() {} func (x *VerifyOTPRequest) ProtoReflect() protoreflect.Message { - mi := &file_mantrae_v1_user_proto_msgTypes[5] + mi := &file_mantrae_v1_user_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -408,7 +464,7 @@ func (x *VerifyOTPRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use VerifyOTPRequest.ProtoReflect.Descriptor instead. func (*VerifyOTPRequest) Descriptor() ([]byte, []int) { - return file_mantrae_v1_user_proto_rawDescGZIP(), []int{5} + return file_mantrae_v1_user_proto_rawDescGZIP(), []int{7} } func (x *VerifyOTPRequest) GetIdentifier() isVerifyOTPRequest_Identifier { @@ -468,7 +524,7 @@ type VerifyOTPResponse struct { func (x *VerifyOTPResponse) Reset() { *x = VerifyOTPResponse{} - mi := &file_mantrae_v1_user_proto_msgTypes[6] + mi := &file_mantrae_v1_user_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -480,7 +536,7 @@ func (x *VerifyOTPResponse) String() string { func (*VerifyOTPResponse) ProtoMessage() {} func (x *VerifyOTPResponse) ProtoReflect() protoreflect.Message { - mi := &file_mantrae_v1_user_proto_msgTypes[6] + mi := &file_mantrae_v1_user_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -493,7 +549,7 @@ func (x *VerifyOTPResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use VerifyOTPResponse.ProtoReflect.Descriptor instead. func (*VerifyOTPResponse) Descriptor() ([]byte, []int) { - return file_mantrae_v1_user_proto_rawDescGZIP(), []int{6} + return file_mantrae_v1_user_proto_rawDescGZIP(), []int{8} } func (x *VerifyOTPResponse) GetToken() string { @@ -516,7 +572,7 @@ type SendOTPRequest struct { func (x *SendOTPRequest) Reset() { *x = SendOTPRequest{} - mi := &file_mantrae_v1_user_proto_msgTypes[7] + mi := &file_mantrae_v1_user_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -528,7 +584,7 @@ func (x *SendOTPRequest) String() string { func (*SendOTPRequest) ProtoMessage() {} func (x *SendOTPRequest) ProtoReflect() protoreflect.Message { - mi := &file_mantrae_v1_user_proto_msgTypes[7] + mi := &file_mantrae_v1_user_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -541,7 +597,7 @@ func (x *SendOTPRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SendOTPRequest.ProtoReflect.Descriptor instead. func (*SendOTPRequest) Descriptor() ([]byte, []int) { - return file_mantrae_v1_user_proto_rawDescGZIP(), []int{7} + return file_mantrae_v1_user_proto_rawDescGZIP(), []int{9} } func (x *SendOTPRequest) GetIdentifier() isSendOTPRequest_Identifier { @@ -593,7 +649,7 @@ type SendOTPResponse struct { func (x *SendOTPResponse) Reset() { *x = SendOTPResponse{} - mi := &file_mantrae_v1_user_proto_msgTypes[8] + mi := &file_mantrae_v1_user_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -605,7 +661,7 @@ func (x *SendOTPResponse) String() string { func (*SendOTPResponse) ProtoMessage() {} func (x *SendOTPResponse) ProtoReflect() protoreflect.Message { - mi := &file_mantrae_v1_user_proto_msgTypes[8] + mi := &file_mantrae_v1_user_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -618,7 +674,7 @@ func (x *SendOTPResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SendOTPResponse.ProtoReflect.Descriptor instead. func (*SendOTPResponse) Descriptor() ([]byte, []int) { - return file_mantrae_v1_user_proto_rawDescGZIP(), []int{8} + return file_mantrae_v1_user_proto_rawDescGZIP(), []int{10} } type GetUserRequest struct { @@ -635,7 +691,7 @@ type GetUserRequest struct { func (x *GetUserRequest) Reset() { *x = GetUserRequest{} - mi := &file_mantrae_v1_user_proto_msgTypes[9] + mi := &file_mantrae_v1_user_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -647,7 +703,7 @@ func (x *GetUserRequest) String() string { func (*GetUserRequest) ProtoMessage() {} func (x *GetUserRequest) ProtoReflect() protoreflect.Message { - mi := &file_mantrae_v1_user_proto_msgTypes[9] + mi := &file_mantrae_v1_user_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -660,7 +716,7 @@ func (x *GetUserRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetUserRequest.ProtoReflect.Descriptor instead. func (*GetUserRequest) Descriptor() ([]byte, []int) { - return file_mantrae_v1_user_proto_rawDescGZIP(), []int{9} + return file_mantrae_v1_user_proto_rawDescGZIP(), []int{11} } func (x *GetUserRequest) GetIdentifier() isGetUserRequest_Identifier { @@ -728,7 +784,7 @@ type GetUserResponse struct { func (x *GetUserResponse) Reset() { *x = GetUserResponse{} - mi := &file_mantrae_v1_user_proto_msgTypes[10] + mi := &file_mantrae_v1_user_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -740,7 +796,7 @@ func (x *GetUserResponse) String() string { func (*GetUserResponse) ProtoMessage() {} func (x *GetUserResponse) ProtoReflect() protoreflect.Message { - mi := &file_mantrae_v1_user_proto_msgTypes[10] + mi := &file_mantrae_v1_user_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -753,7 +809,7 @@ func (x *GetUserResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetUserResponse.ProtoReflect.Descriptor instead. func (*GetUserResponse) Descriptor() ([]byte, []int) { - return file_mantrae_v1_user_proto_rawDescGZIP(), []int{10} + return file_mantrae_v1_user_proto_rawDescGZIP(), []int{12} } func (x *GetUserResponse) GetUser() *User { @@ -775,7 +831,7 @@ type CreateUserRequest struct { func (x *CreateUserRequest) Reset() { *x = CreateUserRequest{} - mi := &file_mantrae_v1_user_proto_msgTypes[11] + mi := &file_mantrae_v1_user_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -787,7 +843,7 @@ func (x *CreateUserRequest) String() string { func (*CreateUserRequest) ProtoMessage() {} func (x *CreateUserRequest) ProtoReflect() protoreflect.Message { - mi := &file_mantrae_v1_user_proto_msgTypes[11] + mi := &file_mantrae_v1_user_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -800,7 +856,7 @@ func (x *CreateUserRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateUserRequest.ProtoReflect.Descriptor instead. func (*CreateUserRequest) Descriptor() ([]byte, []int) { - return file_mantrae_v1_user_proto_rawDescGZIP(), []int{11} + return file_mantrae_v1_user_proto_rawDescGZIP(), []int{13} } func (x *CreateUserRequest) GetUsername() string { @@ -840,7 +896,7 @@ type CreateUserResponse struct { func (x *CreateUserResponse) Reset() { *x = CreateUserResponse{} - mi := &file_mantrae_v1_user_proto_msgTypes[12] + mi := &file_mantrae_v1_user_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -852,7 +908,7 @@ func (x *CreateUserResponse) String() string { func (*CreateUserResponse) ProtoMessage() {} func (x *CreateUserResponse) ProtoReflect() protoreflect.Message { - mi := &file_mantrae_v1_user_proto_msgTypes[12] + mi := &file_mantrae_v1_user_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -865,7 +921,7 @@ func (x *CreateUserResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateUserResponse.ProtoReflect.Descriptor instead. func (*CreateUserResponse) Descriptor() ([]byte, []int) { - return file_mantrae_v1_user_proto_rawDescGZIP(), []int{12} + return file_mantrae_v1_user_proto_rawDescGZIP(), []int{14} } func (x *CreateUserResponse) GetUser() *User { @@ -888,7 +944,7 @@ type UpdateUserRequest struct { func (x *UpdateUserRequest) Reset() { *x = UpdateUserRequest{} - mi := &file_mantrae_v1_user_proto_msgTypes[13] + mi := &file_mantrae_v1_user_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -900,7 +956,7 @@ func (x *UpdateUserRequest) String() string { func (*UpdateUserRequest) ProtoMessage() {} func (x *UpdateUserRequest) ProtoReflect() protoreflect.Message { - mi := &file_mantrae_v1_user_proto_msgTypes[13] + mi := &file_mantrae_v1_user_proto_msgTypes[15] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -913,7 +969,7 @@ func (x *UpdateUserRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateUserRequest.ProtoReflect.Descriptor instead. func (*UpdateUserRequest) Descriptor() ([]byte, []int) { - return file_mantrae_v1_user_proto_rawDescGZIP(), []int{13} + return file_mantrae_v1_user_proto_rawDescGZIP(), []int{15} } func (x *UpdateUserRequest) GetId() string { @@ -960,7 +1016,7 @@ type UpdateUserResponse struct { func (x *UpdateUserResponse) Reset() { *x = UpdateUserResponse{} - mi := &file_mantrae_v1_user_proto_msgTypes[14] + mi := &file_mantrae_v1_user_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -972,7 +1028,7 @@ func (x *UpdateUserResponse) String() string { func (*UpdateUserResponse) ProtoMessage() {} func (x *UpdateUserResponse) ProtoReflect() protoreflect.Message { - mi := &file_mantrae_v1_user_proto_msgTypes[14] + mi := &file_mantrae_v1_user_proto_msgTypes[16] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -985,7 +1041,7 @@ func (x *UpdateUserResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateUserResponse.ProtoReflect.Descriptor instead. func (*UpdateUserResponse) Descriptor() ([]byte, []int) { - return file_mantrae_v1_user_proto_rawDescGZIP(), []int{14} + return file_mantrae_v1_user_proto_rawDescGZIP(), []int{16} } func (x *UpdateUserResponse) GetUser() *User { @@ -1004,7 +1060,7 @@ type DeleteUserRequest struct { func (x *DeleteUserRequest) Reset() { *x = DeleteUserRequest{} - mi := &file_mantrae_v1_user_proto_msgTypes[15] + mi := &file_mantrae_v1_user_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1016,7 +1072,7 @@ func (x *DeleteUserRequest) String() string { func (*DeleteUserRequest) ProtoMessage() {} func (x *DeleteUserRequest) ProtoReflect() protoreflect.Message { - mi := &file_mantrae_v1_user_proto_msgTypes[15] + mi := &file_mantrae_v1_user_proto_msgTypes[17] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1029,7 +1085,7 @@ func (x *DeleteUserRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteUserRequest.ProtoReflect.Descriptor instead. func (*DeleteUserRequest) Descriptor() ([]byte, []int) { - return file_mantrae_v1_user_proto_rawDescGZIP(), []int{15} + return file_mantrae_v1_user_proto_rawDescGZIP(), []int{17} } func (x *DeleteUserRequest) GetId() string { @@ -1047,7 +1103,7 @@ type DeleteUserResponse struct { func (x *DeleteUserResponse) Reset() { *x = DeleteUserResponse{} - mi := &file_mantrae_v1_user_proto_msgTypes[16] + mi := &file_mantrae_v1_user_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1059,7 +1115,7 @@ func (x *DeleteUserResponse) String() string { func (*DeleteUserResponse) ProtoMessage() {} func (x *DeleteUserResponse) ProtoReflect() protoreflect.Message { - mi := &file_mantrae_v1_user_proto_msgTypes[16] + mi := &file_mantrae_v1_user_proto_msgTypes[18] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1072,7 +1128,7 @@ func (x *DeleteUserResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteUserResponse.ProtoReflect.Descriptor instead. func (*DeleteUserResponse) Descriptor() ([]byte, []int) { - return file_mantrae_v1_user_proto_rawDescGZIP(), []int{16} + return file_mantrae_v1_user_proto_rawDescGZIP(), []int{18} } type ListUsersRequest struct { @@ -1085,7 +1141,7 @@ type ListUsersRequest struct { func (x *ListUsersRequest) Reset() { *x = ListUsersRequest{} - mi := &file_mantrae_v1_user_proto_msgTypes[17] + mi := &file_mantrae_v1_user_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1097,7 +1153,7 @@ func (x *ListUsersRequest) String() string { func (*ListUsersRequest) ProtoMessage() {} func (x *ListUsersRequest) ProtoReflect() protoreflect.Message { - mi := &file_mantrae_v1_user_proto_msgTypes[17] + mi := &file_mantrae_v1_user_proto_msgTypes[19] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1110,7 +1166,7 @@ func (x *ListUsersRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListUsersRequest.ProtoReflect.Descriptor instead. func (*ListUsersRequest) Descriptor() ([]byte, []int) { - return file_mantrae_v1_user_proto_rawDescGZIP(), []int{17} + return file_mantrae_v1_user_proto_rawDescGZIP(), []int{19} } func (x *ListUsersRequest) GetLimit() int64 { @@ -1137,7 +1193,7 @@ type ListUsersResponse struct { func (x *ListUsersResponse) Reset() { *x = ListUsersResponse{} - mi := &file_mantrae_v1_user_proto_msgTypes[18] + mi := &file_mantrae_v1_user_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1149,7 +1205,7 @@ func (x *ListUsersResponse) String() string { func (*ListUsersResponse) ProtoMessage() {} func (x *ListUsersResponse) ProtoReflect() protoreflect.Message { - mi := &file_mantrae_v1_user_proto_msgTypes[18] + mi := &file_mantrae_v1_user_proto_msgTypes[20] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1162,7 +1218,7 @@ func (x *ListUsersResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListUsersResponse.ProtoReflect.Descriptor instead. func (*ListUsersResponse) Descriptor() ([]byte, []int) { - return file_mantrae_v1_user_proto_rawDescGZIP(), []int{18} + return file_mantrae_v1_user_proto_rawDescGZIP(), []int{20} } func (x *ListUsersResponse) GetUsers() []*User { @@ -1187,7 +1243,7 @@ type GetOIDCStatusRequest struct { func (x *GetOIDCStatusRequest) Reset() { *x = GetOIDCStatusRequest{} - mi := &file_mantrae_v1_user_proto_msgTypes[19] + mi := &file_mantrae_v1_user_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1199,7 +1255,7 @@ func (x *GetOIDCStatusRequest) String() string { func (*GetOIDCStatusRequest) ProtoMessage() {} func (x *GetOIDCStatusRequest) ProtoReflect() protoreflect.Message { - mi := &file_mantrae_v1_user_proto_msgTypes[19] + mi := &file_mantrae_v1_user_proto_msgTypes[21] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1212,7 +1268,7 @@ func (x *GetOIDCStatusRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetOIDCStatusRequest.ProtoReflect.Descriptor instead. func (*GetOIDCStatusRequest) Descriptor() ([]byte, []int) { - return file_mantrae_v1_user_proto_rawDescGZIP(), []int{19} + return file_mantrae_v1_user_proto_rawDescGZIP(), []int{21} } type GetOIDCStatusResponse struct { @@ -1226,7 +1282,7 @@ type GetOIDCStatusResponse struct { func (x *GetOIDCStatusResponse) Reset() { *x = GetOIDCStatusResponse{} - mi := &file_mantrae_v1_user_proto_msgTypes[20] + mi := &file_mantrae_v1_user_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1238,7 +1294,7 @@ func (x *GetOIDCStatusResponse) String() string { func (*GetOIDCStatusResponse) ProtoMessage() {} func (x *GetOIDCStatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_mantrae_v1_user_proto_msgTypes[20] + mi := &file_mantrae_v1_user_proto_msgTypes[22] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1251,7 +1307,7 @@ func (x *GetOIDCStatusResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetOIDCStatusResponse.ProtoReflect.Descriptor instead. func (*GetOIDCStatusResponse) Descriptor() ([]byte, []int) { - return file_mantrae_v1_user_proto_rawDescGZIP(), []int{20} + return file_mantrae_v1_user_proto_rawDescGZIP(), []int{22} } func (x *GetOIDCStatusResponse) GetOidcEnabled() bool { @@ -1308,7 +1364,7 @@ var file_mantrae_v1_user_proto_rawDesc = string([]byte{ 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, - 0x74, 0x22, 0xb3, 0x01, 0x0a, 0x10, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x55, 0x73, 0x65, 0x72, 0x52, + 0x74, 0x22, 0x97, 0x01, 0x0a, 0x10, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x03, 0x48, 0x00, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, @@ -1316,173 +1372,177 @@ var file_mantrae_v1_user_proto_rawDesc = string([]byte{ 0x04, 0x72, 0x02, 0x60, 0x01, 0x48, 0x00, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x26, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01, 0x01, 0x72, 0x02, 0x10, 0x08, 0x52, 0x08, 0x70, 0x61, - 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x6d, 0x65, 0x6d, 0x62, - 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x6d, 0x65, 0x6d, 0x62, - 0x65, 0x72, 0x42, 0x13, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, - 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, 0x22, 0x29, 0x0a, 0x11, 0x4c, 0x6f, 0x67, 0x69, 0x6e, - 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, - 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, - 0x65, 0x6e, 0x22, 0x34, 0x0a, 0x10, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4a, 0x57, 0x54, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01, 0x01, 0x72, 0x02, 0x10, - 0x01, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x39, 0x0a, 0x11, 0x56, 0x65, 0x72, 0x69, - 0x66, 0x79, 0x4a, 0x57, 0x54, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, - 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x61, - 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, - 0x73, 0x65, 0x72, 0x22, 0x8d, 0x01, 0x0a, 0x10, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4f, 0x54, - 0x50, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, - 0x02, 0x10, 0x03, 0x48, 0x00, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, - 0x1f, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, - 0xba, 0x48, 0x04, 0x72, 0x02, 0x60, 0x01, 0x48, 0x00, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, - 0x12, 0x1c, 0x0a, 0x03, 0x6f, 0x74, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, - 0x48, 0x07, 0xc8, 0x01, 0x01, 0x72, 0x02, 0x10, 0x06, 0x52, 0x03, 0x6f, 0x74, 0x70, 0x42, 0x13, - 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x05, 0xba, 0x48, - 0x02, 0x08, 0x01, 0x22, 0x29, 0x0a, 0x11, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4f, 0x54, 0x50, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, - 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x6d, - 0x0a, 0x0e, 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x54, 0x50, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x25, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x03, 0x48, 0x00, 0x52, 0x08, 0x75, - 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x60, 0x01, 0x48, - 0x00, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x42, 0x13, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, 0x22, 0x11, 0x0a, - 0x0f, 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x54, 0x50, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x88, 0x01, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, - 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x48, 0x00, 0x52, 0x02, 0x69, 0x64, 0x12, 0x25, - 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x03, 0x48, 0x00, 0x52, 0x08, 0x75, 0x73, 0x65, - 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x60, 0x01, 0x48, 0x00, 0x52, - 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x42, 0x13, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x66, 0x69, 0x65, 0x72, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, 0x22, 0x37, 0x0a, 0x0f, 0x47, - 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, - 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, - 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, - 0x75, 0x73, 0x65, 0x72, 0x22, 0x9d, 0x01, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, - 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x08, 0x75, 0x73, - 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, - 0x07, 0xc8, 0x01, 0x01, 0x72, 0x02, 0x10, 0x03, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01, 0x01, 0x72, 0x02, 0x10, 0x08, - 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1d, 0x0a, 0x05, 0x65, 0x6d, - 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, - 0x60, 0x01, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, - 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x41, - 0x64, 0x6d, 0x69, 0x6e, 0x22, 0x3a, 0x0a, 0x12, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, - 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x04, 0x75, 0x73, + 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x42, 0x13, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x66, 0x69, 0x65, 0x72, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, 0x22, 0x29, 0x0a, 0x11, 0x4c, + 0x6f, 0x67, 0x69, 0x6e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x13, 0x0a, 0x11, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, + 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x14, 0x0a, 0x12, 0x4c, + 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x12, 0x0a, 0x10, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4a, 0x57, 0x54, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x39, 0x0a, 0x11, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4a, + 0x57, 0x54, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, - 0x22, 0xc8, 0x01, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01, 0x01, 0x72, 0x02, 0x10, 0x01, 0x52, 0x02, - 0x69, 0x64, 0x12, 0x26, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01, 0x01, 0x72, 0x02, 0x10, 0x03, - 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x05, 0x65, 0x6d, - 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, - 0x60, 0x01, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, - 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x41, - 0x64, 0x6d, 0x69, 0x6e, 0x12, 0x28, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x08, 0x48, - 0x00, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x88, 0x01, 0x01, 0x42, 0x0b, - 0x0a, 0x09, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x3a, 0x0a, 0x12, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x24, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x10, 0x2e, 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, - 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x2f, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x02, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01, 0x01, - 0x72, 0x02, 0x10, 0x01, 0x52, 0x02, 0x69, 0x64, 0x22, 0x14, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xc0, - 0x01, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x71, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x03, 0x42, 0x56, 0xba, 0x48, 0x53, 0xba, 0x01, 0x50, 0x0a, 0x0b, 0x6c, 0x69, 0x6d, 0x69, - 0x74, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x12, 0x29, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x20, 0x6d, - 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x65, 0x69, 0x74, 0x68, 0x65, 0x72, 0x20, 0x2d, 0x31, - 0x20, 0x6f, 0x72, 0x20, 0x67, 0x72, 0x65, 0x61, 0x74, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x6e, - 0x20, 0x30, 0x1a, 0x16, 0x74, 0x68, 0x69, 0x73, 0x20, 0x3d, 0x3d, 0x20, 0x2d, 0x31, 0x20, 0x7c, - 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x3e, 0x20, 0x30, 0x48, 0x00, 0x52, 0x05, 0x6c, 0x69, - 0x6d, 0x69, 0x74, 0x88, 0x01, 0x01, 0x12, 0x24, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x42, 0x07, 0xba, 0x48, 0x04, 0x22, 0x02, 0x28, 0x00, 0x48, - 0x01, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, - 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, - 0x74, 0x22, 0x5c, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 0x12, 0x1f, - 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, - 0x16, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x4f, 0x49, 0x44, 0x43, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x7b, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x4f, 0x49, - 0x44, 0x43, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x69, 0x64, 0x63, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x6f, 0x69, 0x64, 0x63, 0x45, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x65, 0x6e, 0x61, - 0x62, 0x6c, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x6c, 0x6f, 0x67, 0x69, - 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, - 0x69, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, - 0x69, 0x64, 0x65, 0x72, 0x32, 0x84, 0x06, 0x0a, 0x0b, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x12, 0x48, 0x0a, 0x09, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x55, 0x73, 0x65, - 0x72, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, - 0x6f, 0x67, 0x69, 0x6e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, - 0x69, 0x6e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, - 0x0a, 0x09, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4a, 0x57, 0x54, 0x12, 0x1c, 0x2e, 0x6d, 0x61, - 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4a, - 0x57, 0x54, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x74, - 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4a, 0x57, 0x54, + 0x22, 0x8d, 0x01, 0x0a, 0x10, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4f, 0x54, 0x50, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x03, + 0x48, 0x00, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x05, + 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, + 0x72, 0x02, 0x60, 0x01, 0x48, 0x00, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x1c, 0x0a, + 0x03, 0x6f, 0x74, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0xc8, + 0x01, 0x01, 0x72, 0x02, 0x10, 0x06, 0x52, 0x03, 0x6f, 0x74, 0x70, 0x42, 0x13, 0x0a, 0x0a, 0x69, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, + 0x22, 0x29, 0x0a, 0x11, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4f, 0x54, 0x50, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x6d, 0x0a, 0x0e, 0x53, + 0x65, 0x6e, 0x64, 0x4f, 0x54, 0x50, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, + 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x03, 0x48, 0x00, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x60, 0x01, 0x48, 0x00, 0x52, 0x05, + 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x42, 0x13, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, + 0x69, 0x65, 0x72, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, 0x22, 0x11, 0x0a, 0x0f, 0x53, 0x65, + 0x6e, 0x64, 0x4f, 0x54, 0x50, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x88, 0x01, + 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x19, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, + 0x04, 0x72, 0x02, 0x10, 0x01, 0x48, 0x00, 0x52, 0x02, 0x69, 0x64, 0x12, 0x25, 0x0a, 0x08, 0x75, + 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, + 0x48, 0x04, 0x72, 0x02, 0x10, 0x03, 0x48, 0x00, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x60, 0x01, 0x48, 0x00, 0x52, 0x05, 0x65, 0x6d, + 0x61, 0x69, 0x6c, 0x42, 0x13, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, + 0x72, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, 0x22, 0x37, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x55, + 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x04, 0x75, + 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x61, 0x6e, 0x74, + 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, + 0x72, 0x22, 0x9d, 0x01, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01, + 0x01, 0x72, 0x02, 0x10, 0x03, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x26, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01, 0x01, 0x72, 0x02, 0x10, 0x08, 0x52, 0x08, 0x70, + 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1d, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x60, 0x01, 0x52, + 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x61, 0x64, 0x6d, + 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x41, 0x64, 0x6d, 0x69, + 0x6e, 0x22, 0x3a, 0x0a, 0x12, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0xc8, 0x01, + 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01, 0x01, 0x72, 0x02, 0x10, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, + 0x26, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01, 0x01, 0x72, 0x02, 0x10, 0x03, 0x52, 0x08, 0x75, + 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x60, 0x01, 0x52, + 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x61, 0x64, 0x6d, + 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x41, 0x64, 0x6d, 0x69, + 0x6e, 0x12, 0x28, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x08, 0x48, 0x00, 0x52, 0x08, + 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x88, 0x01, 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x5f, + 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x3a, 0x0a, 0x12, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, + 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, + 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, + 0x75, 0x73, 0x65, 0x72, 0x22, 0x2f, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, + 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01, 0x01, 0x72, 0x02, 0x10, + 0x01, 0x52, 0x02, 0x69, 0x64, 0x22, 0x14, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, + 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xc0, 0x01, 0x0a, 0x10, + 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x71, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x42, + 0x56, 0xba, 0x48, 0x53, 0xba, 0x01, 0x50, 0x0a, 0x0b, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x2e, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x12, 0x29, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x20, 0x6d, 0x75, 0x73, 0x74, + 0x20, 0x62, 0x65, 0x20, 0x65, 0x69, 0x74, 0x68, 0x65, 0x72, 0x20, 0x2d, 0x31, 0x20, 0x6f, 0x72, + 0x20, 0x67, 0x72, 0x65, 0x61, 0x74, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20, 0x30, 0x1a, + 0x16, 0x74, 0x68, 0x69, 0x73, 0x20, 0x3d, 0x3d, 0x20, 0x2d, 0x31, 0x20, 0x7c, 0x7c, 0x20, 0x74, + 0x68, 0x69, 0x73, 0x20, 0x3e, 0x20, 0x30, 0x48, 0x00, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, + 0x88, 0x01, 0x01, 0x12, 0x24, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x03, 0x42, 0x07, 0xba, 0x48, 0x04, 0x22, 0x02, 0x28, 0x00, 0x48, 0x01, 0x52, 0x06, + 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x6c, 0x69, + 0x6d, 0x69, 0x74, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x5c, + 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x55, 0x73, 0x65, 0x72, 0x52, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x74, + 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x16, 0x0a, 0x14, + 0x47, 0x65, 0x74, 0x4f, 0x49, 0x44, 0x43, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x22, 0x7b, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x4f, 0x49, 0x44, 0x43, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, + 0x0c, 0x6f, 0x69, 0x64, 0x63, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0b, 0x6f, 0x69, 0x64, 0x63, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, + 0x12, 0x23, 0x0a, 0x0d, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x45, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x32, 0xd1, 0x06, 0x0a, 0x0b, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x12, 0x48, 0x0a, 0x09, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1c, + 0x2e, 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x69, + 0x6e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6d, + 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x55, + 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0a, 0x4c, + 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x74, + 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x55, 0x73, 0x65, + 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6d, 0x61, 0x6e, 0x74, 0x72, + 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x09, 0x56, 0x65, 0x72, 0x69, - 0x66, 0x79, 0x4f, 0x54, 0x50, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4f, 0x54, 0x50, 0x52, 0x65, 0x71, 0x75, + 0x66, 0x79, 0x4a, 0x57, 0x54, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4a, 0x57, 0x54, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4f, 0x54, 0x50, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x42, 0x0a, 0x07, 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x54, 0x50, 0x12, 0x1a, 0x2e, - 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4f, - 0x54, 0x50, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6d, 0x61, 0x6e, 0x74, - 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x54, 0x50, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, - 0x72, 0x12, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, - 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, - 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, - 0x4b, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1d, 0x2e, - 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6d, - 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0a, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, - 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, - 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6d, 0x61, 0x6e, 0x74, - 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, - 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0a, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, + 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4a, 0x57, 0x54, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x48, 0x0a, 0x09, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4f, 0x54, 0x50, 0x12, + 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x65, 0x72, + 0x69, 0x66, 0x79, 0x4f, 0x54, 0x50, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, + 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, + 0x79, 0x4f, 0x54, 0x50, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x07, + 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x54, 0x50, 0x12, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x54, 0x50, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x54, 0x50, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x47, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x6d, 0x61, + 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x4b, 0x0a, 0x0a, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, - 0x65, 0x72, 0x73, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x54, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4f, 0x49, 0x44, 0x43, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x20, 0x2e, 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x49, 0x44, 0x43, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6d, 0x61, 0x6e, 0x74, 0x72, - 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x49, 0x44, 0x43, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0xa3, 0x01, 0x0a, 0x0e, - 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x42, 0x09, - 0x55, 0x73, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3d, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, 0x69, 0x7a, 0x75, 0x63, 0x68, 0x69, 0x6c, - 0x61, 0x62, 0x73, 0x2f, 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2f, 0x76, 0x31, - 0x3b, 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x4d, 0x58, 0x58, - 0xaa, 0x02, 0x0a, 0x4d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0a, - 0x4d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x16, 0x4d, 0x61, 0x6e, - 0x74, 0x72, 0x61, 0x65, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0xea, 0x02, 0x0b, 0x4d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x3a, 0x3a, 0x56, - 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x55, 0x73, 0x65, 0x72, 0x12, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, + 0x72, 0x12, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1e, 0x2e, 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x4d, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x12, 0x1c, 0x2e, + 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, + 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6d, 0x61, + 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, + 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, + 0x54, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4f, 0x49, 0x44, 0x43, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x20, 0x2e, 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, + 0x74, 0x4f, 0x49, 0x44, 0x43, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x47, 0x65, 0x74, 0x4f, 0x49, 0x44, 0x43, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0xa3, 0x01, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x61, + 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2e, 0x76, 0x31, 0x42, 0x09, 0x55, 0x73, 0x65, 0x72, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x6d, 0x69, 0x7a, 0x75, 0x63, 0x68, 0x69, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x6d, 0x61, + 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, + 0x6d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x6d, 0x61, 0x6e, 0x74, 0x72, + 0x61, 0x65, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x4d, 0x58, 0x58, 0xaa, 0x02, 0x0a, 0x4d, 0x61, 0x6e, + 0x74, 0x72, 0x61, 0x65, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0a, 0x4d, 0x61, 0x6e, 0x74, 0x72, 0x61, + 0x65, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x16, 0x4d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x5c, 0x56, + 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0b, + 0x4d, 0x61, 0x6e, 0x74, 0x72, 0x61, 0x65, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, }) var ( @@ -1497,63 +1557,67 @@ func file_mantrae_v1_user_proto_rawDescGZIP() []byte { return file_mantrae_v1_user_proto_rawDescData } -var file_mantrae_v1_user_proto_msgTypes = make([]protoimpl.MessageInfo, 21) +var file_mantrae_v1_user_proto_msgTypes = make([]protoimpl.MessageInfo, 23) var file_mantrae_v1_user_proto_goTypes = []any{ (*User)(nil), // 0: mantrae.v1.User (*LoginUserRequest)(nil), // 1: mantrae.v1.LoginUserRequest (*LoginUserResponse)(nil), // 2: mantrae.v1.LoginUserResponse - (*VerifyJWTRequest)(nil), // 3: mantrae.v1.VerifyJWTRequest - (*VerifyJWTResponse)(nil), // 4: mantrae.v1.VerifyJWTResponse - (*VerifyOTPRequest)(nil), // 5: mantrae.v1.VerifyOTPRequest - (*VerifyOTPResponse)(nil), // 6: mantrae.v1.VerifyOTPResponse - (*SendOTPRequest)(nil), // 7: mantrae.v1.SendOTPRequest - (*SendOTPResponse)(nil), // 8: mantrae.v1.SendOTPResponse - (*GetUserRequest)(nil), // 9: mantrae.v1.GetUserRequest - (*GetUserResponse)(nil), // 10: mantrae.v1.GetUserResponse - (*CreateUserRequest)(nil), // 11: mantrae.v1.CreateUserRequest - (*CreateUserResponse)(nil), // 12: mantrae.v1.CreateUserResponse - (*UpdateUserRequest)(nil), // 13: mantrae.v1.UpdateUserRequest - (*UpdateUserResponse)(nil), // 14: mantrae.v1.UpdateUserResponse - (*DeleteUserRequest)(nil), // 15: mantrae.v1.DeleteUserRequest - (*DeleteUserResponse)(nil), // 16: mantrae.v1.DeleteUserResponse - (*ListUsersRequest)(nil), // 17: mantrae.v1.ListUsersRequest - (*ListUsersResponse)(nil), // 18: mantrae.v1.ListUsersResponse - (*GetOIDCStatusRequest)(nil), // 19: mantrae.v1.GetOIDCStatusRequest - (*GetOIDCStatusResponse)(nil), // 20: mantrae.v1.GetOIDCStatusResponse - (*timestamppb.Timestamp)(nil), // 21: google.protobuf.Timestamp + (*LogoutUserRequest)(nil), // 3: mantrae.v1.LogoutUserRequest + (*LogoutUserResponse)(nil), // 4: mantrae.v1.LogoutUserResponse + (*VerifyJWTRequest)(nil), // 5: mantrae.v1.VerifyJWTRequest + (*VerifyJWTResponse)(nil), // 6: mantrae.v1.VerifyJWTResponse + (*VerifyOTPRequest)(nil), // 7: mantrae.v1.VerifyOTPRequest + (*VerifyOTPResponse)(nil), // 8: mantrae.v1.VerifyOTPResponse + (*SendOTPRequest)(nil), // 9: mantrae.v1.SendOTPRequest + (*SendOTPResponse)(nil), // 10: mantrae.v1.SendOTPResponse + (*GetUserRequest)(nil), // 11: mantrae.v1.GetUserRequest + (*GetUserResponse)(nil), // 12: mantrae.v1.GetUserResponse + (*CreateUserRequest)(nil), // 13: mantrae.v1.CreateUserRequest + (*CreateUserResponse)(nil), // 14: mantrae.v1.CreateUserResponse + (*UpdateUserRequest)(nil), // 15: mantrae.v1.UpdateUserRequest + (*UpdateUserResponse)(nil), // 16: mantrae.v1.UpdateUserResponse + (*DeleteUserRequest)(nil), // 17: mantrae.v1.DeleteUserRequest + (*DeleteUserResponse)(nil), // 18: mantrae.v1.DeleteUserResponse + (*ListUsersRequest)(nil), // 19: mantrae.v1.ListUsersRequest + (*ListUsersResponse)(nil), // 20: mantrae.v1.ListUsersResponse + (*GetOIDCStatusRequest)(nil), // 21: mantrae.v1.GetOIDCStatusRequest + (*GetOIDCStatusResponse)(nil), // 22: mantrae.v1.GetOIDCStatusResponse + (*timestamppb.Timestamp)(nil), // 23: google.protobuf.Timestamp } var file_mantrae_v1_user_proto_depIdxs = []int32{ - 21, // 0: mantrae.v1.User.otp_expiry:type_name -> google.protobuf.Timestamp - 21, // 1: mantrae.v1.User.last_login:type_name -> google.protobuf.Timestamp - 21, // 2: mantrae.v1.User.created_at:type_name -> google.protobuf.Timestamp - 21, // 3: mantrae.v1.User.updated_at:type_name -> google.protobuf.Timestamp + 23, // 0: mantrae.v1.User.otp_expiry:type_name -> google.protobuf.Timestamp + 23, // 1: mantrae.v1.User.last_login:type_name -> google.protobuf.Timestamp + 23, // 2: mantrae.v1.User.created_at:type_name -> google.protobuf.Timestamp + 23, // 3: mantrae.v1.User.updated_at:type_name -> google.protobuf.Timestamp 0, // 4: mantrae.v1.VerifyJWTResponse.user:type_name -> mantrae.v1.User 0, // 5: mantrae.v1.GetUserResponse.user:type_name -> mantrae.v1.User 0, // 6: mantrae.v1.CreateUserResponse.user:type_name -> mantrae.v1.User 0, // 7: mantrae.v1.UpdateUserResponse.user:type_name -> mantrae.v1.User 0, // 8: mantrae.v1.ListUsersResponse.users:type_name -> mantrae.v1.User 1, // 9: mantrae.v1.UserService.LoginUser:input_type -> mantrae.v1.LoginUserRequest - 3, // 10: mantrae.v1.UserService.VerifyJWT:input_type -> mantrae.v1.VerifyJWTRequest - 5, // 11: mantrae.v1.UserService.VerifyOTP:input_type -> mantrae.v1.VerifyOTPRequest - 7, // 12: mantrae.v1.UserService.SendOTP:input_type -> mantrae.v1.SendOTPRequest - 9, // 13: mantrae.v1.UserService.GetUser:input_type -> mantrae.v1.GetUserRequest - 11, // 14: mantrae.v1.UserService.CreateUser:input_type -> mantrae.v1.CreateUserRequest - 13, // 15: mantrae.v1.UserService.UpdateUser:input_type -> mantrae.v1.UpdateUserRequest - 15, // 16: mantrae.v1.UserService.DeleteUser:input_type -> mantrae.v1.DeleteUserRequest - 17, // 17: mantrae.v1.UserService.ListUsers:input_type -> mantrae.v1.ListUsersRequest - 19, // 18: mantrae.v1.UserService.GetOIDCStatus:input_type -> mantrae.v1.GetOIDCStatusRequest - 2, // 19: mantrae.v1.UserService.LoginUser:output_type -> mantrae.v1.LoginUserResponse - 4, // 20: mantrae.v1.UserService.VerifyJWT:output_type -> mantrae.v1.VerifyJWTResponse - 6, // 21: mantrae.v1.UserService.VerifyOTP:output_type -> mantrae.v1.VerifyOTPResponse - 8, // 22: mantrae.v1.UserService.SendOTP:output_type -> mantrae.v1.SendOTPResponse - 10, // 23: mantrae.v1.UserService.GetUser:output_type -> mantrae.v1.GetUserResponse - 12, // 24: mantrae.v1.UserService.CreateUser:output_type -> mantrae.v1.CreateUserResponse - 14, // 25: mantrae.v1.UserService.UpdateUser:output_type -> mantrae.v1.UpdateUserResponse - 16, // 26: mantrae.v1.UserService.DeleteUser:output_type -> mantrae.v1.DeleteUserResponse - 18, // 27: mantrae.v1.UserService.ListUsers:output_type -> mantrae.v1.ListUsersResponse - 20, // 28: mantrae.v1.UserService.GetOIDCStatus:output_type -> mantrae.v1.GetOIDCStatusResponse - 19, // [19:29] is the sub-list for method output_type - 9, // [9:19] is the sub-list for method input_type + 3, // 10: mantrae.v1.UserService.LogoutUser:input_type -> mantrae.v1.LogoutUserRequest + 5, // 11: mantrae.v1.UserService.VerifyJWT:input_type -> mantrae.v1.VerifyJWTRequest + 7, // 12: mantrae.v1.UserService.VerifyOTP:input_type -> mantrae.v1.VerifyOTPRequest + 9, // 13: mantrae.v1.UserService.SendOTP:input_type -> mantrae.v1.SendOTPRequest + 11, // 14: mantrae.v1.UserService.GetUser:input_type -> mantrae.v1.GetUserRequest + 13, // 15: mantrae.v1.UserService.CreateUser:input_type -> mantrae.v1.CreateUserRequest + 15, // 16: mantrae.v1.UserService.UpdateUser:input_type -> mantrae.v1.UpdateUserRequest + 17, // 17: mantrae.v1.UserService.DeleteUser:input_type -> mantrae.v1.DeleteUserRequest + 19, // 18: mantrae.v1.UserService.ListUsers:input_type -> mantrae.v1.ListUsersRequest + 21, // 19: mantrae.v1.UserService.GetOIDCStatus:input_type -> mantrae.v1.GetOIDCStatusRequest + 2, // 20: mantrae.v1.UserService.LoginUser:output_type -> mantrae.v1.LoginUserResponse + 4, // 21: mantrae.v1.UserService.LogoutUser:output_type -> mantrae.v1.LogoutUserResponse + 6, // 22: mantrae.v1.UserService.VerifyJWT:output_type -> mantrae.v1.VerifyJWTResponse + 8, // 23: mantrae.v1.UserService.VerifyOTP:output_type -> mantrae.v1.VerifyOTPResponse + 10, // 24: mantrae.v1.UserService.SendOTP:output_type -> mantrae.v1.SendOTPResponse + 12, // 25: mantrae.v1.UserService.GetUser:output_type -> mantrae.v1.GetUserResponse + 14, // 26: mantrae.v1.UserService.CreateUser:output_type -> mantrae.v1.CreateUserResponse + 16, // 27: mantrae.v1.UserService.UpdateUser:output_type -> mantrae.v1.UpdateUserResponse + 18, // 28: mantrae.v1.UserService.DeleteUser:output_type -> mantrae.v1.DeleteUserResponse + 20, // 29: mantrae.v1.UserService.ListUsers:output_type -> mantrae.v1.ListUsersResponse + 22, // 30: mantrae.v1.UserService.GetOIDCStatus:output_type -> mantrae.v1.GetOIDCStatusResponse + 20, // [20:31] is the sub-list for method output_type + 9, // [9:20] is the sub-list for method input_type 9, // [9:9] is the sub-list for extension type_name 9, // [9:9] is the sub-list for extension extendee 0, // [0:9] is the sub-list for field type_name @@ -1568,28 +1632,28 @@ func file_mantrae_v1_user_proto_init() { (*LoginUserRequest_Username)(nil), (*LoginUserRequest_Email)(nil), } - file_mantrae_v1_user_proto_msgTypes[5].OneofWrappers = []any{ + file_mantrae_v1_user_proto_msgTypes[7].OneofWrappers = []any{ (*VerifyOTPRequest_Username)(nil), (*VerifyOTPRequest_Email)(nil), } - file_mantrae_v1_user_proto_msgTypes[7].OneofWrappers = []any{ + file_mantrae_v1_user_proto_msgTypes[9].OneofWrappers = []any{ (*SendOTPRequest_Username)(nil), (*SendOTPRequest_Email)(nil), } - file_mantrae_v1_user_proto_msgTypes[9].OneofWrappers = []any{ + file_mantrae_v1_user_proto_msgTypes[11].OneofWrappers = []any{ (*GetUserRequest_Id)(nil), (*GetUserRequest_Username)(nil), (*GetUserRequest_Email)(nil), } - file_mantrae_v1_user_proto_msgTypes[13].OneofWrappers = []any{} - file_mantrae_v1_user_proto_msgTypes[17].OneofWrappers = []any{} + file_mantrae_v1_user_proto_msgTypes[15].OneofWrappers = []any{} + file_mantrae_v1_user_proto_msgTypes[19].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_mantrae_v1_user_proto_rawDesc), len(file_mantrae_v1_user_proto_rawDesc)), NumEnums: 0, - NumMessages: 21, + NumMessages: 23, NumExtensions: 0, NumServices: 1, }, diff --git a/proto/gen/openapi/openapi.yaml b/proto/gen/openapi/openapi.yaml index c0797b3..db96c9d 100644 --- a/proto/gen/openapi/openapi.yaml +++ b/proto/gen/openapi/openapi.yaml @@ -2215,9 +2215,6 @@ components: type: string title: password minLength: 8 - remember: - type: boolean - title: remember title: LoginUserRequest required: - password @@ -2230,6 +2227,14 @@ components: title: token title: LoginUserResponse additionalProperties: false + mantrae.v1.LogoutUserRequest: + type: object + title: LogoutUserRequest + additionalProperties: false + mantrae.v1.LogoutUserResponse: + type: object + title: LogoutUserResponse + additionalProperties: false mantrae.v1.SendOTPRequest: type: object anyOf: @@ -2331,14 +2336,7 @@ components: additionalProperties: false mantrae.v1.VerifyJWTRequest: type: object - properties: - token: - type: string - title: token - minLength: 1 title: VerifyJWTRequest - required: - - token additionalProperties: false mantrae.v1.VerifyJWTResponse: type: object @@ -5131,6 +5129,41 @@ paths: application/json: schema: $ref: '#/components/schemas/mantrae.v1.LoginUserResponse' + /mantrae.v1.UserService/LogoutUser: + post: + tags: + - mantrae.v1.UserService + summary: LogoutUser + operationId: mantrae.v1.UserService.LogoutUser + parameters: + - name: Connect-Protocol-Version + in: header + required: true + schema: + $ref: '#/components/schemas/connect-protocol-version' + - name: Connect-Timeout-Ms + in: header + schema: + $ref: '#/components/schemas/connect-timeout-header' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/mantrae.v1.LogoutUserRequest' + required: true + responses: + default: + description: Error + content: + application/json: + schema: + $ref: '#/components/schemas/connect.error' + "200": + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/mantrae.v1.LogoutUserResponse' /mantrae.v1.UserService/VerifyJWT: post: tags: diff --git a/proto/mantrae/v1/user.proto b/proto/mantrae/v1/user.proto index 28bdd73..afa515a 100644 --- a/proto/mantrae/v1/user.proto +++ b/proto/mantrae/v1/user.proto @@ -7,6 +7,7 @@ import "google/protobuf/timestamp.proto"; service UserService { rpc LoginUser(LoginUserRequest) returns (LoginUserResponse); + rpc LogoutUser(LogoutUserRequest) returns (LogoutUserResponse); rpc VerifyJWT(VerifyJWTRequest) returns (VerifyJWTResponse); rpc VerifyOTP(VerifyOTPRequest) returns (VerifyOTPResponse); rpc SendOTP(SendOTPRequest) returns (SendOTPResponse); @@ -45,18 +46,15 @@ message LoginUserRequest { (buf.validate.field).required = true, (buf.validate.field).string.min_len = 8 ]; - bool remember = 4; } message LoginUserResponse { string token = 1; } -message VerifyJWTRequest { - string token = 1 [ - (buf.validate.field).required = true, - (buf.validate.field).string.min_len = 1 - ]; -} +message LogoutUserRequest {} +message LogoutUserResponse {} + +message VerifyJWTRequest {} message VerifyJWTResponse { User user = 1; } diff --git a/web/src/lib/api.ts b/web/src/lib/api.ts index 07a5289..dfe2a11 100644 --- a/web/src/lib/api.ts +++ b/web/src/lib/api.ts @@ -23,39 +23,31 @@ export const BASE_URL = import.meta.env.PROD ? "" : `http://127.0.0.1:${BACKEND_PORT}`; -export function useClient( - service: T, - fetch?: typeof window.fetch, -): Client { - // Custom fetch function that adds the Authorization header - const customFetch: typeof window.fetch = async (url, options) => { - const headers = new Headers(options?.headers); // Get existing headers - if (token.value) { - headers.set("Authorization", "Bearer " + token.value); // Add the Authorization header - } - const customOptions = { - ...options, - headers, - }; - return fetch ? fetch(url, customOptions) : window.fetch(url, customOptions); // Use custom fetch or default - }; +export function useClient(service: T): Client { + const headers = new Headers(); + headers.set("Content-Type", "application/json"); + // if (token.value) { + // headers.set("Authorization", "Bearer " + token.value); + // } const transport = createConnectTransport({ baseUrl: BASE_URL, - fetch: customFetch, + fetch: (input, init) => + fetch(input, { + ...init, + headers, + credentials: "include", + }), }); return createClient(service, transport); } -export function logout() { - token.value = null; - user.clear(); - goto("/login"); +export function handleOIDCLogin() { + window.location.href = `${BASE_URL}/oidc/login`; } -export async function upload(event: Event, endpoint: string) { - const input = event.target as HTMLInputElement; - if (!input.files?.length) return; +export async function upload(input: HTMLInputElement | null, endpoint: string) { + if (!input?.files?.length) return; const body = new FormData(); body.append("file", input.files[0]); diff --git a/web/src/lib/components/nav/AppSidebar.svelte b/web/src/lib/components/nav/AppSidebar.svelte index 5976a27..ef0be15 100644 --- a/web/src/lib/components/nav/AppSidebar.svelte +++ b/web/src/lib/components/nav/AppSidebar.svelte @@ -27,11 +27,12 @@ } from '@lucide/svelte'; import { profile } from '$lib/stores/profile'; import { user } from '$lib/stores/user'; - import { logout, profileClient } from '$lib/api'; + import { profileClient, userClient } from '$lib/api'; import type { Profile } from '$lib/gen/mantrae/v1/profile_pb'; import ProfileModal from '$lib/components/modals/profile.svelte'; import UserModal from '$lib/components/modals/user.svelte'; import { toggleMode, mode } from 'mode-watcher'; + import { goto } from '$app/navigation'; let { ...restProps }: ComponentProps = $props(); @@ -278,7 +279,13 @@ - logout()}> + { + userClient.logoutUser({}); + user.clear(); + goto('/login'); + }} + > Log out diff --git a/web/src/lib/gen/mantrae/v1/user_pb.ts b/web/src/lib/gen/mantrae/v1/user_pb.ts index c41c579..8d3995d 100644 --- a/web/src/lib/gen/mantrae/v1/user_pb.ts +++ b/web/src/lib/gen/mantrae/v1/user_pb.ts @@ -13,7 +13,7 @@ import type { Message } from "@bufbuild/protobuf"; * Describes the file mantrae/v1/user.proto. */ export const file_mantrae_v1_user: GenFile = /*@__PURE__*/ - fileDesc("ChVtYW50cmFlL3YxL3VzZXIucHJvdG8SCm1hbnRyYWUudjEipAIKBFVzZXISCgoCaWQYASABKAkSEAoIdXNlcm5hbWUYAiABKAkSEAoIcGFzc3dvcmQYAyABKAkSDQoFZW1haWwYBCABKAkSEAoIaXNfYWRtaW4YBSABKAgSCwoDb3RwGAYgASgJEi4KCm90cF9leHBpcnkYByABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEi4KCmxhc3RfbG9naW4YCCABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEi4KCmNyZWF0ZWRfYXQYCSABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEi4KCnVwZGF0ZWRfYXQYCiABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wIo4BChBMb2dpblVzZXJSZXF1ZXN0EhsKCHVzZXJuYW1lGAEgASgJQge6SARyAhADSAASGAoFZW1haWwYAiABKAlCB7pIBHICYAFIABIcCghwYXNzd29yZBgDIAEoCUIKukgHyAEBcgIQCBIQCghyZW1lbWJlchgEIAEoCEITCgppZGVudGlmaWVyEgW6SAIIASIiChFMb2dpblVzZXJSZXNwb25zZRINCgV0b2tlbhgBIAEoCSItChBWZXJpZnlKV1RSZXF1ZXN0EhkKBXRva2VuGAEgASgJQgq6SAfIAQFyAhABIjMKEVZlcmlmeUpXVFJlc3BvbnNlEh4KBHVzZXIYASABKAsyEC5tYW50cmFlLnYxLlVzZXIidwoQVmVyaWZ5T1RQUmVxdWVzdBIbCgh1c2VybmFtZRgBIAEoCUIHukgEcgIQA0gAEhgKBWVtYWlsGAIgASgJQge6SARyAmABSAASFwoDb3RwGAMgASgJQgq6SAfIAQFyAhAGQhMKCmlkZW50aWZpZXISBbpIAggBIiIKEVZlcmlmeU9UUFJlc3BvbnNlEg0KBXRva2VuGAEgASgJIlwKDlNlbmRPVFBSZXF1ZXN0EhsKCHVzZXJuYW1lGAEgASgJQge6SARyAhADSAASGAoFZW1haWwYAiABKAlCB7pIBHICYAFIAEITCgppZGVudGlmaWVyEgW6SAIIASIRCg9TZW5kT1RQUmVzcG9uc2UicwoOR2V0VXNlclJlcXVlc3QSFQoCaWQYASABKAlCB7pIBHICEAFIABIbCgh1c2VybmFtZRgCIAEoCUIHukgEcgIQA0gAEhgKBWVtYWlsGAMgASgJQge6SARyAmABSABCEwoKaWRlbnRpZmllchIFukgCCAEiMQoPR2V0VXNlclJlc3BvbnNlEh4KBHVzZXIYASABKAsyEC5tYW50cmFlLnYxLlVzZXIieQoRQ3JlYXRlVXNlclJlcXVlc3QSHAoIdXNlcm5hbWUYASABKAlCCrpIB8gBAXICEAMSHAoIcGFzc3dvcmQYAiABKAlCCrpIB8gBAXICEAgSFgoFZW1haWwYAyABKAlCB7pIBHICYAESEAoIaXNfYWRtaW4YBCABKAgiNAoSQ3JlYXRlVXNlclJlc3BvbnNlEh4KBHVzZXIYASABKAsyEC5tYW50cmFlLnYxLlVzZXIioAEKEVVwZGF0ZVVzZXJSZXF1ZXN0EhYKAmlkGAEgASgJQgq6SAfIAQFyAhABEhwKCHVzZXJuYW1lGAIgASgJQgq6SAfIAQFyAhADEhYKBWVtYWlsGAMgASgJQge6SARyAmABEhAKCGlzX2FkbWluGAQgASgIEh4KCHBhc3N3b3JkGAUgASgJQge6SARyAhAISACIAQFCCwoJX3Bhc3N3b3JkIjQKElVwZGF0ZVVzZXJSZXNwb25zZRIeCgR1c2VyGAEgASgLMhAubWFudHJhZS52MS5Vc2VyIisKEURlbGV0ZVVzZXJSZXF1ZXN0EhYKAmlkGAEgASgJQgq6SAfIAQFyAhABIhQKEkRlbGV0ZVVzZXJSZXNwb25zZSKxAQoQTGlzdFVzZXJzUmVxdWVzdBJqCgVsaW1pdBgBIAEoA0JWukhTugFQCgtsaW1pdC52YWxpZBIpbGltaXQgbXVzdCBiZSBlaXRoZXIgLTEgb3IgZ3JlYXRlciB0aGFuIDAaFnRoaXMgPT0gLTEgfHwgdGhpcyA+IDBIAIgBARIcCgZvZmZzZXQYAiABKANCB7pIBCICKABIAYgBAUIICgZfbGltaXRCCQoHX29mZnNldCJJChFMaXN0VXNlcnNSZXNwb25zZRIfCgV1c2VycxgBIAMoCzIQLm1hbnRyYWUudjEuVXNlchITCgt0b3RhbF9jb3VudBgCIAEoAyIWChRHZXRPSURDU3RhdHVzUmVxdWVzdCJWChVHZXRPSURDU3RhdHVzUmVzcG9uc2USFAoMb2lkY19lbmFibGVkGAEgASgIEhUKDWxvZ2luX2VuYWJsZWQYAiABKAgSEAoIcHJvdmlkZXIYAyABKAkyhAYKC1VzZXJTZXJ2aWNlEkgKCUxvZ2luVXNlchIcLm1hbnRyYWUudjEuTG9naW5Vc2VyUmVxdWVzdBodLm1hbnRyYWUudjEuTG9naW5Vc2VyUmVzcG9uc2USSAoJVmVyaWZ5SldUEhwubWFudHJhZS52MS5WZXJpZnlKV1RSZXF1ZXN0Gh0ubWFudHJhZS52MS5WZXJpZnlKV1RSZXNwb25zZRJICglWZXJpZnlPVFASHC5tYW50cmFlLnYxLlZlcmlmeU9UUFJlcXVlc3QaHS5tYW50cmFlLnYxLlZlcmlmeU9UUFJlc3BvbnNlEkIKB1NlbmRPVFASGi5tYW50cmFlLnYxLlNlbmRPVFBSZXF1ZXN0GhsubWFudHJhZS52MS5TZW5kT1RQUmVzcG9uc2USRwoHR2V0VXNlchIaLm1hbnRyYWUudjEuR2V0VXNlclJlcXVlc3QaGy5tYW50cmFlLnYxLkdldFVzZXJSZXNwb25zZSIDkAIBEksKCkNyZWF0ZVVzZXISHS5tYW50cmFlLnYxLkNyZWF0ZVVzZXJSZXF1ZXN0Gh4ubWFudHJhZS52MS5DcmVhdGVVc2VyUmVzcG9uc2USSwoKVXBkYXRlVXNlchIdLm1hbnRyYWUudjEuVXBkYXRlVXNlclJlcXVlc3QaHi5tYW50cmFlLnYxLlVwZGF0ZVVzZXJSZXNwb25zZRJLCgpEZWxldGVVc2VyEh0ubWFudHJhZS52MS5EZWxldGVVc2VyUmVxdWVzdBoeLm1hbnRyYWUudjEuRGVsZXRlVXNlclJlc3BvbnNlEk0KCUxpc3RVc2VycxIcLm1hbnRyYWUudjEuTGlzdFVzZXJzUmVxdWVzdBodLm1hbnRyYWUudjEuTGlzdFVzZXJzUmVzcG9uc2UiA5ACARJUCg1HZXRPSURDU3RhdHVzEiAubWFudHJhZS52MS5HZXRPSURDU3RhdHVzUmVxdWVzdBohLm1hbnRyYWUudjEuR2V0T0lEQ1N0YXR1c1Jlc3BvbnNlQqMBCg5jb20ubWFudHJhZS52MUIJVXNlclByb3RvUAFaPWdpdGh1Yi5jb20vbWl6dWNoaWxhYnMvbWFudHJhZS9wcm90by9nZW4vbWFudHJhZS92MTttYW50cmFldjGiAgNNWFiqAgpNYW50cmFlLlYxygIKTWFudHJhZVxWMeICFk1hbnRyYWVcVjFcR1BCTWV0YWRhdGHqAgtNYW50cmFlOjpWMWIGcHJvdG8z", [file_buf_validate_validate, file_google_protobuf_timestamp]); + fileDesc("ChVtYW50cmFlL3YxL3VzZXIucHJvdG8SCm1hbnRyYWUudjEipAIKBFVzZXISCgoCaWQYASABKAkSEAoIdXNlcm5hbWUYAiABKAkSEAoIcGFzc3dvcmQYAyABKAkSDQoFZW1haWwYBCABKAkSEAoIaXNfYWRtaW4YBSABKAgSCwoDb3RwGAYgASgJEi4KCm90cF9leHBpcnkYByABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEi4KCmxhc3RfbG9naW4YCCABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEi4KCmNyZWF0ZWRfYXQYCSABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEi4KCnVwZGF0ZWRfYXQYCiABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wInwKEExvZ2luVXNlclJlcXVlc3QSGwoIdXNlcm5hbWUYASABKAlCB7pIBHICEANIABIYCgVlbWFpbBgCIAEoCUIHukgEcgJgAUgAEhwKCHBhc3N3b3JkGAMgASgJQgq6SAfIAQFyAhAIQhMKCmlkZW50aWZpZXISBbpIAggBIiIKEUxvZ2luVXNlclJlc3BvbnNlEg0KBXRva2VuGAEgASgJIhMKEUxvZ291dFVzZXJSZXF1ZXN0IhQKEkxvZ291dFVzZXJSZXNwb25zZSISChBWZXJpZnlKV1RSZXF1ZXN0IjMKEVZlcmlmeUpXVFJlc3BvbnNlEh4KBHVzZXIYASABKAsyEC5tYW50cmFlLnYxLlVzZXIidwoQVmVyaWZ5T1RQUmVxdWVzdBIbCgh1c2VybmFtZRgBIAEoCUIHukgEcgIQA0gAEhgKBWVtYWlsGAIgASgJQge6SARyAmABSAASFwoDb3RwGAMgASgJQgq6SAfIAQFyAhAGQhMKCmlkZW50aWZpZXISBbpIAggBIiIKEVZlcmlmeU9UUFJlc3BvbnNlEg0KBXRva2VuGAEgASgJIlwKDlNlbmRPVFBSZXF1ZXN0EhsKCHVzZXJuYW1lGAEgASgJQge6SARyAhADSAASGAoFZW1haWwYAiABKAlCB7pIBHICYAFIAEITCgppZGVudGlmaWVyEgW6SAIIASIRCg9TZW5kT1RQUmVzcG9uc2UicwoOR2V0VXNlclJlcXVlc3QSFQoCaWQYASABKAlCB7pIBHICEAFIABIbCgh1c2VybmFtZRgCIAEoCUIHukgEcgIQA0gAEhgKBWVtYWlsGAMgASgJQge6SARyAmABSABCEwoKaWRlbnRpZmllchIFukgCCAEiMQoPR2V0VXNlclJlc3BvbnNlEh4KBHVzZXIYASABKAsyEC5tYW50cmFlLnYxLlVzZXIieQoRQ3JlYXRlVXNlclJlcXVlc3QSHAoIdXNlcm5hbWUYASABKAlCCrpIB8gBAXICEAMSHAoIcGFzc3dvcmQYAiABKAlCCrpIB8gBAXICEAgSFgoFZW1haWwYAyABKAlCB7pIBHICYAESEAoIaXNfYWRtaW4YBCABKAgiNAoSQ3JlYXRlVXNlclJlc3BvbnNlEh4KBHVzZXIYASABKAsyEC5tYW50cmFlLnYxLlVzZXIioAEKEVVwZGF0ZVVzZXJSZXF1ZXN0EhYKAmlkGAEgASgJQgq6SAfIAQFyAhABEhwKCHVzZXJuYW1lGAIgASgJQgq6SAfIAQFyAhADEhYKBWVtYWlsGAMgASgJQge6SARyAmABEhAKCGlzX2FkbWluGAQgASgIEh4KCHBhc3N3b3JkGAUgASgJQge6SARyAhAISACIAQFCCwoJX3Bhc3N3b3JkIjQKElVwZGF0ZVVzZXJSZXNwb25zZRIeCgR1c2VyGAEgASgLMhAubWFudHJhZS52MS5Vc2VyIisKEURlbGV0ZVVzZXJSZXF1ZXN0EhYKAmlkGAEgASgJQgq6SAfIAQFyAhABIhQKEkRlbGV0ZVVzZXJSZXNwb25zZSKxAQoQTGlzdFVzZXJzUmVxdWVzdBJqCgVsaW1pdBgBIAEoA0JWukhTugFQCgtsaW1pdC52YWxpZBIpbGltaXQgbXVzdCBiZSBlaXRoZXIgLTEgb3IgZ3JlYXRlciB0aGFuIDAaFnRoaXMgPT0gLTEgfHwgdGhpcyA+IDBIAIgBARIcCgZvZmZzZXQYAiABKANCB7pIBCICKABIAYgBAUIICgZfbGltaXRCCQoHX29mZnNldCJJChFMaXN0VXNlcnNSZXNwb25zZRIfCgV1c2VycxgBIAMoCzIQLm1hbnRyYWUudjEuVXNlchITCgt0b3RhbF9jb3VudBgCIAEoAyIWChRHZXRPSURDU3RhdHVzUmVxdWVzdCJWChVHZXRPSURDU3RhdHVzUmVzcG9uc2USFAoMb2lkY19lbmFibGVkGAEgASgIEhUKDWxvZ2luX2VuYWJsZWQYAiABKAgSEAoIcHJvdmlkZXIYAyABKAky0QYKC1VzZXJTZXJ2aWNlEkgKCUxvZ2luVXNlchIcLm1hbnRyYWUudjEuTG9naW5Vc2VyUmVxdWVzdBodLm1hbnRyYWUudjEuTG9naW5Vc2VyUmVzcG9uc2USSwoKTG9nb3V0VXNlchIdLm1hbnRyYWUudjEuTG9nb3V0VXNlclJlcXVlc3QaHi5tYW50cmFlLnYxLkxvZ291dFVzZXJSZXNwb25zZRJICglWZXJpZnlKV1QSHC5tYW50cmFlLnYxLlZlcmlmeUpXVFJlcXVlc3QaHS5tYW50cmFlLnYxLlZlcmlmeUpXVFJlc3BvbnNlEkgKCVZlcmlmeU9UUBIcLm1hbnRyYWUudjEuVmVyaWZ5T1RQUmVxdWVzdBodLm1hbnRyYWUudjEuVmVyaWZ5T1RQUmVzcG9uc2USQgoHU2VuZE9UUBIaLm1hbnRyYWUudjEuU2VuZE9UUFJlcXVlc3QaGy5tYW50cmFlLnYxLlNlbmRPVFBSZXNwb25zZRJHCgdHZXRVc2VyEhoubWFudHJhZS52MS5HZXRVc2VyUmVxdWVzdBobLm1hbnRyYWUudjEuR2V0VXNlclJlc3BvbnNlIgOQAgESSwoKQ3JlYXRlVXNlchIdLm1hbnRyYWUudjEuQ3JlYXRlVXNlclJlcXVlc3QaHi5tYW50cmFlLnYxLkNyZWF0ZVVzZXJSZXNwb25zZRJLCgpVcGRhdGVVc2VyEh0ubWFudHJhZS52MS5VcGRhdGVVc2VyUmVxdWVzdBoeLm1hbnRyYWUudjEuVXBkYXRlVXNlclJlc3BvbnNlEksKCkRlbGV0ZVVzZXISHS5tYW50cmFlLnYxLkRlbGV0ZVVzZXJSZXF1ZXN0Gh4ubWFudHJhZS52MS5EZWxldGVVc2VyUmVzcG9uc2USTQoJTGlzdFVzZXJzEhwubWFudHJhZS52MS5MaXN0VXNlcnNSZXF1ZXN0Gh0ubWFudHJhZS52MS5MaXN0VXNlcnNSZXNwb25zZSIDkAIBElQKDUdldE9JRENTdGF0dXMSIC5tYW50cmFlLnYxLkdldE9JRENTdGF0dXNSZXF1ZXN0GiEubWFudHJhZS52MS5HZXRPSURDU3RhdHVzUmVzcG9uc2VCowEKDmNvbS5tYW50cmFlLnYxQglVc2VyUHJvdG9QAVo9Z2l0aHViLmNvbS9taXp1Y2hpbGFicy9tYW50cmFlL3Byb3RvL2dlbi9tYW50cmFlL3YxO21hbnRyYWV2MaICA01YWKoCCk1hbnRyYWUuVjHKAgpNYW50cmFlXFYx4gIWTWFudHJhZVxWMVxHUEJNZXRhZGF0YeoCC01hbnRyYWU6OlYxYgZwcm90bzM", [file_buf_validate_validate, file_google_protobuf_timestamp]); /** * @generated from message mantrae.v1.User @@ -102,11 +102,6 @@ export type LoginUserRequest = Message<"mantrae.v1.LoginUserRequest"> & { * @generated from field: string password = 3; */ password: string; - - /** - * @generated from field: bool remember = 4; - */ - remember: boolean; }; /** @@ -133,14 +128,36 @@ export type LoginUserResponse = Message<"mantrae.v1.LoginUserResponse"> & { export const LoginUserResponseSchema: GenMessage = /*@__PURE__*/ messageDesc(file_mantrae_v1_user, 2); +/** + * @generated from message mantrae.v1.LogoutUserRequest + */ +export type LogoutUserRequest = Message<"mantrae.v1.LogoutUserRequest"> & { +}; + +/** + * Describes the message mantrae.v1.LogoutUserRequest. + * Use `create(LogoutUserRequestSchema)` to create a new message. + */ +export const LogoutUserRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_mantrae_v1_user, 3); + +/** + * @generated from message mantrae.v1.LogoutUserResponse + */ +export type LogoutUserResponse = Message<"mantrae.v1.LogoutUserResponse"> & { +}; + +/** + * Describes the message mantrae.v1.LogoutUserResponse. + * Use `create(LogoutUserResponseSchema)` to create a new message. + */ +export const LogoutUserResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_mantrae_v1_user, 4); + /** * @generated from message mantrae.v1.VerifyJWTRequest */ export type VerifyJWTRequest = Message<"mantrae.v1.VerifyJWTRequest"> & { - /** - * @generated from field: string token = 1; - */ - token: string; }; /** @@ -148,7 +165,7 @@ export type VerifyJWTRequest = Message<"mantrae.v1.VerifyJWTRequest"> & { * Use `create(VerifyJWTRequestSchema)` to create a new message. */ export const VerifyJWTRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_mantrae_v1_user, 3); + messageDesc(file_mantrae_v1_user, 5); /** * @generated from message mantrae.v1.VerifyJWTResponse @@ -165,7 +182,7 @@ export type VerifyJWTResponse = Message<"mantrae.v1.VerifyJWTResponse"> & { * Use `create(VerifyJWTResponseSchema)` to create a new message. */ export const VerifyJWTResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_mantrae_v1_user, 4); + messageDesc(file_mantrae_v1_user, 6); /** * @generated from message mantrae.v1.VerifyOTPRequest @@ -199,7 +216,7 @@ export type VerifyOTPRequest = Message<"mantrae.v1.VerifyOTPRequest"> & { * Use `create(VerifyOTPRequestSchema)` to create a new message. */ export const VerifyOTPRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_mantrae_v1_user, 5); + messageDesc(file_mantrae_v1_user, 7); /** * @generated from message mantrae.v1.VerifyOTPResponse @@ -216,7 +233,7 @@ export type VerifyOTPResponse = Message<"mantrae.v1.VerifyOTPResponse"> & { * Use `create(VerifyOTPResponseSchema)` to create a new message. */ export const VerifyOTPResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_mantrae_v1_user, 6); + messageDesc(file_mantrae_v1_user, 8); /** * @generated from message mantrae.v1.SendOTPRequest @@ -245,7 +262,7 @@ export type SendOTPRequest = Message<"mantrae.v1.SendOTPRequest"> & { * Use `create(SendOTPRequestSchema)` to create a new message. */ export const SendOTPRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_mantrae_v1_user, 7); + messageDesc(file_mantrae_v1_user, 9); /** * @generated from message mantrae.v1.SendOTPResponse @@ -258,7 +275,7 @@ export type SendOTPResponse = Message<"mantrae.v1.SendOTPResponse"> & { * Use `create(SendOTPResponseSchema)` to create a new message. */ export const SendOTPResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_mantrae_v1_user, 8); + messageDesc(file_mantrae_v1_user, 10); /** * @generated from message mantrae.v1.GetUserRequest @@ -293,7 +310,7 @@ export type GetUserRequest = Message<"mantrae.v1.GetUserRequest"> & { * Use `create(GetUserRequestSchema)` to create a new message. */ export const GetUserRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_mantrae_v1_user, 9); + messageDesc(file_mantrae_v1_user, 11); /** * @generated from message mantrae.v1.GetUserResponse @@ -310,7 +327,7 @@ export type GetUserResponse = Message<"mantrae.v1.GetUserResponse"> & { * Use `create(GetUserResponseSchema)` to create a new message. */ export const GetUserResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_mantrae_v1_user, 10); + messageDesc(file_mantrae_v1_user, 12); /** * @generated from message mantrae.v1.CreateUserRequest @@ -342,7 +359,7 @@ export type CreateUserRequest = Message<"mantrae.v1.CreateUserRequest"> & { * Use `create(CreateUserRequestSchema)` to create a new message. */ export const CreateUserRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_mantrae_v1_user, 11); + messageDesc(file_mantrae_v1_user, 13); /** * @generated from message mantrae.v1.CreateUserResponse @@ -359,7 +376,7 @@ export type CreateUserResponse = Message<"mantrae.v1.CreateUserResponse"> & { * Use `create(CreateUserResponseSchema)` to create a new message. */ export const CreateUserResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_mantrae_v1_user, 12); + messageDesc(file_mantrae_v1_user, 14); /** * @generated from message mantrae.v1.UpdateUserRequest @@ -396,7 +413,7 @@ export type UpdateUserRequest = Message<"mantrae.v1.UpdateUserRequest"> & { * Use `create(UpdateUserRequestSchema)` to create a new message. */ export const UpdateUserRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_mantrae_v1_user, 13); + messageDesc(file_mantrae_v1_user, 15); /** * @generated from message mantrae.v1.UpdateUserResponse @@ -413,7 +430,7 @@ export type UpdateUserResponse = Message<"mantrae.v1.UpdateUserResponse"> & { * Use `create(UpdateUserResponseSchema)` to create a new message. */ export const UpdateUserResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_mantrae_v1_user, 14); + messageDesc(file_mantrae_v1_user, 16); /** * @generated from message mantrae.v1.DeleteUserRequest @@ -430,7 +447,7 @@ export type DeleteUserRequest = Message<"mantrae.v1.DeleteUserRequest"> & { * Use `create(DeleteUserRequestSchema)` to create a new message. */ export const DeleteUserRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_mantrae_v1_user, 15); + messageDesc(file_mantrae_v1_user, 17); /** * @generated from message mantrae.v1.DeleteUserResponse @@ -443,7 +460,7 @@ export type DeleteUserResponse = Message<"mantrae.v1.DeleteUserResponse"> & { * Use `create(DeleteUserResponseSchema)` to create a new message. */ export const DeleteUserResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_mantrae_v1_user, 16); + messageDesc(file_mantrae_v1_user, 18); /** * @generated from message mantrae.v1.ListUsersRequest @@ -465,7 +482,7 @@ export type ListUsersRequest = Message<"mantrae.v1.ListUsersRequest"> & { * Use `create(ListUsersRequestSchema)` to create a new message. */ export const ListUsersRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_mantrae_v1_user, 17); + messageDesc(file_mantrae_v1_user, 19); /** * @generated from message mantrae.v1.ListUsersResponse @@ -487,7 +504,7 @@ export type ListUsersResponse = Message<"mantrae.v1.ListUsersResponse"> & { * Use `create(ListUsersResponseSchema)` to create a new message. */ export const ListUsersResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_mantrae_v1_user, 18); + messageDesc(file_mantrae_v1_user, 20); /** * @generated from message mantrae.v1.GetOIDCStatusRequest @@ -500,7 +517,7 @@ export type GetOIDCStatusRequest = Message<"mantrae.v1.GetOIDCStatusRequest"> & * Use `create(GetOIDCStatusRequestSchema)` to create a new message. */ export const GetOIDCStatusRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_mantrae_v1_user, 19); + messageDesc(file_mantrae_v1_user, 21); /** * @generated from message mantrae.v1.GetOIDCStatusResponse @@ -527,7 +544,7 @@ export type GetOIDCStatusResponse = Message<"mantrae.v1.GetOIDCStatusResponse"> * Use `create(GetOIDCStatusResponseSchema)` to create a new message. */ export const GetOIDCStatusResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_mantrae_v1_user, 20); + messageDesc(file_mantrae_v1_user, 22); /** * @generated from service mantrae.v1.UserService @@ -541,6 +558,14 @@ export const UserService: GenService<{ input: typeof LoginUserRequestSchema; output: typeof LoginUserResponseSchema; }, + /** + * @generated from rpc mantrae.v1.UserService.LogoutUser + */ + logoutUser: { + methodKind: "unary"; + input: typeof LogoutUserRequestSchema; + output: typeof LogoutUserResponseSchema; + }, /** * @generated from rpc mantrae.v1.UserService.VerifyJWT */ diff --git a/web/src/routes/+layout.ts b/web/src/routes/+layout.ts index be90ff0..1b871d4 100644 --- a/web/src/routes/+layout.ts +++ b/web/src/routes/+layout.ts @@ -1,7 +1,5 @@ import { goto } from "$app/navigation"; -import { logout, profileClient, useClient } from "$lib/api"; -import { UserService } from "$lib/gen/mantrae/v1/user_pb"; -import { token } from "$lib/stores/common"; +import { profileClient, userClient } from "$lib/api"; import { profile } from "$lib/stores/profile"; import { user } from "$lib/stores/user"; import type { LayoutLoad } from "./$types"; @@ -14,45 +12,78 @@ const isPublicRoute = (path: string) => { return path.startsWith("/login") || path === "/login"; }; -export const load: LayoutLoad = async ({ url, fetch }) => { - // Case 1: No token and accessing protected route - if (!token.value && !isPublicRoute(url.pathname)) { - await goto("/login/"); - user.clear(); - return; - } +export const load: LayoutLoad = async ({ url }) => { + const currentPath = url.pathname; + const isPublic = isPublicRoute(currentPath); - // If we have a token, verify it - if (token.value) { - try { - const client = useClient(UserService, fetch); - const verified = await client.verifyJWT({ token: token.value }); - if (!verified.user) { - throw new Error("Invalid token"); - } + try { + const verified = await userClient.verifyJWT({}); + + if (verified.user) { user.value = verified.user; + + // Update profile if not set if (!profile.id) { const response = await profileClient.listProfiles({}); profile.value = response.profiles[0]; } - // Redirect to home if trying to access login page while authenticated - if (isPublicRoute(url.pathname) && user.isLoggedIn()) { + if (isPublic) { + // Authenticated user trying to access login page - redirect to home await goto("/"); + return; } return; - } catch (error) { - // Token verification failed, clean up - logout(); - user.clear(); - throw new Error("Token verification failed: " + error); + } else { + throw new Error("Authentication failed"); } + } catch (_) { + user.clear(); + if (!isPublic) { + await goto("/login"); + } + return; } - - // No token and trying to access protected route - if (!isPublicRoute) { - await goto("/login"); - } - - return; }; + +// export const load: LayoutLoad = async ({ url }) => { +// // Case 1: No token and accessing protected route +// if (!token.value && !isPublicRoute(url.pathname)) { +// await goto("/login/"); +// user.clear(); +// return; +// } +// +// // If we have a token, verify it +// if (token.value) { +// try { +// const verified = await userClient.verifyJWT({}); +// if (!verified.user) { +// throw new Error("Invalid token"); +// } +// user.value = verified.user; +// if (!profile.id) { +// const response = await profileClient.listProfiles({}); +// profile.value = response.profiles[0]; +// } +// +// // Redirect to home if trying to access login page while authenticated +// if (isPublicRoute(url.pathname) && user.isLoggedIn()) { +// await goto("/"); +// } +// return; +// } catch (error) { +// // Token verification failed, clean up +// logout(); +// user.clear(); +// throw new Error("Token verification failed: " + error); +// } +// } +// +// // No token and trying to access protected route +// if (!isPublicRoute) { +// await goto("/login"); +// } +// +// return; +// }; diff --git a/web/src/routes/+page.svelte b/web/src/routes/+page.svelte index 6425ec6..f1c3acd 100644 --- a/web/src/routes/+page.svelte +++ b/web/src/routes/+page.svelte @@ -23,153 +23,155 @@ -
- - - Total Profiles - - - - {#await profileClient.listProfiles({}) then result} -
{result.totalCount}
- {/await} -
-
- - - - Connected Agents - - - - {#await agentClient.listAgents({ profileId: profile.id }) then result} -
{result.totalCount}
- {/await} -
-
- - - - Active DNS Provider - - - - {#await dnsClient.listDnsProviders({}) then result} -
- {result.dnsProviders.find((p) => p.isActive)?.name || 'None'} -
-

- {result.totalCount} providers configured -

- {/await} -
-
- - - - Total Users - - - - {#await userClient.listUsers({}) then result} -
{result.totalCount}
- {/await} -

-
-
-
- -
- - - - Profile Status - - -
+ {#if profile.id} +
+ + + Total Profiles + + + {#await profileClient.listProfiles({}) then result} - {#each result.profiles || [] as profile (profile.id)} -
-
- -
-

- {profile.name} -

-

- {profile.description} -

+
{result.totalCount}
+ {/await} + + + + + + Connected Agents + + + + {#await agentClient.listAgents({ profileId: profile.id }) then result} +
{result.totalCount}
+ {/await} +
+
+ + + + Active DNS Provider + + + + {#await dnsClient.listDnsProviders({}) then result} +
+ {result.dnsProviders.find((p) => p.isActive)?.name || 'None'} +
+

+ {result.totalCount} providers configured +

+ {/await} +
+
+ + + + Total Users + + + + {#await userClient.listUsers({}) then result} +
{result.totalCount}
+ {/await} +

+
+
+
+ +
+ + + + Profile Status + + +
+ {#await profileClient.listProfiles({}) then result} + {#each result.profiles || [] as profile (profile.id)} +
+
+ +
+

+ {profile.name} +

+

+ {profile.description} +

+
+
+
+ {#await agentClient.listAgents({ profileId: profile.id }) then result} + 0 ? 'default' : 'secondary'}> + {result.totalCount} + {result.totalCount === 1n ? 'Agent' : 'Agents'} + + {/await} + {#await routerClient.listRouters({ profileId: profile.id }) then result} + 0 ? 'default' : 'secondary'}> + {result.totalCount} + {result.totalCount === 1n ? 'Router' : 'Routers'} + + {/await} + {#await serviceClient.listServices({ profileId: profile.id }) then result} + 0 ? 'default' : 'secondary'}> + {result.totalCount} + {result.totalCount === 1n ? 'Service' : 'Services'} + + {/await} + {#await middlewareClient.listMiddlewares({ profileId: profile.id }) then result} + 0 ? 'default' : 'secondary'}> + {result.totalCount} + {result.totalCount === 1n ? 'Middleware' : 'Middlewares'} + + {/await}
-
- {#await agentClient.listAgents({ profileId: profile.id }) then result} - 0 ? 'default' : 'secondary'}> - {result.totalCount} - {result.totalCount === 1n ? 'Agent' : 'Agents'} - - {/await} - {#await routerClient.listRouters({ profileId: profile.id }) then result} - 0 ? 'default' : 'secondary'}> - {result.totalCount} - {result.totalCount === 1n ? 'Router' : 'Routers'} - - {/await} - {#await serviceClient.listServices({ profileId: profile.id }) then result} - 0 ? 'default' : 'secondary'}> - {result.totalCount} - {result.totalCount === 1n ? 'Service' : 'Services'} - - {/await} - {#await middlewareClient.listMiddlewares({ profileId: profile.id }) then result} - 0 ? 'default' : 'secondary'}> - {result.totalCount} - {result.totalCount === 1n ? 'Middleware' : 'Middlewares'} - - {/await} -
-
- {/each} - {/await} -
- - + {/each} + {/await} +
+ + - - - - - System Errors - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - -
-
-
-
+ + + + + System Errors + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + +
+
+
+
+ {/if}
diff --git a/web/src/routes/login/+page.svelte b/web/src/routes/login/+page.svelte index 7c63b38..522d449 100644 --- a/web/src/routes/login/+page.svelte +++ b/web/src/routes/login/+page.svelte @@ -8,13 +8,12 @@ import PasswordInput from '$lib/components/ui/password-input/password-input.svelte'; import { goto } from '$app/navigation'; import { user } from '$lib/stores/user'; - import { token } from '$lib/stores/common'; import { ConnectError } from '@connectrpc/connect'; import { profile } from '$lib/stores/profile'; + import { handleOIDCLogin } from '$lib/api'; let username = $state(''); let password = $state(''); - let remember = $state(false); const handleReset = async () => { if (username.length > 0) { @@ -42,20 +41,15 @@ const isEmail = username.includes('@'); try { - const response = await userClient.loginUser({ + await userClient.loginUser({ identifier: { case: isEmail ? 'email' : 'username', value: username }, - password: password, - remember: remember + password: password }); - token.value = response.token ?? null; - if (!token.value) { - throw new Error('No token received'); - } - const verified = await userClient.verifyJWT({ token: token.value }); + const verified = await userClient.verifyJWT({}); if (verified.user) { user.value = verified.user; if (!profile.id) { @@ -71,9 +65,9 @@ } }; - const handleOIDCLogin = () => { - window.location.href = '/api/oidc/login'; - }; + // const handleOIDCLogin = () => { + // window.location.href = '/oidc/login'; + // }; {#if !user.isLoggedIn()} @@ -86,7 +80,7 @@
{#await userClient.getOIDCStatus({}) then value} {#if value.loginEnabled} -
+
@@ -121,7 +115,7 @@ {/if} {#if value.oidcEnabled} - {/if} diff --git a/web/src/routes/settings/+page.svelte b/web/src/routes/settings/+page.svelte index 0f7597a..a11b266 100644 --- a/web/src/routes/settings/+page.svelte +++ b/web/src/routes/settings/+page.svelte @@ -43,6 +43,12 @@ type: 'text', description: 'The base URL of your backend server, including protocol (e.g., https://example.com).' + }, + { + key: 'storage_select', + label: 'Storage Type', + type: 'select', + description: 'Select the storage backend for backups (e.g., local, S3).' } ] }, @@ -67,18 +73,6 @@ label: 'Backups to Keep', type: 'number', description: 'The number of recent backups to retain.' - }, - { - key: 'backup_path', - label: 'Backup Path', - type: 'text', - description: 'Local filesystem path where backups will be stored.' - }, - { - key: 'backup_storage_select', - label: 'Storage Type', - type: 'select', - description: 'Select the storage backend for backups (e.g., local, S3).' } ] }, @@ -593,7 +587,7 @@ {settingsMap[setting.key] || 'Select...'} - {#if setting.key === 'backup_storage_select'} + {#if setting.key === 'storage_select'} {#each storageTypes as option (option.value)} {option.label} {/each}