1
0
Fork 0
mirror of https://github.com/Luzifer/nginx-sso.git synced 2024-10-18 07:34:22 +00:00

Move auth plugins to own modules

Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
Knut Ahlers 2019-04-21 19:55:52 +02:00
parent 282a95c2e9
commit 29beaa6fa3
Signed by: luzifer
GPG key ID: DC2729FDD34BE99E
6 changed files with 131 additions and 92 deletions

10
core.go
View file

@ -1,14 +1,24 @@
package main package main
import ( import (
"github.com/Luzifer/nginx-sso/plugins/auth/crowd"
"github.com/Luzifer/nginx-sso/plugins/auth/google" "github.com/Luzifer/nginx-sso/plugins/auth/google"
"github.com/Luzifer/nginx-sso/plugins/auth/ldap"
"github.com/Luzifer/nginx-sso/plugins/auth/simple"
"github.com/Luzifer/nginx-sso/plugins/auth/token"
auth_yubikey "github.com/Luzifer/nginx-sso/plugins/auth/yubikey"
"github.com/Luzifer/nginx-sso/plugins/mfa/duo" "github.com/Luzifer/nginx-sso/plugins/mfa/duo"
"github.com/Luzifer/nginx-sso/plugins/mfa/totp" "github.com/Luzifer/nginx-sso/plugins/mfa/totp"
mfa_yubikey "github.com/Luzifer/nginx-sso/plugins/mfa/yubikey" mfa_yubikey "github.com/Luzifer/nginx-sso/plugins/mfa/yubikey"
) )
func registerModules() { func registerModules() {
registerAuthenticator(crowd.New())
registerAuthenticator(ldap.New(cookieStore))
registerAuthenticator(google.New(cookieStore)) registerAuthenticator(google.New(cookieStore))
registerAuthenticator(simple.New(cookieStore))
registerAuthenticator(token.New())
registerAuthenticator(auth_yubikey.New(cookieStore))
registerMFAProvider(duo.New()) registerMFAProvider(duo.New())
registerMFAProvider(totp.New()) registerMFAProvider(totp.New())

View file

@ -1,4 +1,4 @@
package main package crowd
import ( import (
"net/http" "net/http"
@ -11,11 +11,7 @@ import (
"github.com/Luzifer/nginx-sso/plugins" "github.com/Luzifer/nginx-sso/plugins"
) )
func init() { type AuthCrowd struct {
registerAuthenticator(&authCrowd{})
}
type authCrowd struct {
URL string `yaml:"url"` URL string `yaml:"url"`
AppName string `yaml:"app_name"` AppName string `yaml:"app_name"`
AppPassword string `yaml:"app_pass"` AppPassword string `yaml:"app_pass"`
@ -23,18 +19,22 @@ type authCrowd struct {
crowd crowd.Crowd crowd crowd.Crowd
} }
func New() *AuthCrowd {
return &AuthCrowd{}
}
// AuthenticatorID needs to return an unique string to identify // AuthenticatorID needs to return an unique string to identify
// this special authenticator // this special authenticator
func (a authCrowd) AuthenticatorID() string { return "crowd" } func (a AuthCrowd) AuthenticatorID() string { return "crowd" }
// Configure loads the configuration for the Authenticator from the // Configure loads the configuration for the Authenticator from the
// global config.yaml file which is passed as a byte-slice. // global config.yaml file which is passed as a byte-slice.
// If no configuration for the Authenticator is supplied the function // If no configuration for the Authenticator is supplied the function
// needs to return the plugins.ErrProviderUnconfigured // needs to return the plugins.ErrProviderUnconfigured
func (a *authCrowd) Configure(yamlSource []byte) error { func (a *AuthCrowd) Configure(yamlSource []byte) error {
envelope := struct { envelope := struct {
Providers struct { Providers struct {
Crowd *authCrowd `yaml:"crowd"` Crowd *AuthCrowd `yaml:"crowd"`
} `yaml:"providers"` } `yaml:"providers"`
}{} }{}
@ -64,7 +64,7 @@ func (a *authCrowd) Configure(yamlSource []byte) error {
// a cookie, header or other methods // a cookie, header or other methods
// If no user was detected the plugins.ErrNoValidUserFound needs to be // If no user was detected the plugins.ErrNoValidUserFound needs to be
// returned // returned
func (a authCrowd) DetectUser(res http.ResponseWriter, r *http.Request) (string, []string, error) { func (a AuthCrowd) DetectUser(res http.ResponseWriter, r *http.Request) (string, []string, error) {
cc, err := a.crowd.GetCookieConfig() cc, err := a.crowd.GetCookieConfig()
if err != nil { if err != nil {
return "", nil, err return "", nil, err
@ -108,7 +108,7 @@ func (a authCrowd) DetectUser(res http.ResponseWriter, r *http.Request) (string,
// in order to use DetectUser for the next login. // in order to use DetectUser for the next login.
// If the user did not login correctly the plugins.ErrNoValidUserFound // If the user did not login correctly the plugins.ErrNoValidUserFound
// needs to be returned // needs to be returned
func (a authCrowd) Login(res http.ResponseWriter, r *http.Request) (string, []plugins.MFAConfig, error) { func (a AuthCrowd) Login(res http.ResponseWriter, r *http.Request) (string, []plugins.MFAConfig, error) {
username := r.FormValue(strings.Join([]string{a.AuthenticatorID(), "username"}, "-")) username := r.FormValue(strings.Join([]string{a.AuthenticatorID(), "username"}, "-"))
password := r.FormValue(strings.Join([]string{a.AuthenticatorID(), "password"}, "-")) password := r.FormValue(strings.Join([]string{a.AuthenticatorID(), "password"}, "-"))
@ -141,7 +141,7 @@ func (a authCrowd) Login(res http.ResponseWriter, r *http.Request) (string, []pl
// LoginFields needs to return the fields required for this login // LoginFields needs to return the fields required for this login
// method. If no login using this method is possible the function // method. If no login using this method is possible the function
// needs to return nil. // needs to return nil.
func (a authCrowd) LoginFields() (fields []plugins.LoginField) { func (a AuthCrowd) LoginFields() (fields []plugins.LoginField) {
return []plugins.LoginField{ return []plugins.LoginField{
{ {
Label: "Username", Label: "Username",
@ -160,7 +160,7 @@ func (a authCrowd) LoginFields() (fields []plugins.LoginField) {
// Logout is called when the user visits the logout endpoint and // Logout is called when the user visits the logout endpoint and
// needs to destroy any persistent stored cookies // needs to destroy any persistent stored cookies
func (a authCrowd) Logout(res http.ResponseWriter, r *http.Request) (err error) { func (a AuthCrowd) Logout(res http.ResponseWriter, r *http.Request) (err error) {
cc, err := a.crowd.GetCookieConfig() cc, err := a.crowd.GetCookieConfig()
if err != nil { if err != nil {
return err return err
@ -184,4 +184,4 @@ func (a authCrowd) Logout(res http.ResponseWriter, r *http.Request) (err error)
// configuration return true. If this is true the login interface // configuration return true. If this is true the login interface
// will display an additional field for this provider for the user // will display an additional field for this provider for the user
// to fill in their MFA token. // to fill in their MFA token.
func (a authCrowd) SupportsMFA() bool { return false } func (a AuthCrowd) SupportsMFA() bool { return false }

View file

@ -1,4 +1,4 @@
package main package ldap
import ( import (
"crypto/tls" "crypto/tls"
@ -10,6 +10,8 @@ import (
ldap "gopkg.in/ldap.v2" ldap "gopkg.in/ldap.v2"
yaml "gopkg.in/yaml.v2" yaml "gopkg.in/yaml.v2"
"github.com/gorilla/sessions"
"github.com/Luzifer/nginx-sso/plugins" "github.com/Luzifer/nginx-sso/plugins"
) )
@ -18,11 +20,7 @@ const (
authLDAPProtoLDAPs = "ldaps" authLDAPProtoLDAPs = "ldaps"
) )
func init() { type AuthLDAP struct {
registerAuthenticator(&authLDAP{})
}
type authLDAP struct {
EnableBasicAuth bool `yaml:"enable_basic_auth"` EnableBasicAuth bool `yaml:"enable_basic_auth"`
GroupMembershipFilter string `yaml:"group_membership_filter"` GroupMembershipFilter string `yaml:"group_membership_filter"`
GroupSearchBase string `yaml:"group_search_base"` GroupSearchBase string `yaml:"group_search_base"`
@ -37,20 +35,30 @@ type authLDAP struct {
ValidateHostname string `yaml:"validate_hostname"` ValidateHostname string `yaml:"validate_hostname"`
AllowInsecure bool `yaml:"allow_insecure"` AllowInsecure bool `yaml:"allow_insecure"`
} `yaml:"tls_config"` } `yaml:"tls_config"`
cookie plugins.CookieConfig
cookieStore *sessions.CookieStore
}
func New(cs *sessions.CookieStore) *AuthLDAP {
return &AuthLDAP{
cookieStore: cs,
}
} }
// AuthenticatorID needs to return an unique string to identify // AuthenticatorID needs to return an unique string to identify
// this special authenticator // this special authenticator
func (a authLDAP) AuthenticatorID() string { return "ldap" } func (a AuthLDAP) AuthenticatorID() string { return "ldap" }
// Configure loads the configuration for the Authenticator from the // Configure loads the configuration for the Authenticator from the
// global config.yaml file which is passed as a byte-slice. // global config.yaml file which is passed as a byte-slice.
// If no configuration for the Authenticator is supplied the function // If no configuration for the Authenticator is supplied the function
// needs to return the plugins.ErrProviderUnconfigured // needs to return the plugins.ErrProviderUnconfigured
func (a *authLDAP) Configure(yamlSource []byte) error { func (a *AuthLDAP) Configure(yamlSource []byte) error {
envelope := struct { envelope := struct {
Cookie plugins.CookieConfig `yaml:"cookie"`
Providers struct { Providers struct {
LDAP *authLDAP `yaml:"ldap"` LDAP *AuthLDAP `yaml:"ldap"`
} `yaml:"providers"` } `yaml:"providers"`
}{} }{}
@ -74,6 +82,8 @@ func (a *authLDAP) Configure(yamlSource []byte) error {
a.UsernameAttribute = envelope.Providers.LDAP.UsernameAttribute a.UsernameAttribute = envelope.Providers.LDAP.UsernameAttribute
a.TLSConfig = envelope.Providers.LDAP.TLSConfig a.TLSConfig = envelope.Providers.LDAP.TLSConfig
a.cookie = envelope.Cookie
// Set defaults // Set defaults
if a.UserSearchFilter == "" { if a.UserSearchFilter == "" {
a.UserSearchFilter = `(uid={0})` a.UserSearchFilter = `(uid={0})`
@ -100,7 +110,7 @@ func (a *authLDAP) Configure(yamlSource []byte) error {
// a cookie, header or other methods // a cookie, header or other methods
// If no user was detected the plugins.ErrNoValidUserFound needs to be // If no user was detected the plugins.ErrNoValidUserFound needs to be
// returned // returned
func (a authLDAP) DetectUser(res http.ResponseWriter, r *http.Request) (string, []string, error) { func (a AuthLDAP) DetectUser(res http.ResponseWriter, r *http.Request) (string, []string, error) {
var alias, user string var alias, user string
if a.EnableBasicAuth { if a.EnableBasicAuth {
@ -116,7 +126,7 @@ func (a authLDAP) DetectUser(res http.ResponseWriter, r *http.Request) (string,
} }
if user == "" { if user == "" {
sess, err := cookieStore.Get(r, strings.Join([]string{mainCfg.Cookie.Prefix, a.AuthenticatorID()}, "-")) sess, err := a.cookieStore.Get(r, strings.Join([]string{a.cookie.Prefix, a.AuthenticatorID()}, "-"))
if err != nil { if err != nil {
return "", nil, plugins.ErrNoValidUserFound return "", nil, plugins.ErrNoValidUserFound
} }
@ -132,7 +142,7 @@ func (a authLDAP) DetectUser(res http.ResponseWriter, r *http.Request) (string,
} }
// We had a cookie, lets renew it // We had a cookie, lets renew it
sess.Options = mainCfg.Cookie.GetSessionOpts() sess.Options = a.cookie.GetSessionOpts()
if err := sess.Save(r, res); err != nil { if err := sess.Save(r, res); err != nil {
return "", nil, err return "", nil, err
} }
@ -149,7 +159,7 @@ func (a authLDAP) DetectUser(res http.ResponseWriter, r *http.Request) (string,
// in order to use DetectUser for the next login. // in order to use DetectUser for the next login.
// If the user did not login correctly the plugins.ErrNoValidUserFound // If the user did not login correctly the plugins.ErrNoValidUserFound
// needs to be returned // needs to be returned
func (a authLDAP) Login(res http.ResponseWriter, r *http.Request) (string, []plugins.MFAConfig, error) { func (a AuthLDAP) Login(res http.ResponseWriter, r *http.Request) (string, []plugins.MFAConfig, error) {
username := r.FormValue(strings.Join([]string{a.AuthenticatorID(), "username"}, "-")) username := r.FormValue(strings.Join([]string{a.AuthenticatorID(), "username"}, "-"))
password := r.FormValue(strings.Join([]string{a.AuthenticatorID(), "password"}, "-")) password := r.FormValue(strings.Join([]string{a.AuthenticatorID(), "password"}, "-"))
@ -163,8 +173,8 @@ func (a authLDAP) Login(res http.ResponseWriter, r *http.Request) (string, []plu
return "", nil, err return "", nil, err
} }
sess, _ := cookieStore.Get(r, strings.Join([]string{mainCfg.Cookie.Prefix, a.AuthenticatorID()}, "-")) // #nosec G104 - On error empty session is returned sess, _ := a.cookieStore.Get(r, strings.Join([]string{a.cookie.Prefix, a.AuthenticatorID()}, "-")) // #nosec G104 - On error empty session is returned
sess.Options = mainCfg.Cookie.GetSessionOpts() sess.Options = a.cookie.GetSessionOpts()
sess.Values["user"] = userDN sess.Values["user"] = userDN
sess.Values["alias"] = alias sess.Values["alias"] = alias
return userDN, nil, sess.Save(r, res) return userDN, nil, sess.Save(r, res)
@ -173,7 +183,7 @@ func (a authLDAP) Login(res http.ResponseWriter, r *http.Request) (string, []plu
// LoginFields needs to return the fields required for this login // LoginFields needs to return the fields required for this login
// method. If no login using this method is possible the function // method. If no login using this method is possible the function
// needs to return nil. // needs to return nil.
func (a authLDAP) LoginFields() (fields []plugins.LoginField) { func (a AuthLDAP) LoginFields() (fields []plugins.LoginField) {
return []plugins.LoginField{ return []plugins.LoginField{
{ {
Label: "Username", Label: "Username",
@ -192,16 +202,16 @@ func (a authLDAP) LoginFields() (fields []plugins.LoginField) {
// Logout is called when the user visits the logout endpoint and // Logout is called when the user visits the logout endpoint and
// needs to destroy any persistent stored cookies // needs to destroy any persistent stored cookies
func (a authLDAP) Logout(res http.ResponseWriter, r *http.Request) (err error) { func (a AuthLDAP) Logout(res http.ResponseWriter, r *http.Request) (err error) {
sess, _ := cookieStore.Get(r, strings.Join([]string{mainCfg.Cookie.Prefix, a.AuthenticatorID()}, "-")) // #nosec G104 - On error empty session is returned sess, _ := a.cookieStore.Get(r, strings.Join([]string{a.cookie.Prefix, a.AuthenticatorID()}, "-")) // #nosec G104 - On error empty session is returned
sess.Options = mainCfg.Cookie.GetSessionOpts() sess.Options = a.cookie.GetSessionOpts()
sess.Options.MaxAge = -1 // Instant delete sess.Options.MaxAge = -1 // Instant delete
return sess.Save(r, res) return sess.Save(r, res)
} }
// checkLogin searches for the username using the specified UserSearchFilter // checkLogin searches for the username using the specified UserSearchFilter
// and returns the UserDN and an error (plugins.ErrNoValidUserFound / processing error) // and returns the UserDN and an error (plugins.ErrNoValidUserFound / processing error)
func (a authLDAP) checkLogin(username, password, aliasAttribute string) (string, string, error) { func (a AuthLDAP) checkLogin(username, password, aliasAttribute string) (string, string, error) {
l, err := a.dial() l, err := a.dial()
if err != nil { if err != nil {
return "", "", err return "", "", err
@ -242,7 +252,7 @@ func (a authLDAP) checkLogin(username, password, aliasAttribute string) (string,
return userDN, alias, nil return userDN, alias, nil
} }
func (a authLDAP) portFromScheme(scheme, override string) string { func (a AuthLDAP) portFromScheme(scheme, override string) string {
if override != "" { if override != "" {
return override return override
} }
@ -258,7 +268,7 @@ func (a authLDAP) portFromScheme(scheme, override string) string {
} }
// dial connects to the LDAP server and authenticates using manager_dn // dial connects to the LDAP server and authenticates using manager_dn
func (a authLDAP) dial() (*ldap.Conn, error) { func (a AuthLDAP) dial() (*ldap.Conn, error) {
u, err := url.Parse(a.Server) u, err := url.Parse(a.Server)
if err != nil { if err != nil {
return nil, err return nil, err
@ -305,7 +315,7 @@ func (a authLDAP) dial() (*ldap.Conn, error) {
} }
// getUserGroups searches for groups containing the user // getUserGroups searches for groups containing the user
func (a authLDAP) getUserGroups(userDN, alias string) ([]string, error) { func (a AuthLDAP) getUserGroups(userDN, alias string) ([]string, error) {
l, err := a.dial() l, err := a.dial()
if err != nil { if err != nil {
return nil, err return nil, err
@ -343,4 +353,4 @@ func (a authLDAP) getUserGroups(userDN, alias string) ([]string, error) {
// configuration return true. If this is true the login interface // configuration return true. If this is true the login interface
// will display an additional field for this provider for the user // will display an additional field for this provider for the user
// to fill in their MFA token. // to fill in their MFA token.
func (a authLDAP) SupportsMFA() bool { return false } // TODO: Implement func (a AuthLDAP) SupportsMFA() bool { return false } // TODO: Implement

View file

@ -1,4 +1,4 @@
package main package simple
import ( import (
"net/http" "net/http"
@ -7,33 +7,41 @@ import (
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
yaml "gopkg.in/yaml.v2" yaml "gopkg.in/yaml.v2"
"github.com/gorilla/sessions"
"github.com/Luzifer/go_helpers/str" "github.com/Luzifer/go_helpers/str"
"github.com/Luzifer/nginx-sso/plugins" "github.com/Luzifer/nginx-sso/plugins"
) )
func init() { type AuthSimple struct {
registerAuthenticator(&authSimple{})
}
type authSimple struct {
EnableBasicAuth bool `yaml:"enable_basic_auth"` EnableBasicAuth bool `yaml:"enable_basic_auth"`
Users map[string]string `yaml:"users"` Users map[string]string `yaml:"users"`
Groups map[string][]string `yaml:"groups"` Groups map[string][]string `yaml:"groups"`
MFA map[string][]plugins.MFAConfig `yaml:"mfa"` MFA map[string][]plugins.MFAConfig `yaml:"mfa"`
cookie plugins.CookieConfig
cookieStore *sessions.CookieStore
}
func New(cs *sessions.CookieStore) *AuthSimple {
return &AuthSimple{
cookieStore: cs,
}
} }
// AuthenticatorID needs to return an unique string to identify // AuthenticatorID needs to return an unique string to identify
// this special authenticator // this special authenticator
func (a authSimple) AuthenticatorID() string { return "simple" } func (a AuthSimple) AuthenticatorID() string { return "simple" }
// Configure loads the configuration for the Authenticator from the // Configure loads the configuration for the Authenticator from the
// global config.yaml file which is passed as a byte-slice. // global config.yaml file which is passed as a byte-slice.
// If no configuration for the Authenticator is supplied the function // If no configuration for the Authenticator is supplied the function
// needs to return the plugins.ErrProviderUnconfigured // needs to return the plugins.ErrProviderUnconfigured
func (a *authSimple) Configure(yamlSource []byte) error { func (a *AuthSimple) Configure(yamlSource []byte) error {
envelope := struct { envelope := struct {
Cookie plugins.CookieConfig `yaml:"cookie"`
Providers struct { Providers struct {
Simple *authSimple `yaml:"simple"` Simple *AuthSimple `yaml:"simple"`
} `yaml:"providers"` } `yaml:"providers"`
}{} }{}
@ -50,6 +58,8 @@ func (a *authSimple) Configure(yamlSource []byte) error {
a.Groups = envelope.Providers.Simple.Groups a.Groups = envelope.Providers.Simple.Groups
a.MFA = envelope.Providers.Simple.MFA a.MFA = envelope.Providers.Simple.MFA
a.cookie = envelope.Cookie
return nil return nil
} }
@ -57,7 +67,7 @@ func (a *authSimple) Configure(yamlSource []byte) error {
// a cookie, header or other methods // a cookie, header or other methods
// If no user was detected the plugins.ErrNoValidUserFound needs to be // If no user was detected the plugins.ErrNoValidUserFound needs to be
// returned // returned
func (a authSimple) DetectUser(res http.ResponseWriter, r *http.Request) (string, []string, error) { func (a AuthSimple) DetectUser(res http.ResponseWriter, r *http.Request) (string, []string, error) {
var user string var user string
if a.EnableBasicAuth { if a.EnableBasicAuth {
@ -76,7 +86,7 @@ func (a authSimple) DetectUser(res http.ResponseWriter, r *http.Request) (string
} }
if user == "" { if user == "" {
sess, err := cookieStore.Get(r, strings.Join([]string{mainCfg.Cookie.Prefix, a.AuthenticatorID()}, "-")) sess, err := a.cookieStore.Get(r, strings.Join([]string{a.cookie.Prefix, a.AuthenticatorID()}, "-"))
if err != nil { if err != nil {
return "", nil, plugins.ErrNoValidUserFound return "", nil, plugins.ErrNoValidUserFound
} }
@ -88,7 +98,7 @@ func (a authSimple) DetectUser(res http.ResponseWriter, r *http.Request) (string
} }
// We had a cookie, lets renew it // We had a cookie, lets renew it
sess.Options = mainCfg.Cookie.GetSessionOpts() sess.Options = a.cookie.GetSessionOpts()
if err := sess.Save(r, res); err != nil { if err := sess.Save(r, res); err != nil {
return "", nil, err return "", nil, err
} }
@ -110,7 +120,7 @@ func (a authSimple) DetectUser(res http.ResponseWriter, r *http.Request) (string
// in order to use DetectUser for the next login. // in order to use DetectUser for the next login.
// If the user did not login correctly the plugins.ErrNoValidUserFound // If the user did not login correctly the plugins.ErrNoValidUserFound
// needs to be returned // needs to be returned
func (a authSimple) Login(res http.ResponseWriter, r *http.Request) (string, []plugins.MFAConfig, error) { func (a AuthSimple) Login(res http.ResponseWriter, r *http.Request) (string, []plugins.MFAConfig, error) {
username := r.FormValue(strings.Join([]string{a.AuthenticatorID(), "username"}, "-")) username := r.FormValue(strings.Join([]string{a.AuthenticatorID(), "username"}, "-"))
password := r.FormValue(strings.Join([]string{a.AuthenticatorID(), "password"}, "-")) password := r.FormValue(strings.Join([]string{a.AuthenticatorID(), "password"}, "-"))
@ -122,8 +132,8 @@ func (a authSimple) Login(res http.ResponseWriter, r *http.Request) (string, []p
continue continue
} }
sess, _ := cookieStore.Get(r, strings.Join([]string{mainCfg.Cookie.Prefix, a.AuthenticatorID()}, "-")) // #nosec G104 - On error empty session is returned sess, _ := a.cookieStore.Get(r, strings.Join([]string{a.cookie.Prefix, a.AuthenticatorID()}, "-")) // #nosec G104 - On error empty session is returned
sess.Options = mainCfg.Cookie.GetSessionOpts() sess.Options = a.cookie.GetSessionOpts()
sess.Values["user"] = u sess.Values["user"] = u
return u, a.MFA[u], sess.Save(r, res) return u, a.MFA[u], sess.Save(r, res)
} }
@ -134,7 +144,7 @@ func (a authSimple) Login(res http.ResponseWriter, r *http.Request) (string, []p
// LoginFields needs to return the fields required for this login // LoginFields needs to return the fields required for this login
// method. If no login using this method is possible the function // method. If no login using this method is possible the function
// needs to return nil. // needs to return nil.
func (a authSimple) LoginFields() (fields []plugins.LoginField) { func (a AuthSimple) LoginFields() (fields []plugins.LoginField) {
return []plugins.LoginField{ return []plugins.LoginField{
{ {
Label: "Username", Label: "Username",
@ -153,9 +163,9 @@ func (a authSimple) LoginFields() (fields []plugins.LoginField) {
// Logout is called when the user visits the logout endpoint and // Logout is called when the user visits the logout endpoint and
// needs to destroy any persistent stored cookies // needs to destroy any persistent stored cookies
func (a authSimple) Logout(res http.ResponseWriter, r *http.Request) (err error) { func (a AuthSimple) Logout(res http.ResponseWriter, r *http.Request) (err error) {
sess, _ := cookieStore.Get(r, strings.Join([]string{mainCfg.Cookie.Prefix, a.AuthenticatorID()}, "-")) // #nosec G104 - On error empty session is returned sess, _ := a.cookieStore.Get(r, strings.Join([]string{a.cookie.Prefix, a.AuthenticatorID()}, "-")) // #nosec G104 - On error empty session is returned
sess.Options = mainCfg.Cookie.GetSessionOpts() sess.Options = a.cookie.GetSessionOpts()
sess.Options.MaxAge = -1 // Instant delete sess.Options.MaxAge = -1 // Instant delete
return sess.Save(r, res) return sess.Save(r, res)
} }
@ -165,4 +175,4 @@ func (a authSimple) Logout(res http.ResponseWriter, r *http.Request) (err error)
// configuration return true. If this is true the login interface // configuration return true. If this is true the login interface
// will display an additional field for this provider for the user // will display an additional field for this provider for the user
// to fill in their MFA token. // to fill in their MFA token.
func (a authSimple) SupportsMFA() bool { return true } func (a AuthSimple) SupportsMFA() bool { return true }

View file

@ -1,4 +1,4 @@
package main package token
import ( import (
"net/http" "net/http"
@ -10,27 +10,27 @@ import (
yaml "gopkg.in/yaml.v2" yaml "gopkg.in/yaml.v2"
) )
func init() { type AuthToken struct {
registerAuthenticator(&authToken{})
}
type authToken struct {
Tokens map[string]string `yaml:"tokens"` Tokens map[string]string `yaml:"tokens"`
Groups map[string][]string `yaml:"groups"` Groups map[string][]string `yaml:"groups"`
} }
func New() *AuthToken {
return &AuthToken{}
}
// AuthenticatorID needs to return an unique string to identify // AuthenticatorID needs to return an unique string to identify
// this special authenticator // this special authenticator
func (a authToken) AuthenticatorID() string { return "token" } func (a AuthToken) AuthenticatorID() string { return "token" }
// Configure loads the configuration for the Authenticator from the // Configure loads the configuration for the Authenticator from the
// global config.yaml file which is passed as a byte-slice. // global config.yaml file which is passed as a byte-slice.
// If no configuration for the Authenticator is supplied the function // If no configuration for the Authenticator is supplied the function
// needs to return the plugins.ErrProviderUnconfigured // needs to return the plugins.ErrProviderUnconfigured
func (a *authToken) Configure(yamlSource []byte) error { func (a *AuthToken) Configure(yamlSource []byte) error {
envelope := struct { envelope := struct {
Providers struct { Providers struct {
Token *authToken `yaml:"token"` Token *AuthToken `yaml:"token"`
} `yaml:"providers"` } `yaml:"providers"`
}{} }{}
@ -52,7 +52,7 @@ func (a *authToken) Configure(yamlSource []byte) error {
// a cookie, header or other methods // a cookie, header or other methods
// If no user was detected the plugins.ErrNoValidUserFound needs to be // If no user was detected the plugins.ErrNoValidUserFound needs to be
// returned // returned
func (a authToken) DetectUser(res http.ResponseWriter, r *http.Request) (string, []string, error) { func (a AuthToken) DetectUser(res http.ResponseWriter, r *http.Request) (string, []string, error) {
authHeader := r.Header.Get("Authorization") authHeader := r.Header.Get("Authorization")
if !strings.HasPrefix(authHeader, "Token ") { if !strings.HasPrefix(authHeader, "Token ") {
@ -93,22 +93,22 @@ func (a authToken) DetectUser(res http.ResponseWriter, r *http.Request) (string,
// in order to use DetectUser for the next login. // in order to use DetectUser for the next login.
// If the user did not login correctly the plugins.ErrNoValidUserFound // If the user did not login correctly the plugins.ErrNoValidUserFound
// needs to be returned // needs to be returned
func (a authToken) Login(res http.ResponseWriter, r *http.Request) (string, []plugins.MFAConfig, error) { func (a AuthToken) Login(res http.ResponseWriter, r *http.Request) (string, []plugins.MFAConfig, error) {
return "", nil, plugins.ErrNoValidUserFound return "", nil, plugins.ErrNoValidUserFound
} }
// LoginFields needs to return the fields required for this login // LoginFields needs to return the fields required for this login
// method. If no login using this method is possible the function // method. If no login using this method is possible the function
// needs to return nil. // needs to return nil.
func (a authToken) LoginFields() []plugins.LoginField { return nil } func (a AuthToken) LoginFields() []plugins.LoginField { return nil }
// Logout is called when the user visits the logout endpoint and // Logout is called when the user visits the logout endpoint and
// needs to destroy any persistent stored cookies // needs to destroy any persistent stored cookies
func (a authToken) Logout(res http.ResponseWriter, r *http.Request) error { return nil } func (a AuthToken) Logout(res http.ResponseWriter, r *http.Request) error { return nil }
// SupportsMFA returns the MFA detection capabilities of the login // SupportsMFA returns the MFA detection capabilities of the login
// provider. If the provider can provide mfaConfig objects from its // provider. If the provider can provide mfaConfig objects from its
// configuration return true. If this is true the login interface // configuration return true. If this is true the login interface
// will display an additional field for this provider for the user // will display an additional field for this provider for the user
// to fill in their MFA token. // to fill in their MFA token.
func (a authToken) SupportsMFA() bool { return false } func (a AuthToken) SupportsMFA() bool { return false }

View file

@ -1,39 +1,46 @@
package main package yubikey
import ( import (
"net/http" "net/http"
"strings" "strings"
"github.com/GeertJohan/yubigo" "github.com/GeertJohan/yubigo"
"github.com/gorilla/sessions"
yaml "gopkg.in/yaml.v2" yaml "gopkg.in/yaml.v2"
"github.com/Luzifer/go_helpers/str" "github.com/Luzifer/go_helpers/str"
"github.com/Luzifer/nginx-sso/plugins" "github.com/Luzifer/nginx-sso/plugins"
) )
func init() { type AuthYubikey struct {
registerAuthenticator(&authYubikey{})
}
type authYubikey struct {
ClientID string `yaml:"client_id"` ClientID string `yaml:"client_id"`
SecretKey string `yaml:"secret_key"` SecretKey string `yaml:"secret_key"`
Devices map[string]string `yaml:"devices"` Devices map[string]string `yaml:"devices"`
Groups map[string][]string `yaml:"groups"` Groups map[string][]string `yaml:"groups"`
cookie plugins.CookieConfig
cookieStore *sessions.CookieStore
}
func New(cs *sessions.CookieStore) *AuthYubikey {
return &AuthYubikey{
cookieStore: cs,
}
} }
// AuthenticatorID needs to return an unique string to identify // AuthenticatorID needs to return an unique string to identify
// this special authenticator // this special authenticator
func (a authYubikey) AuthenticatorID() string { return "yubikey" } func (a AuthYubikey) AuthenticatorID() string { return "yubikey" }
// Configure loads the configuration for the Authenticator from the // Configure loads the configuration for the Authenticator from the
// global config.yaml file which is passed as a byte-slice. // global config.yaml file which is passed as a byte-slice.
// If no configuration for the Authenticator is supplied the function // If no configuration for the Authenticator is supplied the function
// needs to return the plugins.ErrProviderUnconfigured // needs to return the plugins.ErrProviderUnconfigured
func (a *authYubikey) Configure(yamlSource []byte) error { func (a *AuthYubikey) Configure(yamlSource []byte) error {
envelope := struct { envelope := struct {
Cookie plugins.CookieConfig `yaml:"cookie"`
Providers struct { Providers struct {
Yubikey *authYubikey `yaml:"yubikey"` Yubikey *AuthYubikey `yaml:"yubikey"`
} `yaml:"providers"` } `yaml:"providers"`
}{} }{}
@ -50,6 +57,8 @@ func (a *authYubikey) Configure(yamlSource []byte) error {
a.Devices = envelope.Providers.Yubikey.Devices a.Devices = envelope.Providers.Yubikey.Devices
a.Groups = envelope.Providers.Yubikey.Groups a.Groups = envelope.Providers.Yubikey.Groups
a.cookie = envelope.Cookie
return nil return nil
} }
@ -57,8 +66,8 @@ func (a *authYubikey) Configure(yamlSource []byte) error {
// a cookie, header or other methods // a cookie, header or other methods
// If no user was detected the plugins.ErrNoValidUserFound needs to be // If no user was detected the plugins.ErrNoValidUserFound needs to be
// returned // returned
func (a authYubikey) DetectUser(res http.ResponseWriter, r *http.Request) (string, []string, error) { func (a AuthYubikey) DetectUser(res http.ResponseWriter, r *http.Request) (string, []string, error) {
sess, err := cookieStore.Get(r, strings.Join([]string{mainCfg.Cookie.Prefix, a.AuthenticatorID()}, "-")) sess, err := a.cookieStore.Get(r, strings.Join([]string{a.cookie.Prefix, a.AuthenticatorID()}, "-"))
if err != nil { if err != nil {
return "", nil, plugins.ErrNoValidUserFound return "", nil, plugins.ErrNoValidUserFound
} }
@ -69,7 +78,7 @@ func (a authYubikey) DetectUser(res http.ResponseWriter, r *http.Request) (strin
} }
// We had a cookie, lets renew it // We had a cookie, lets renew it
sess.Options = mainCfg.Cookie.GetSessionOpts() sess.Options = a.cookie.GetSessionOpts()
if err := sess.Save(r, res); err != nil { if err := sess.Save(r, res); err != nil {
return "", nil, err return "", nil, err
} }
@ -90,7 +99,7 @@ func (a authYubikey) DetectUser(res http.ResponseWriter, r *http.Request) (strin
// in order to use DetectUser for the next login. // in order to use DetectUser for the next login.
// If the user did not login correctly the plugins.ErrNoValidUserFound // If the user did not login correctly the plugins.ErrNoValidUserFound
// needs to be returned // needs to be returned
func (a authYubikey) Login(res http.ResponseWriter, r *http.Request) (string, []plugins.MFAConfig, error) { func (a AuthYubikey) Login(res http.ResponseWriter, r *http.Request) (string, []plugins.MFAConfig, error) {
keyInput := r.FormValue(strings.Join([]string{a.AuthenticatorID(), "key-input"}, "-")) keyInput := r.FormValue(strings.Join([]string{a.AuthenticatorID(), "key-input"}, "-"))
yubiAuth, err := yubigo.NewYubiAuth(a.ClientID, a.SecretKey) yubiAuth, err := yubigo.NewYubiAuth(a.ClientID, a.SecretKey)
@ -114,8 +123,8 @@ func (a authYubikey) Login(res http.ResponseWriter, r *http.Request) (string, []
return "", nil, plugins.ErrNoValidUserFound return "", nil, plugins.ErrNoValidUserFound
} }
sess, _ := cookieStore.Get(r, strings.Join([]string{mainCfg.Cookie.Prefix, a.AuthenticatorID()}, "-")) // #nosec G104 - On error empty session is returned sess, _ := a.cookieStore.Get(r, strings.Join([]string{a.cookie.Prefix, a.AuthenticatorID()}, "-")) // #nosec G104 - On error empty session is returned
sess.Options = mainCfg.Cookie.GetSessionOpts() sess.Options = a.cookie.GetSessionOpts()
sess.Values["user"] = user sess.Values["user"] = user
return user, nil, sess.Save(r, res) return user, nil, sess.Save(r, res)
} }
@ -123,7 +132,7 @@ func (a authYubikey) Login(res http.ResponseWriter, r *http.Request) (string, []
// LoginFields needs to return the fields required for this login // LoginFields needs to return the fields required for this login
// method. If no login using this method is possible the function // method. If no login using this method is possible the function
// needs to return nil. // needs to return nil.
func (a authYubikey) LoginFields() (fields []plugins.LoginField) { func (a AuthYubikey) LoginFields() (fields []plugins.LoginField) {
return []plugins.LoginField{ return []plugins.LoginField{
{ {
Label: "Yubikey One-Time-Password", Label: "Yubikey One-Time-Password",
@ -136,9 +145,9 @@ func (a authYubikey) LoginFields() (fields []plugins.LoginField) {
// Logout is called when the user visits the logout endpoint and // Logout is called when the user visits the logout endpoint and
// needs to destroy any persistent stored cookies // needs to destroy any persistent stored cookies
func (a authYubikey) Logout(res http.ResponseWriter, r *http.Request) (err error) { func (a AuthYubikey) Logout(res http.ResponseWriter, r *http.Request) (err error) {
sess, _ := cookieStore.Get(r, strings.Join([]string{mainCfg.Cookie.Prefix, a.AuthenticatorID()}, "-")) // #nosec G104 - On error empty session is returned sess, _ := a.cookieStore.Get(r, strings.Join([]string{a.cookie.Prefix, a.AuthenticatorID()}, "-")) // #nosec G104 - On error empty session is returned
sess.Options = mainCfg.Cookie.GetSessionOpts() sess.Options = a.cookie.GetSessionOpts()
sess.Options.MaxAge = -1 // Instant delete sess.Options.MaxAge = -1 // Instant delete
return sess.Save(r, res) return sess.Save(r, res)
} }
@ -148,4 +157,4 @@ func (a authYubikey) Logout(res http.ResponseWriter, r *http.Request) (err error
// configuration return true. If this is true the login interface // configuration return true. If this is true the login interface
// will display an additional field for this provider for the user // will display an additional field for this provider for the user
// to fill in their MFA token. // to fill in their MFA token.
func (a authYubikey) SupportsMFA() bool { return false } func (a AuthYubikey) SupportsMFA() bool { return false }