mirror of
https://github.com/h44z/wg-portal.git
synced 2025-04-19 08:55:12 +00:00
commit
88278bf677
@ -141,15 +141,14 @@ The following configuration options are available:
|
|||||||
| LDAP_BASEDN | dn | ldap | DC=COMPANY,DC=LOCAL | The base DN for searching users. |
|
| LDAP_BASEDN | dn | ldap | DC=COMPANY,DC=LOCAL | The base DN for searching users. |
|
||||||
| LDAP_USER | user | ldap | company\\\\ldap_wireguard | The bind user. |
|
| LDAP_USER | user | ldap | company\\\\ldap_wireguard | The bind user. |
|
||||||
| LDAP_PASSWORD | pass | ldap | SuperSecret | The bind password. |
|
| LDAP_PASSWORD | pass | ldap | SuperSecret | The bind password. |
|
||||||
| LDAP_TYPE | typ | ldap | AD | Either AD or OpenLDAP. |
|
| LDAP_LOGIN_FILTER | loginFilter | ldap | (&(objectClass=organizationalPerson)(mail={{login_identifier}})(!userAccountControl:1.2.840.113556.1.4.803:=2)) | {{login_identifier}} will be replaced with the login email address. |
|
||||||
| LDAP_USER_CLASS | userClass | ldap | organizationalPerson | The user class that specifies the LDAP object category of users. |
|
| LDAP_SYNC_FILTER | syncFilter | ldap | (&(objectClass=organizationalPerson)(!userAccountControl:1.2.840.113556.1.4.803:=2)) | The filter string for the LDAP synchronization service. |
|
||||||
| LDAP_ADMIN_GROUP | adminGroup | ldap | CN=WireGuardAdmins,OU=_O_IT,DC=COMPANY,DC=LOCAL | Users in this group are marked as administrators. |
|
| LDAP_ADMIN_GROUP | adminGroup | ldap | CN=WireGuardAdmins,OU=_O_IT,DC=COMPANY,DC=LOCAL | Users in this group are marked as administrators. |
|
||||||
| LDAP_ATTR_EMAIL | attrEmail | ldap | mail | User email attribute. |
|
| LDAP_ATTR_EMAIL | attrEmail | ldap | mail | User email attribute. |
|
||||||
| LDAP_ATTR_FIRSTNAME | attrFirstname | ldap | givenName | User firstname attribute. |
|
| LDAP_ATTR_FIRSTNAME | attrFirstname | ldap | givenName | User firstname attribute. |
|
||||||
| LDAP_ATTR_LASTNAME | attrLastname | ldap | sn | User lastname attribute. |
|
| LDAP_ATTR_LASTNAME | attrLastname | ldap | sn | User lastname attribute. |
|
||||||
| LDAP_ATTR_PHONE | attrPhone | ldap | telephoneNumber | User phone number attribute. |
|
| LDAP_ATTR_PHONE | attrPhone | ldap | telephoneNumber | User phone number attribute. |
|
||||||
| LDAP_ATTR_GROUPS | attrGroups | ldap | memberOf | User groups attribute. |
|
| LDAP_ATTR_GROUPS | attrGroups | ldap | memberOf | User groups attribute. |
|
||||||
| LDAP_ATTR_DISABLED | attrDisabled | ldap | userAccountControl | User status attribute. This attribute is used to detect deactivated users. |
|
|
||||||
| LOG_LEVEL | | | debug | Specify log level, one of: trace, debug, info, off. |
|
| LOG_LEVEL | | | debug | Specify log level, one of: trace, debug, info, off. |
|
||||||
| LOG_JSON | | | false | Format log output as JSON. |
|
| LOG_JSON | | | false | Format log output as JSON. |
|
||||||
| LOG_COLOR | | | true | Colorize log output. |
|
| LOG_COLOR | | | true | Colorize log output. |
|
||||||
@ -174,7 +173,6 @@ ldap:
|
|||||||
user: wireguard@test.test
|
user: wireguard@test.test
|
||||||
pass: test
|
pass: test
|
||||||
adminGroup: CN=WireGuardAdmins,CN=Users,DC=test,DC=test
|
adminGroup: CN=WireGuardAdmins,CN=Users,DC=test,DC=test
|
||||||
typ: AD
|
|
||||||
database:
|
database:
|
||||||
typ: sqlite
|
typ: sqlite
|
||||||
database: data/wg_portal.db
|
database: data/wg_portal.db
|
||||||
|
@ -2,7 +2,6 @@ package ldap
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@ -69,13 +68,11 @@ func (provider Provider) Login(ctx *authentication.AuthContext) (string, error)
|
|||||||
|
|
||||||
// Search for the given username
|
// Search for the given username
|
||||||
attrs := []string{"dn", provider.config.EmailAttribute}
|
attrs := []string{"dn", provider.config.EmailAttribute}
|
||||||
if provider.config.DisabledAttribute != "" {
|
loginFilter := strings.Replace(provider.config.LoginFilter, "{{login_identifier}}", username, -1)
|
||||||
attrs = append(attrs, provider.config.DisabledAttribute)
|
|
||||||
}
|
|
||||||
searchRequest := ldap.NewSearchRequest(
|
searchRequest := ldap.NewSearchRequest(
|
||||||
provider.config.BaseDN,
|
provider.config.BaseDN,
|
||||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||||
fmt.Sprintf("(&(objectClass=%s)(%s=%s))", provider.config.UserClass, provider.config.EmailAttribute, username),
|
loginFilter,
|
||||||
attrs,
|
attrs,
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
@ -89,24 +86,8 @@ func (provider Provider) Login(ctx *authentication.AuthContext) (string, error)
|
|||||||
return "", errors.Errorf("invalid amount of ldap entries (%d)", len(sr.Entries))
|
return "", errors.Errorf("invalid amount of ldap entries (%d)", len(sr.Entries))
|
||||||
}
|
}
|
||||||
|
|
||||||
userDN := sr.Entries[0].DN
|
|
||||||
|
|
||||||
// Check if user is disabled, if so deny login
|
|
||||||
if provider.config.DisabledAttribute != "" {
|
|
||||||
uac := sr.Entries[0].GetAttributeValue(provider.config.DisabledAttribute)
|
|
||||||
switch provider.config.Type {
|
|
||||||
case ldapconfig.TypeActiveDirectory:
|
|
||||||
if ldapconfig.IsActiveDirectoryUserDisabled(uac) {
|
|
||||||
return "", errors.New("user is disabled")
|
|
||||||
}
|
|
||||||
case ldapconfig.TypeOpenLDAP:
|
|
||||||
if ldapconfig.IsOpenLdapUserDisabled(uac) {
|
|
||||||
return "", errors.New("user is disabled")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bind as the user to verify their password
|
// Bind as the user to verify their password
|
||||||
|
userDN := sr.Entries[0].DN
|
||||||
err = client.Bind(userDN, password)
|
err = client.Bind(userDN, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.Wrapf(err, "invalid credentials")
|
return "", errors.Wrapf(err, "invalid credentials")
|
||||||
@ -136,13 +117,11 @@ func (provider Provider) GetUserModel(ctx *authentication.AuthContext) (*authent
|
|||||||
// Search for the given username
|
// Search for the given username
|
||||||
attrs := []string{"dn", provider.config.EmailAttribute, provider.config.FirstNameAttribute, provider.config.LastNameAttribute,
|
attrs := []string{"dn", provider.config.EmailAttribute, provider.config.FirstNameAttribute, provider.config.LastNameAttribute,
|
||||||
provider.config.PhoneAttribute, provider.config.GroupMemberAttribute}
|
provider.config.PhoneAttribute, provider.config.GroupMemberAttribute}
|
||||||
if provider.config.DisabledAttribute != "" {
|
loginFilter := strings.Replace(provider.config.LoginFilter, "{{login_identifier}}", username, -1)
|
||||||
attrs = append(attrs, provider.config.DisabledAttribute)
|
|
||||||
}
|
|
||||||
searchRequest := ldap.NewSearchRequest(
|
searchRequest := ldap.NewSearchRequest(
|
||||||
provider.config.BaseDN,
|
provider.config.BaseDN,
|
||||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||||
fmt.Sprintf("(&(objectClass=%s)(%s=%s))", provider.config.UserClass, provider.config.EmailAttribute, username),
|
loginFilter,
|
||||||
attrs,
|
attrs,
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
|
@ -15,14 +15,13 @@ type Config struct {
|
|||||||
BindUser string `yaml:"user" envconfig:"LDAP_USER"`
|
BindUser string `yaml:"user" envconfig:"LDAP_USER"`
|
||||||
BindPass string `yaml:"pass" envconfig:"LDAP_PASSWORD"`
|
BindPass string `yaml:"pass" envconfig:"LDAP_PASSWORD"`
|
||||||
|
|
||||||
Type Type `yaml:"typ" envconfig:"LDAP_TYPE"` // AD for active directory, OpenLDAP for OpenLDAP
|
|
||||||
UserClass string `yaml:"userClass" envconfig:"LDAP_USER_CLASS"`
|
|
||||||
EmailAttribute string `yaml:"attrEmail" envconfig:"LDAP_ATTR_EMAIL"`
|
EmailAttribute string `yaml:"attrEmail" envconfig:"LDAP_ATTR_EMAIL"`
|
||||||
FirstNameAttribute string `yaml:"attrFirstname" envconfig:"LDAP_ATTR_FIRSTNAME"`
|
FirstNameAttribute string `yaml:"attrFirstname" envconfig:"LDAP_ATTR_FIRSTNAME"`
|
||||||
LastNameAttribute string `yaml:"attrLastname" envconfig:"LDAP_ATTR_LASTNAME"`
|
LastNameAttribute string `yaml:"attrLastname" envconfig:"LDAP_ATTR_LASTNAME"`
|
||||||
PhoneAttribute string `yaml:"attrPhone" envconfig:"LDAP_ATTR_PHONE"`
|
PhoneAttribute string `yaml:"attrPhone" envconfig:"LDAP_ATTR_PHONE"`
|
||||||
GroupMemberAttribute string `yaml:"attrGroups" envconfig:"LDAP_ATTR_GROUPS"`
|
GroupMemberAttribute string `yaml:"attrGroups" envconfig:"LDAP_ATTR_GROUPS"`
|
||||||
DisabledAttribute string `yaml:"attrDisabled" envconfig:"LDAP_ATTR_DISABLED"`
|
|
||||||
|
|
||||||
|
LoginFilter string `yaml:"loginFilter" envconfig:"LDAP_LOGIN_FILTER"` // {{login_identifier}} gets replaced with the login email address
|
||||||
|
SyncFilter string `yaml:"syncFilter" envconfig:"LDAP_SYNC_FILTER"`
|
||||||
AdminLdapGroup string `yaml:"adminGroup" envconfig:"LDAP_ADMIN_GROUP"` // Members of this group receive admin rights in WG-Portal
|
AdminLdapGroup string `yaml:"adminGroup" envconfig:"LDAP_ADMIN_GROUP"` // Members of this group receive admin rights in WG-Portal
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,6 @@ package ldap
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/go-ldap/ldap/v3"
|
"github.com/go-ldap/ldap/v3"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@ -54,13 +52,10 @@ func FindAllUsers(cfg *Config) ([]RawLdapData, error) {
|
|||||||
// Search all users
|
// Search all users
|
||||||
attrs := []string{"dn", cfg.EmailAttribute, cfg.EmailAttribute, cfg.FirstNameAttribute, cfg.LastNameAttribute,
|
attrs := []string{"dn", cfg.EmailAttribute, cfg.EmailAttribute, cfg.FirstNameAttribute, cfg.LastNameAttribute,
|
||||||
cfg.PhoneAttribute, cfg.GroupMemberAttribute}
|
cfg.PhoneAttribute, cfg.GroupMemberAttribute}
|
||||||
if cfg.DisabledAttribute != "" {
|
|
||||||
attrs = append(attrs, cfg.DisabledAttribute)
|
|
||||||
}
|
|
||||||
searchRequest := ldap.NewSearchRequest(
|
searchRequest := ldap.NewSearchRequest(
|
||||||
cfg.BaseDN,
|
cfg.BaseDN,
|
||||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||||
fmt.Sprintf("(objectClass=%s)", cfg.UserClass), attrs, nil,
|
cfg.SyncFilter, attrs, nil,
|
||||||
)
|
)
|
||||||
|
|
||||||
sr, err := client.Search(searchRequest)
|
sr, err := client.Search(searchRequest)
|
||||||
@ -87,27 +82,3 @@ func FindAllUsers(cfg *Config) ([]RawLdapData, error) {
|
|||||||
|
|
||||||
return tmpData, nil
|
return tmpData, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsActiveDirectoryUserDisabled(userAccountControl string) bool {
|
|
||||||
if userAccountControl == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
uacInt, err := strconv.ParseInt(userAccountControl, 10, 32)
|
|
||||||
if err != nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if int32(uacInt)&0x2 != 0 {
|
|
||||||
return true // bit 2 set means account is disabled
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsOpenLdapUserDisabled(pwdAccountLockedTime string) bool {
|
|
||||||
if pwdAccountLockedTime != "" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
@ -97,15 +97,14 @@ func NewConfig() *Config {
|
|||||||
cfg.LDAP.StartTLS = true
|
cfg.LDAP.StartTLS = true
|
||||||
cfg.LDAP.BindUser = "company\\\\ldap_wireguard"
|
cfg.LDAP.BindUser = "company\\\\ldap_wireguard"
|
||||||
cfg.LDAP.BindPass = "SuperSecret"
|
cfg.LDAP.BindPass = "SuperSecret"
|
||||||
cfg.LDAP.Type = "AD"
|
|
||||||
cfg.LDAP.UserClass = "organizationalPerson"
|
|
||||||
cfg.LDAP.EmailAttribute = "mail"
|
cfg.LDAP.EmailAttribute = "mail"
|
||||||
cfg.LDAP.FirstNameAttribute = "givenName"
|
cfg.LDAP.FirstNameAttribute = "givenName"
|
||||||
cfg.LDAP.LastNameAttribute = "sn"
|
cfg.LDAP.LastNameAttribute = "sn"
|
||||||
cfg.LDAP.PhoneAttribute = "telephoneNumber"
|
cfg.LDAP.PhoneAttribute = "telephoneNumber"
|
||||||
cfg.LDAP.GroupMemberAttribute = "memberOf"
|
cfg.LDAP.GroupMemberAttribute = "memberOf"
|
||||||
cfg.LDAP.DisabledAttribute = "userAccountControl"
|
|
||||||
cfg.LDAP.AdminLdapGroup = "CN=WireGuardAdmins,OU=_O_IT,DC=COMPANY,DC=LOCAL"
|
cfg.LDAP.AdminLdapGroup = "CN=WireGuardAdmins,OU=_O_IT,DC=COMPANY,DC=LOCAL"
|
||||||
|
cfg.LDAP.LoginFilter = "(&(objectClass=organizationalPerson)(mail={{login_identifier}})(!userAccountControl:1.2.840.113556.1.4.803:=2))"
|
||||||
|
cfg.LDAP.SyncFilter = "(&(objectClass=organizationalPerson)(!userAccountControl:1.2.840.113556.1.4.803:=2))"
|
||||||
|
|
||||||
cfg.WG.DeviceNames = []string{"wg0"}
|
cfg.WG.DeviceNames = []string{"wg0"}
|
||||||
cfg.WG.DefaultDeviceName = "wg0"
|
cfg.WG.DefaultDeviceName = "wg0"
|
||||||
|
@ -4,6 +4,8 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/h44z/wg-portal/internal/authentication"
|
"github.com/h44z/wg-portal/internal/authentication"
|
||||||
"github.com/h44z/wg-portal/internal/users"
|
"github.com/h44z/wg-portal/internal/users"
|
||||||
@ -53,65 +55,15 @@ func (s *Server) PostLogin(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check user database for an matching entry
|
|
||||||
var loginProvider authentication.AuthProvider
|
|
||||||
email := ""
|
|
||||||
user := s.users.GetUser(username) // retrieve active candidate user from db
|
|
||||||
if user != nil { // existing user
|
|
||||||
loginProvider = s.auth.GetProvider(string(user.Source))
|
|
||||||
if loginProvider == nil {
|
|
||||||
s.GetHandleError(c, http.StatusInternalServerError, "login error", "login provider unavailable")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
authEmail, err := loginProvider.Login(&authentication.AuthContext{
|
|
||||||
Username: username,
|
|
||||||
Password: password,
|
|
||||||
})
|
|
||||||
if err == nil {
|
|
||||||
email = authEmail
|
|
||||||
}
|
|
||||||
} else { // possible new user
|
|
||||||
// Check all available auth backends
|
// Check all available auth backends
|
||||||
for _, provider := range s.auth.GetProvidersForType(authentication.AuthProviderTypePassword) {
|
user, err := s.checkAuthentication(username, password)
|
||||||
// try to log in to the given provider
|
|
||||||
authEmail, err := provider.Login(&authentication.AuthContext{
|
|
||||||
Username: username,
|
|
||||||
Password: password,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
email = authEmail
|
|
||||||
loginProvider = provider
|
|
||||||
|
|
||||||
// create new user in the database (or reactivate him)
|
|
||||||
userData, err := loginProvider.GetUserModel(&authentication.AuthContext{
|
|
||||||
Username: email,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.GetHandleError(c, http.StatusInternalServerError, "login error", err.Error())
|
s.GetHandleError(c, http.StatusInternalServerError, "login error", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := s.CreateUser(users.User{
|
|
||||||
Email: userData.Email,
|
|
||||||
Source: users.UserSource(loginProvider.GetName()),
|
|
||||||
IsAdmin: userData.IsAdmin,
|
|
||||||
Firstname: userData.Firstname,
|
|
||||||
Lastname: userData.Lastname,
|
|
||||||
Phone: userData.Phone,
|
|
||||||
}, s.wg.Cfg.GetDefaultDeviceName()); err != nil {
|
|
||||||
s.GetHandleError(c, http.StatusInternalServerError, "login error", "failed to update user data")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
user = s.users.GetUser(username)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if user is authenticated
|
// Check if user is authenticated
|
||||||
if email == "" || loginProvider == nil || user == nil {
|
if user == nil {
|
||||||
c.Redirect(http.StatusSeeOther, "/auth/login?err=authfail")
|
c.Redirect(http.StatusSeeOther, "/auth/login?err=authfail")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -152,3 +104,48 @@ func (s *Server) GetLogout(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
c.Redirect(http.StatusSeeOther, "/")
|
c.Redirect(http.StatusSeeOther, "/")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) checkAuthentication(username, password string) (*users.User, error) {
|
||||||
|
var user *users.User
|
||||||
|
|
||||||
|
// Check all available auth backends
|
||||||
|
for _, provider := range s.auth.GetProvidersForType(authentication.AuthProviderTypePassword) {
|
||||||
|
// try to log in to the given provider
|
||||||
|
authEmail, err := provider.Login(&authentication.AuthContext{
|
||||||
|
Username: username,
|
||||||
|
Password: password,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Login succeeded
|
||||||
|
user = s.users.GetUser(authEmail)
|
||||||
|
if user != nil {
|
||||||
|
break // user exists, nothing more to do...
|
||||||
|
}
|
||||||
|
|
||||||
|
// create new user in the database (or reactivate him)
|
||||||
|
userData, err := provider.GetUserModel(&authentication.AuthContext{
|
||||||
|
Username: username,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to get user model")
|
||||||
|
}
|
||||||
|
if err := s.CreateUser(users.User{
|
||||||
|
Email: userData.Email,
|
||||||
|
Source: users.UserSource(provider.GetName()),
|
||||||
|
IsAdmin: userData.IsAdmin,
|
||||||
|
Firstname: userData.Firstname,
|
||||||
|
Lastname: userData.Lastname,
|
||||||
|
Phone: userData.Phone,
|
||||||
|
}, s.wg.Cfg.GetDefaultDeviceName()); err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to update user data")
|
||||||
|
}
|
||||||
|
|
||||||
|
user = s.users.GetUser(authEmail)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
@ -32,40 +32,91 @@ func (s *Server) SyncLdapWithUserDatabase() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range ldapUsers {
|
// Update existing LDAP users
|
||||||
// prefilter
|
s.updateLdapUsers(ldapUsers)
|
||||||
if ldapUsers[i].Attributes[s.config.LDAP.EmailAttribute] == "" ||
|
|
||||||
ldapUsers[i].Attributes[s.config.LDAP.FirstNameAttribute] == "" ||
|
// Disable missing LDAP users
|
||||||
ldapUsers[i].Attributes[s.config.LDAP.LastNameAttribute] == "" {
|
s.disableMissingLdapUsers(ldapUsers)
|
||||||
|
}
|
||||||
|
logrus.Info("ldap user synchronization stopped")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Server) userChangedInLdap(user *users.User, ldapData *ldap.RawLdapData) bool {
|
||||||
|
if user.Firstname != ldapData.Attributes[s.config.LDAP.FirstNameAttribute] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if user.Lastname != ldapData.Attributes[s.config.LDAP.LastNameAttribute] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if user.Email != strings.ToLower(ldapData.Attributes[s.config.LDAP.EmailAttribute]) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if user.Phone != ldapData.Attributes[s.config.LDAP.PhoneAttribute] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.DeletedAt.Valid {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
ldapAdmin := false
|
||||||
|
for _, group := range ldapData.RawAttributes[s.config.LDAP.GroupMemberAttribute] {
|
||||||
|
if string(group) == s.config.LDAP.AdminLdapGroup {
|
||||||
|
ldapAdmin = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if user.IsAdmin != ldapAdmin {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) disableMissingLdapUsers(ldapUsers []ldap.RawLdapData) {
|
||||||
|
// Disable missing LDAP users
|
||||||
|
activeUsers := s.users.GetUsers()
|
||||||
|
for i := range activeUsers {
|
||||||
|
if activeUsers[i].Source != users.UserSourceLdap {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
existsInLDAP := false
|
||||||
|
for j := range ldapUsers {
|
||||||
|
if activeUsers[i].Email == ldapUsers[j].Attributes[s.config.LDAP.EmailAttribute] {
|
||||||
|
existsInLDAP = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if existsInLDAP {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// disable all peers for the given user
|
||||||
|
for _, peer := range s.peers.GetPeersByMail(activeUsers[i].Email) {
|
||||||
|
now := time.Now()
|
||||||
|
peer.DeactivatedAt = &now
|
||||||
|
if err := s.UpdatePeer(peer, now); err != nil {
|
||||||
|
logrus.Errorf("failed to update deactivated peer %s: %v", peer.PublicKey, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.users.DeleteUser(&activeUsers[i]); err != nil {
|
||||||
|
logrus.Errorf("failed to delete deactivated user %s in database: %v", activeUsers[i].Email, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) updateLdapUsers(ldapUsers []ldap.RawLdapData) {
|
||||||
|
for i := range ldapUsers {
|
||||||
user, err := s.users.GetOrCreateUserUnscoped(ldapUsers[i].Attributes[s.config.LDAP.EmailAttribute])
|
user, err := s.users.GetOrCreateUserUnscoped(ldapUsers[i].Attributes[s.config.LDAP.EmailAttribute])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Errorf("failed to get/create user %s in database: %v", ldapUsers[i].Attributes[s.config.LDAP.EmailAttribute], err)
|
logrus.Errorf("failed to get/create user %s in database: %v", ldapUsers[i].Attributes[s.config.LDAP.EmailAttribute], err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if user should be deactivated
|
// re-enable LDAP user if the user was disabled
|
||||||
ldapDeactivated := false
|
if user.DeletedAt.Valid {
|
||||||
switch s.config.LDAP.Type {
|
|
||||||
case ldap.TypeActiveDirectory:
|
|
||||||
ldapDeactivated = ldap.IsActiveDirectoryUserDisabled(ldapUsers[i].Attributes[s.config.LDAP.DisabledAttribute])
|
|
||||||
case ldap.TypeOpenLDAP:
|
|
||||||
ldapDeactivated = ldap.IsOpenLdapUserDisabled(ldapUsers[i].Attributes[s.config.LDAP.DisabledAttribute])
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if user has been disabled in ldap, update peers accordingly
|
|
||||||
if ldapDeactivated != user.DeletedAt.Valid {
|
|
||||||
if ldapDeactivated {
|
|
||||||
// disable all peers for the given user
|
|
||||||
for _, peer := range s.peers.GetPeersByMail(user.Email) {
|
|
||||||
now := time.Now()
|
|
||||||
peer.DeactivatedAt = &now
|
|
||||||
if err = s.UpdatePeer(peer, now); err != nil {
|
|
||||||
logrus.Errorf("failed to update deactivated peer %s: %v", peer.PublicKey, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// enable all peers for the given user
|
// enable all peers for the given user
|
||||||
for _, peer := range s.peers.GetPeersByMail(user.Email) {
|
for _, peer := range s.peers.GetPeersByMail(user.Email) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
@ -75,10 +126,9 @@ func (s *Server) SyncLdapWithUserDatabase() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Sync attributes from ldap
|
// Sync attributes from ldap
|
||||||
if s.UserChangedInLdap(user, &ldapUsers[i]) {
|
if s.userChangedInLdap(user, &ldapUsers[i]) {
|
||||||
user.Firstname = ldapUsers[i].Attributes[s.config.LDAP.FirstNameAttribute]
|
user.Firstname = ldapUsers[i].Attributes[s.config.LDAP.FirstNameAttribute]
|
||||||
user.Lastname = ldapUsers[i].Attributes[s.config.LDAP.LastNameAttribute]
|
user.Lastname = ldapUsers[i].Attributes[s.config.LDAP.LastNameAttribute]
|
||||||
user.Email = ldapUsers[i].Attributes[s.config.LDAP.EmailAttribute]
|
user.Email = ldapUsers[i].Attributes[s.config.LDAP.EmailAttribute]
|
||||||
@ -98,54 +148,6 @@ func (s *Server) SyncLdapWithUserDatabase() {
|
|||||||
logrus.Errorf("failed to update ldap user %s in database: %v", user.Email, err)
|
logrus.Errorf("failed to update ldap user %s in database: %v", user.Email, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if ldapDeactivated {
|
|
||||||
if err = s.users.DeleteUser(user); err != nil {
|
|
||||||
logrus.Errorf("failed to delete deactivated user %s in database: %v", user.Email, err)
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
logrus.Info("ldap user synchronization stopped")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s Server) UserChangedInLdap(user *users.User, ldapData *ldap.RawLdapData) bool {
|
|
||||||
if user.Firstname != ldapData.Attributes[s.config.LDAP.FirstNameAttribute] {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if user.Lastname != ldapData.Attributes[s.config.LDAP.LastNameAttribute] {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if user.Email != strings.ToLower(ldapData.Attributes[s.config.LDAP.EmailAttribute]) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if user.Phone != ldapData.Attributes[s.config.LDAP.PhoneAttribute] {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
ldapDeactivated := false
|
|
||||||
switch s.config.LDAP.Type {
|
|
||||||
case ldap.TypeActiveDirectory:
|
|
||||||
ldapDeactivated = ldap.IsActiveDirectoryUserDisabled(ldapData.Attributes[s.config.LDAP.DisabledAttribute])
|
|
||||||
case ldap.TypeOpenLDAP:
|
|
||||||
ldapDeactivated = ldap.IsOpenLdapUserDisabled(ldapData.Attributes[s.config.LDAP.DisabledAttribute])
|
|
||||||
}
|
|
||||||
if ldapDeactivated != user.DeletedAt.Valid {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
ldapAdmin := false
|
|
||||||
for _, group := range ldapData.RawAttributes[s.config.LDAP.GroupMemberAttribute] {
|
|
||||||
if string(group) == s.config.LDAP.AdminLdapGroup {
|
|
||||||
ldapAdmin = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if user.IsAdmin != ldapAdmin {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
@ -6,7 +6,6 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
wgportal "github.com/h44z/wg-portal"
|
wgportal "github.com/h44z/wg-portal"
|
||||||
"github.com/h44z/wg-portal/internal/authentication"
|
|
||||||
_ "github.com/h44z/wg-portal/internal/server/docs" // docs is generated by Swag CLI, you have to import it.
|
_ "github.com/h44z/wg-portal/internal/server/docs" // docs is generated by Swag CLI, you have to import it.
|
||||||
ginSwagger "github.com/swaggo/gin-swagger"
|
ginSwagger "github.com/swaggo/gin-swagger"
|
||||||
"github.com/swaggo/gin-swagger/swaggerFiles"
|
"github.com/swaggo/gin-swagger/swaggerFiles"
|
||||||
@ -162,28 +161,16 @@ func (s *Server) RequireApiAuthentication(scope string) gin.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check user database for an matching entry
|
// Check all available auth backends
|
||||||
var loginProvider authentication.AuthProvider
|
user, err := s.checkAuthentication(username, password)
|
||||||
user := s.users.GetUser(username) // retrieve active candidate user from db
|
if err != nil {
|
||||||
if user == nil || user.Email == "" {
|
|
||||||
c.Abort()
|
c.Abort()
|
||||||
c.JSON(http.StatusUnauthorized, ApiError{Message: "unauthorized"})
|
c.JSON(http.StatusInternalServerError, ApiError{Message: "login error"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
loginProvider = s.auth.GetProvider(string(user.Source))
|
// Check if user is authenticated
|
||||||
if loginProvider == nil {
|
if user == nil {
|
||||||
c.Abort()
|
|
||||||
c.JSON(http.StatusUnauthorized, ApiError{Message: "unauthorized"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
authEmail, err := loginProvider.Login(&authentication.AuthContext{
|
|
||||||
Username: username,
|
|
||||||
Password: password,
|
|
||||||
})
|
|
||||||
|
|
||||||
// Test if authentication succeeded
|
|
||||||
if err != nil || authEmail == "" {
|
|
||||||
c.Abort()
|
c.Abort()
|
||||||
c.JSON(http.StatusUnauthorized, ApiError{Message: "unauthorized"})
|
c.JSON(http.StatusUnauthorized, ApiError{Message: "unauthorized"})
|
||||||
return
|
return
|
||||||
|
Loading…
x
Reference in New Issue
Block a user