mirror of
https://github.com/m1k1o/neko.git
synced 2026-11-03 00:11:48 -06:00
feat: implement user email restrictions for OAuth login
This commit is contained in:
@@ -101,6 +101,10 @@ func oauthServiceError(err error, start bool) error {
|
||||
return utils.HttpForbidden("logins are locked").WithInternalErr(err)
|
||||
}
|
||||
|
||||
if errors.Is(err, types.ErrSessionEmailNotAllowed) {
|
||||
return utils.HttpForbidden("email address is not allowed to log in").WithInternalErr(err)
|
||||
}
|
||||
|
||||
if start && strings.HasPrefix(err.Error(), "issuer discovery:") {
|
||||
return utils.HttpError(http.StatusServiceUnavailable, "OAuth issuer discovery failed").WithInternalErr(err)
|
||||
}
|
||||
|
||||
@@ -112,6 +112,11 @@ func (Member) Init(cmd *cobra.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().StringSlice("member.oauth.user_emails", []string{}, "OAuth user-info email addresses allowed to log in, if empty all are allowed")
|
||||
if err := viper.BindPFlag("member.oauth.user_emails", cmd.PersistentFlags().Lookup("member.oauth.user_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
|
||||
@@ -231,6 +236,7 @@ func (s *Member) Set() {
|
||||
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.UserEmails = viper.GetStringSlice("member.oauth.user_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")
|
||||
|
||||
@@ -9,6 +9,7 @@ type Config struct {
|
||||
AutoRedirect bool
|
||||
Name string
|
||||
AdminEmails []string
|
||||
UserEmails []string
|
||||
ClientID string
|
||||
ClientSecret string
|
||||
IssuerURL string
|
||||
|
||||
@@ -167,9 +167,14 @@ func (service *Service) Complete(ctx context.Context, stateToken, code string) (
|
||||
}
|
||||
|
||||
isAdmin := boolClaim(claims, "isAdmin") || boolClaim(idClaims, "isAdmin")
|
||||
admin := administrator(email, isAdmin, config.AdminEmails)
|
||||
|
||||
if !admin && !emailAllowed(email, config.UserEmails) {
|
||||
return nil, "", types.ErrSessionEmailNotAllowed
|
||||
}
|
||||
|
||||
var profile types.MemberProfile
|
||||
if administrator(email, isAdmin, config.AdminEmails) {
|
||||
if admin {
|
||||
profile = config.AdminProfile
|
||||
} else {
|
||||
profile = config.UserProfile
|
||||
|
||||
@@ -74,6 +74,19 @@ func administrator(email string, isAdmin bool, emails []string) bool {
|
||||
if isAdmin {
|
||||
return true
|
||||
}
|
||||
return emailMatches(email, emails)
|
||||
}
|
||||
|
||||
// emailAllowed reports whether email is permitted to log in given a user
|
||||
// allowlist. An empty allowlist permits every email.
|
||||
func emailAllowed(email string, emails []string) bool {
|
||||
if len(emails) == 0 {
|
||||
return true
|
||||
}
|
||||
return emailMatches(email, emails)
|
||||
}
|
||||
|
||||
func emailMatches(email string, emails []string) bool {
|
||||
for _, candidate := range emails {
|
||||
if email != "" && strings.EqualFold(strings.TrimSpace(email), strings.TrimSpace(candidate)) {
|
||||
return true
|
||||
|
||||
@@ -12,6 +12,7 @@ var (
|
||||
ErrSessionAlreadyConnected = errors.New("session is already connected")
|
||||
ErrSessionLoginDisabled = errors.New("session login disabled")
|
||||
ErrSessionLoginsLocked = errors.New("session logins locked")
|
||||
ErrSessionEmailNotAllowed = errors.New("email address is not allowed to log in")
|
||||
)
|
||||
|
||||
type Cursor struct {
|
||||
|
||||
Reference in New Issue
Block a user