Commit d00e7f54 authored by ale's avatar ale

Support the case where usernames are already email addresses

In this scenario there is no need to set 'users_file', instead we
introduce the new boolean attribute 'email_usernames'.
parent f1c2d0bf
Pipeline #7584 passed with stages
in 1 minute and 3 seconds
......@@ -41,7 +41,8 @@ type serviceProvider struct {
type Config struct {
BaseURL string `yaml:"base_url"`
UsersFile string `yaml:"users_file"`
UsersFile string `yaml:"users_file"`
EmailUsernames bool `yaml:"email_usernames"`
// SAML X509 credentials.
CertificateFile string `yaml:"certificate_file"`
......@@ -134,25 +135,63 @@ type userInfo struct {
Email string `yaml:"email"`
}
type userBackend interface {
GetUser(string) (*userInfo, bool)
}
type userFileBackend struct {
config *Config
users map[string]userInfo
users map[string]*userInfo
}
func (b *userFileBackend) GetUser(username string) (*userInfo, bool) {
info, ok := b.users[username]
return info, ok
}
func newUserFileBackend(config *Config) (*userFileBackend, error) {
data, err := ioutil.ReadFile(config.UsersFile)
func newUserFileBackend(path string) (*userFileBackend, error) {
data, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
var userList []userInfo
var userList []*userInfo
if err := yaml.Unmarshal(data, &userList); err != nil {
return nil, err
}
users := make(map[string]userInfo)
users := make(map[string]*userInfo)
for _, u := range userList {
users[u.Name] = u
}
return &userFileBackend{
users: users,
}, nil
}
type identityUserBackend struct{}
func (b *identityUserBackend) GetUser(username string) (*userInfo, bool) {
return &userInfo{Name: username, Email: username}, true
}
type sessionProvider struct {
config *Config
users userBackend
}
func newSessionProvider(config *Config) (*sessionProvider, error) {
var users userBackend
var err error
switch {
case config.UsersFile != "":
users, err = newUserFileBackend(config.UsersFile)
if err != nil {
return nil, err
}
case config.EmailUsernames:
users = new(identityUserBackend)
default:
return nil, errors.New("neither users_file or email_usernames are specified")
}
return &sessionProvider{
config: config,
users: users,
}, nil
......@@ -173,7 +212,7 @@ func matchGroups(user, exp []string) bool {
return false
}
func (b *userFileBackend) GetSession(w http.ResponseWriter, r *http.Request, req *saml.IdpAuthnRequest) *saml.Session {
func (sp *sessionProvider) GetSession(w http.ResponseWriter, r *http.Request, req *saml.IdpAuthnRequest) *saml.Session {
// Check for authentication by verifying the SSO username. We
// also need to be able to retrieve user information from the
// backend, to match SSO. Group membership, if enabled in our
......@@ -185,12 +224,12 @@ func (b *userFileBackend) GetSession(w http.ResponseWriter, r *http.Request, req
return nil
}
if !matchGroups(httpsso.Groups(r), b.config.GetSSOGroups(req.ServiceProviderMetadata.ID)) {
if !matchGroups(httpsso.Groups(r), sp.config.GetSSOGroups(req.ServiceProviderMetadata.ID)) {
http.Error(w, "Forbidden (bad group)", http.StatusForbidden)
return nil
}
user, ok := b.users[username]
user, ok := sp.users.GetUser(username)
if !ok {
log.Printf("error: user %s is authenticated but unknown", username)
http.Error(w, "User not found", http.StatusInternalServerError)
......@@ -252,7 +291,7 @@ func NewSAMLIDP(config *Config) (http.Handler, error) {
svc += "/"
}
users, err := newUserFileBackend(config)
sp, err := newSessionProvider(config)
if err != nil {
return nil, err
}
......@@ -267,7 +306,7 @@ func NewSAMLIDP(config *Config) (http.Handler, error) {
MetadataURL: metadataURL,
SSOURL: ssoURL,
ServiceProviderProvider: config,
SessionProvider: users,
SessionProvider: sp,
}
h := idp.Handler()
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment