-
-
Notifications
You must be signed in to change notification settings - Fork 233
feat: add ldap support #232
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1e413e6
e55f29c
3fe17cb
0e43c50
01042a3
c671ef1
dc3b2bc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,7 @@ | |
| "sync" | ||
| "time" | ||
| "tinyauth/internal/docker" | ||
| "tinyauth/internal/ldap" | ||
| "tinyauth/internal/types" | ||
| "tinyauth/internal/utils" | ||
|
|
||
|
|
@@ -22,9 +23,10 @@ | |
| LoginAttempts map[string]*types.LoginAttempt | ||
| LoginMutex sync.RWMutex | ||
| Store *sessions.CookieStore | ||
| LDAP *ldap.LDAP | ||
| } | ||
|
|
||
| func NewAuth(config types.AuthConfig, docker *docker.Docker) *Auth { | ||
| func NewAuth(config types.AuthConfig, docker *docker.Docker, ldap *ldap.LDAP) *Auth { | ||
| // Create cookie store | ||
| store := sessions.NewCookieStore([]byte(config.HMACSecret), []byte(config.EncryptionSecret)) | ||
|
|
||
|
|
@@ -42,6 +44,7 @@ | |
| Docker: docker, | ||
| LoginAttempts: make(map[string]*types.LoginAttempt), | ||
| Store: store, | ||
| LDAP: ldap, | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -68,14 +71,97 @@ | |
| return session, nil | ||
| } | ||
|
|
||
| func (auth *Auth) GetUser(username string) *types.User { | ||
| func (auth *Auth) SearchUser(username string) types.UserSearch { | ||
| // Loop through users and return the user if the username matches | ||
| log.Debug().Str("username", username).Msg("Searching for user") | ||
|
|
||
| if auth.GetLocalUser(username).Username != "" { | ||
| log.Debug().Str("username", username).Msg("Found local user") | ||
|
|
||
| // If user found, return a user with the username and type "local" | ||
| return types.UserSearch{ | ||
| Username: username, | ||
| Type: "local", | ||
| } | ||
| } | ||
|
|
||
| // If no user found, check LDAP | ||
| if auth.LDAP != nil { | ||
| log.Debug().Str("username", username).Msg("Checking LDAP for user") | ||
|
|
||
| userDN, err := auth.LDAP.Search(username) | ||
|
steveiliop56 marked this conversation as resolved.
|
||
| if err != nil { | ||
| log.Warn().Err(err).Str("username", username).Msg("Failed to find user in LDAP") | ||
| return types.UserSearch{} | ||
| } | ||
|
|
||
| // If user found in LDAP, return a user with the DN as username | ||
| return types.UserSearch{ | ||
| Username: userDN, | ||
| Type: "ldap", | ||
| } | ||
| } | ||
|
|
||
| return types.UserSearch{} | ||
| } | ||
|
|
||
| func (auth *Auth) VerifyUser(search types.UserSearch, password string) bool { | ||
| // Authenticate the user based on the type | ||
| switch search.Type { | ||
| case "local": | ||
| // Get local user | ||
| user := auth.GetLocalUser(search.Username) | ||
|
|
||
| // Check if password is correct | ||
| return auth.CheckPassword(user, password) | ||
| case "ldap": | ||
| // If LDAP is configured, bind to the LDAP server with the user DN and password | ||
| if auth.LDAP != nil { | ||
| log.Debug().Str("username", search.Username).Msg("Binding to LDAP for user authentication") | ||
|
|
||
| // Bind to the LDAP server | ||
| err := auth.LDAP.Bind(search.Username, password) | ||
| if err != nil { | ||
| log.Warn().Err(err).Str("username", search.Username).Msg("Failed to bind to LDAP") | ||
| return false | ||
| } | ||
|
|
||
| // If bind is successful, rebind with the LDAP bind user | ||
| err = auth.LDAP.Bind(auth.LDAP.Config.BindDN, auth.LDAP.Config.BindPassword) | ||
| if err != nil { | ||
| log.Error().Err(err).Msg("Failed to rebind with service account after user authentication") | ||
| // Consider closing the connection or creating a new one | ||
| return false | ||
| } | ||
|
|
||
| log.Debug().Str("username", search.Username).Msg("LDAP authentication successful") | ||
|
|
||
| // Return true if the bind was successful | ||
| return true | ||
| } | ||
| default: | ||
| log.Warn().Str("type", search.Type).Msg("Unknown user type for authentication") | ||
| return false | ||
| } | ||
|
|
||
| // If no user found or authentication failed, return false | ||
| log.Warn().Str("username", search.Username).Msg("User authentication failed") | ||
| return false | ||
| } | ||
|
Comment on lines
+74
to
+150
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Consider adding comprehensive unit tests for LDAP functionality The static analysis shows extensive test coverage gaps for the new LDAP functionality. Given the security-critical nature of authentication code and the complexity of the dual authentication flow, comprehensive unit tests are essential. Would you like me to help generate unit tests that cover:
🧰 Tools🪛 GitHub Check: codecov/patch[warning] 74-74: internal/auth/auth.go#L74 [warning] 76-86: internal/auth/auth.go#L76-L86 [warning] 89-96: internal/auth/auth.go#L89-L96 [warning] 99-102: internal/auth/auth.go#L99-L102 [warning] 105-105: internal/auth/auth.go#L105 [warning] 108-127: internal/auth/auth.go#L108-L127 [warning] 130-135: internal/auth/auth.go#L130-L135 [warning] 137-140: internal/auth/auth.go#L137-L140 [warning] 142-144: internal/auth/auth.go#L142-L144 [warning] 148-149: internal/auth/auth.go#L148-L149 🤖 Prompt for AI Agents |
||
|
|
||
| func (auth *Auth) GetLocalUser(username string) types.User { | ||
| // Loop through users and return the user if the username matches | ||
| log.Debug().Str("username", username).Msg("Searching for local user") | ||
|
|
||
| for _, user := range auth.Config.Users { | ||
| if user.Username == username { | ||
| return &user | ||
| return user | ||
| } | ||
| } | ||
| return nil | ||
|
|
||
| // If no user found, return an empty user | ||
| log.Warn().Str("username", username).Msg("Local user not found") | ||
| return types.User{} | ||
| } | ||
|
|
||
| func (auth *Auth) CheckPassword(user types.User, password string) bool { | ||
|
|
@@ -275,7 +361,7 @@ | |
|
|
||
| func (auth *Auth) UserAuthConfigured() bool { | ||
| // If there are users, return true | ||
| return len(auth.Config.Users) > 0 | ||
| return len(auth.Config.Users) > 0 || auth.LDAP != nil | ||
| } | ||
|
|
||
| func (auth *Auth) ResourceAllowed(c *gin.Context, context types.UserContext, labels types.Labels) bool { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.