From 89e4efcf077774a7e5df617f20ded538a98443f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= Date: Thu, 13 Aug 2026 10:27:28 +0200 Subject: [PATCH] consistency across member provider configs. in the furure we should refactor all configs to be part of the providers with init and set functions. --- server/internal/config/member.go | 171 ++++++++++++++++++++----- server/internal/member/oauth/config.go | 104 --------------- 2 files changed, 140 insertions(+), 135 deletions(-) diff --git a/server/internal/config/member.go b/server/internal/config/member.go index ae61b4d9..7d252763 100644 --- a/server/internal/config/member.go +++ b/server/internal/config/member.go @@ -13,6 +13,30 @@ import ( "github.com/m1k1o/neko/server/pkg/utils" ) +var DefaultUserProfile = types.MemberProfile{ + IsAdmin: false, + CanLogin: true, + CanConnect: true, + CanWatch: true, + CanHost: true, + CanShareMedia: true, + CanAccessClipboard: true, + SendsInactiveCursor: true, + CanSeeInactiveCursors: false, +} + +var DefaultAdminProfile = types.MemberProfile{ + IsAdmin: true, + CanLogin: true, + CanConnect: true, + CanWatch: true, + CanHost: true, + CanShareMedia: true, + CanAccessClipboard: true, + SendsInactiveCursor: true, + CanSeeInactiveCursors: true, +} + type Member struct { Provider string @@ -67,7 +91,89 @@ func (Member) Init(cmd *cobra.Command) error { return err } - if err := oauth.Init(cmd); err != nil { + // oauth provider + cmd.PersistentFlags().Bool("member.oauth.enabled", false, "enable OAuth 2.0 login") + if err := viper.BindPFlag("member.oauth.enabled", cmd.PersistentFlags().Lookup("member.oauth.enabled")); err != nil { + return err + } + + cmd.PersistentFlags().Bool("member.oauth.auto_redirect", false, "redirect the Neko root page to OAuth login automatically") + if err := viper.BindPFlag("member.oauth.auto_redirect", cmd.PersistentFlags().Lookup("member.oauth.auto_redirect")); err != nil { + return err + } + + cmd.PersistentFlags().String("member.oauth.name", "OAuth", "display name for the OAuth login option") + if err := viper.BindPFlag("member.oauth.name", cmd.PersistentFlags().Lookup("member.oauth.name")); err != nil { + return err + } + + cmd.PersistentFlags().StringSlice("member.oauth.admin_emails", []string{}, "OAuth user-info email addresses granted administrator access") + if err := viper.BindPFlag("member.oauth.admin_emails", cmd.PersistentFlags().Lookup("member.oauth.admin_emails")); err != nil { + return err + } + + cmd.PersistentFlags().String("member.oauth.client_id", "", "OAuth 2.0 client ID") + if err := viper.BindPFlag("member.oauth.client_id", cmd.PersistentFlags().Lookup("member.oauth.client_id")); err != nil { + return err + } + + cmd.PersistentFlags().String("member.oauth.client_secret", "", "OAuth 2.0 client secret") + if err := viper.BindPFlag("member.oauth.client_secret", cmd.PersistentFlags().Lookup("member.oauth.client_secret")); err != nil { + return err + } + + cmd.PersistentFlags().String("member.oauth.issuer_url", "", "OpenID Connect issuer URL used to discover OAuth endpoints") + if err := viper.BindPFlag("member.oauth.issuer_url", cmd.PersistentFlags().Lookup("member.oauth.issuer_url")); err != nil { + return err + } + + cmd.PersistentFlags().String("member.oauth.authorization_url", "", "OAuth 2.0 authorization endpoint") + if err := viper.BindPFlag("member.oauth.authorization_url", cmd.PersistentFlags().Lookup("member.oauth.authorization_url")); err != nil { + return err + } + + cmd.PersistentFlags().String("member.oauth.token_url", "", "OAuth 2.0 token endpoint") + if err := viper.BindPFlag("member.oauth.token_url", cmd.PersistentFlags().Lookup("member.oauth.token_url")); err != nil { + return err + } + + cmd.PersistentFlags().String("member.oauth.userinfo_url", "", "OAuth 2.0 user-info endpoint") + if err := viper.BindPFlag("member.oauth.userinfo_url", cmd.PersistentFlags().Lookup("member.oauth.userinfo_url")); err != nil { + return err + } + + cmd.PersistentFlags().String("member.oauth.redirect_url", "", "OAuth 2.0 callback URL registered with the provider") + if err := viper.BindPFlag("member.oauth.redirect_url", cmd.PersistentFlags().Lookup("member.oauth.redirect_url")); err != nil { + return err + } + + cmd.PersistentFlags().StringSlice("member.oauth.scopes", []string{"openid", "profile"}, "OAuth 2.0 scopes") + if err := viper.BindPFlag("member.oauth.scopes", cmd.PersistentFlags().Lookup("member.oauth.scopes")); err != nil { + return err + } + + cmd.PersistentFlags().String("member.oauth.subject_field", "sub", "field in user-info response used as the stable user identifier") + if err := viper.BindPFlag("member.oauth.subject_field", cmd.PersistentFlags().Lookup("member.oauth.subject_field")); err != nil { + return err + } + + cmd.PersistentFlags().String("member.oauth.username_field", "name", "field in user-info response used as the display name") + if err := viper.BindPFlag("member.oauth.username_field", cmd.PersistentFlags().Lookup("member.oauth.username_field")); err != nil { + return err + } + + cmd.PersistentFlags().String("member.oauth.avatar_field", "picture", "field in user-info response used as the avatar URL") + if err := viper.BindPFlag("member.oauth.avatar_field", cmd.PersistentFlags().Lookup("member.oauth.avatar_field")); err != nil { + return err + } + + cmd.PersistentFlags().String("member.oauth.success_redirect", "/", "relative URL to redirect to after OAuth login") + if err := viper.BindPFlag("member.oauth.success_redirect", cmd.PersistentFlags().Lookup("member.oauth.success_redirect")); err != nil { + return err + } + + cmd.PersistentFlags().String("member.oauth.user_profile", "{}", "OAuth user permission profile") + if err := viper.BindPFlag("member.oauth.user_profile", cmd.PersistentFlags().Lookup("member.oauth.user_profile")); err != nil { return err } @@ -106,47 +212,50 @@ func (s *Member) Set() { s.Multiuser.UserPassword = viper.GetString("member.multiuser.user_password") s.Multiuser.AdminPassword = viper.GetString("member.multiuser.admin_password") - // default user profile - s.Multiuser.UserProfile = types.MemberProfile{ - IsAdmin: false, - CanLogin: true, - CanConnect: true, - CanWatch: true, - CanHost: true, - CanShareMedia: true, - CanAccessClipboard: true, - SendsInactiveCursor: true, - CanSeeInactiveCursors: false, - } - - // override user profile + s.Multiuser.UserProfile = DefaultUserProfile if err := viper.UnmarshalKey("member.multiuser.user_profile", &s.Multiuser.UserProfile, viper.DecodeHook( utils.JsonStringAutoDecode(s.Multiuser.UserProfile), )); err != nil { log.Warn().Err(err).Msgf("unable to parse member multiuser user profile") } - // default admin profile - s.Multiuser.AdminProfile = types.MemberProfile{ - IsAdmin: true, - CanLogin: true, - CanConnect: true, - CanWatch: true, - CanHost: true, - CanShareMedia: true, - CanAccessClipboard: true, - SendsInactiveCursor: true, - CanSeeInactiveCursors: true, - } - - // override admin profile + s.Multiuser.AdminProfile = DefaultAdminProfile if err := viper.UnmarshalKey("member.multiuser.admin_profile", &s.Multiuser.AdminProfile, viper.DecodeHook( utils.JsonStringAutoDecode(s.Multiuser.AdminProfile), )); err != nil { log.Warn().Err(err).Msgf("unable to parse member multiuser admin profile") } - if err := s.OAuth.Set(s.Multiuser.UserProfile, s.Multiuser.AdminProfile); err != nil { - log.Warn().Err(err).Msg("unable to configure member OAuth provider") + + // oauth provider + s.OAuth.Enabled = viper.GetBool("member.oauth.enabled") + s.OAuth.AutoRedirect = viper.GetBool("member.oauth.auto_redirect") + s.OAuth.Name = viper.GetString("member.oauth.name") + s.OAuth.AdminEmails = viper.GetStringSlice("member.oauth.admin_emails") + s.OAuth.ClientID = viper.GetString("member.oauth.client_id") + s.OAuth.ClientSecret = viper.GetString("member.oauth.client_secret") + s.OAuth.IssuerURL = viper.GetString("member.oauth.issuer_url") + s.OAuth.AuthorizationURL = viper.GetString("member.oauth.authorization_url") + s.OAuth.TokenURL = viper.GetString("member.oauth.token_url") + s.OAuth.UserInfoURL = viper.GetString("member.oauth.userinfo_url") + s.OAuth.RedirectURL = viper.GetString("member.oauth.redirect_url") + s.OAuth.Scopes = viper.GetStringSlice("member.oauth.scopes") + s.OAuth.SubjectField = viper.GetString("member.oauth.subject_field") + s.OAuth.UsernameField = viper.GetString("member.oauth.username_field") + s.OAuth.AvatarField = viper.GetString("member.oauth.avatar_field") + s.OAuth.SuccessRedirect = viper.GetString("member.oauth.success_redirect") + + s.OAuth.UserProfile = DefaultUserProfile + if err := viper.UnmarshalKey("member.oauth.user_profile", &s.OAuth.UserProfile, viper.DecodeHook( + utils.JsonStringAutoDecode(s.OAuth.UserProfile), + )); err != nil { + log.Warn().Err(err).Msgf("unable to parse member OAuth user profile") + } + + s.OAuth.AdminProfile = DefaultAdminProfile + if err := viper.UnmarshalKey("member.oauth.admin_profile", &s.OAuth.AdminProfile, viper.DecodeHook( + utils.JsonStringAutoDecode(s.OAuth.AdminProfile), + )); err != nil { + log.Warn().Err(err).Msgf("unable to parse member OAuth admin profile") } } diff --git a/server/internal/member/oauth/config.go b/server/internal/member/oauth/config.go index 99208484..5a3ec4d0 100644 --- a/server/internal/member/oauth/config.go +++ b/server/internal/member/oauth/config.go @@ -1,14 +1,9 @@ package oauth import ( - "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/m1k1o/neko/server/pkg/types" - "github.com/m1k1o/neko/server/pkg/utils" ) -// Config contains the OAuth member provider settings. type Config struct { Enabled bool AutoRedirect bool @@ -29,102 +24,3 @@ type Config struct { AdminProfile types.MemberProfile UserProfile types.MemberProfile } - -func Init(cmd *cobra.Command) error { - flags := cmd.PersistentFlags() - flags.Bool("member.oauth.enabled", false, "enable OAuth 2.0 login") - if err := viper.BindPFlag("member.oauth.enabled", flags.Lookup("member.oauth.enabled")); err != nil { - return err - } - flags.Bool("member.oauth.auto_redirect", false, "redirect the Neko root page to OAuth login automatically") - if err := viper.BindPFlag("member.oauth.auto_redirect", flags.Lookup("member.oauth.auto_redirect")); err != nil { - return err - } - flags.String("member.oauth.name", "OAuth", "display name for the OAuth login option") - if err := viper.BindPFlag("member.oauth.name", flags.Lookup("member.oauth.name")); err != nil { - return err - } - flags.StringSlice("member.oauth.admin_emails", []string{}, "OAuth user-info email addresses granted administrator access") - if err := viper.BindPFlag("member.oauth.admin_emails", flags.Lookup("member.oauth.admin_emails")); err != nil { - return err - } - flags.String("member.oauth.client_id", "", "OAuth 2.0 client ID") - if err := viper.BindPFlag("member.oauth.client_id", flags.Lookup("member.oauth.client_id")); err != nil { - return err - } - flags.String("member.oauth.client_secret", "", "OAuth 2.0 client secret") - if err := viper.BindPFlag("member.oauth.client_secret", flags.Lookup("member.oauth.client_secret")); err != nil { - return err - } - flags.String("member.oauth.issuer_url", "", "OpenID Connect issuer URL used to discover OAuth endpoints") - if err := viper.BindPFlag("member.oauth.issuer_url", flags.Lookup("member.oauth.issuer_url")); err != nil { - return err - } - flags.String("member.oauth.authorization_url", "", "OAuth 2.0 authorization endpoint") - if err := viper.BindPFlag("member.oauth.authorization_url", flags.Lookup("member.oauth.authorization_url")); err != nil { - return err - } - flags.String("member.oauth.token_url", "", "OAuth 2.0 token endpoint") - if err := viper.BindPFlag("member.oauth.token_url", flags.Lookup("member.oauth.token_url")); err != nil { - return err - } - flags.String("member.oauth.userinfo_url", "", "OAuth 2.0 user-info endpoint") - if err := viper.BindPFlag("member.oauth.userinfo_url", flags.Lookup("member.oauth.userinfo_url")); err != nil { - return err - } - flags.String("member.oauth.redirect_url", "", "OAuth 2.0 callback URL registered with the provider") - if err := viper.BindPFlag("member.oauth.redirect_url", flags.Lookup("member.oauth.redirect_url")); err != nil { - return err - } - flags.StringSlice("member.oauth.scopes", []string{"openid", "profile"}, "OAuth 2.0 scopes") - if err := viper.BindPFlag("member.oauth.scopes", flags.Lookup("member.oauth.scopes")); err != nil { - return err - } - flags.String("member.oauth.subject_field", "sub", "field in user-info response used as the stable user identifier") - if err := viper.BindPFlag("member.oauth.subject_field", flags.Lookup("member.oauth.subject_field")); err != nil { - return err - } - flags.String("member.oauth.username_field", "name", "field in user-info response used as the display name") - if err := viper.BindPFlag("member.oauth.username_field", flags.Lookup("member.oauth.username_field")); err != nil { - return err - } - flags.String("member.oauth.avatar_field", "picture", "field in user-info response used as the avatar URL") - if err := viper.BindPFlag("member.oauth.avatar_field", flags.Lookup("member.oauth.avatar_field")); err != nil { - return err - } - flags.String("member.oauth.success_redirect", "/", "relative URL to redirect to after OAuth login") - if err := viper.BindPFlag("member.oauth.success_redirect", flags.Lookup("member.oauth.success_redirect")); err != nil { - return err - } - flags.String("member.oauth.user_profile", "{}", "OAuth user permission profile") - if err := viper.BindPFlag("member.oauth.user_profile", flags.Lookup("member.oauth.user_profile")); err != nil { - return err - } - flags.String("member.oauth.admin_profile", "{}", "OAuth administrator permission profile") - return viper.BindPFlag("member.oauth.admin_profile", flags.Lookup("member.oauth.admin_profile")) -} - -func (c *Config) Set(userProfile, adminProfile types.MemberProfile) error { - c.Enabled = viper.GetBool("member.oauth.enabled") - c.AutoRedirect = viper.GetBool("member.oauth.auto_redirect") - c.Name = viper.GetString("member.oauth.name") - c.AdminEmails = viper.GetStringSlice("member.oauth.admin_emails") - c.ClientID = viper.GetString("member.oauth.client_id") - c.ClientSecret = viper.GetString("member.oauth.client_secret") - c.IssuerURL = viper.GetString("member.oauth.issuer_url") - c.AuthorizationURL = viper.GetString("member.oauth.authorization_url") - c.TokenURL = viper.GetString("member.oauth.token_url") - c.UserInfoURL = viper.GetString("member.oauth.userinfo_url") - c.RedirectURL = viper.GetString("member.oauth.redirect_url") - c.Scopes = viper.GetStringSlice("member.oauth.scopes") - c.SubjectField = viper.GetString("member.oauth.subject_field") - c.UsernameField = viper.GetString("member.oauth.username_field") - c.AvatarField = viper.GetString("member.oauth.avatar_field") - c.SuccessRedirect = viper.GetString("member.oauth.success_redirect") - c.UserProfile = userProfile - c.AdminProfile = adminProfile - if err := viper.UnmarshalKey("member.oauth.user_profile", &c.UserProfile, viper.DecodeHook(utils.JsonStringAutoDecode(c.UserProfile))); err != nil { - return err - } - return viper.UnmarshalKey("member.oauth.admin_profile", &c.AdminProfile, viper.DecodeHook(utils.JsonStringAutoDecode(c.AdminProfile))) -}