vikunja-api/vendor/github.com/jcmturner/gokrb5/v8/service/authenticator.go
2020-05-29 17:47:28 +00:00

118 lines
3.9 KiB
Go

package service
import (
"encoding/base64"
"fmt"
"strings"
"time"
goidentity "github.com/jcmturner/goidentity/v6"
"github.com/jcmturner/gokrb5/v8/client"
"github.com/jcmturner/gokrb5/v8/config"
"github.com/jcmturner/gokrb5/v8/credentials"
)
// NewKRB5BasicAuthenticator creates a new NewKRB5BasicAuthenticator
func NewKRB5BasicAuthenticator(headerVal string, krb5conf *config.Config, serviceSettings *Settings, clientSettings *client.Settings) KRB5BasicAuthenticator {
return KRB5BasicAuthenticator{
BasicHeaderValue: headerVal,
clientConfig: krb5conf,
serviceSettings: serviceSettings,
clientSettings: clientSettings,
}
}
// KRB5BasicAuthenticator implements gokrb5.com/jcmturner/goidentity.Authenticator interface.
// It takes username and password so can be used for basic authentication.
type KRB5BasicAuthenticator struct {
BasicHeaderValue string
serviceSettings *Settings
clientSettings *client.Settings
clientConfig *config.Config
realm string
username string
password string
}
// Authenticate and return the identity. The boolean indicates if the authentication was successful.
func (a KRB5BasicAuthenticator) Authenticate() (i goidentity.Identity, ok bool, err error) {
a.realm, a.username, a.password, err = parseBasicHeaderValue(a.BasicHeaderValue)
if err != nil {
err = fmt.Errorf("could not parse basic authentication header: %v", err)
return
}
cl := client.NewWithPassword(a.username, a.realm, a.password, a.clientConfig)
err = cl.Login()
if err != nil {
// Username and/or password could be wrong
err = fmt.Errorf("error with user credentials during login: %v", err)
return
}
tkt, _, err := cl.GetServiceTicket(a.serviceSettings.SName())
if err != nil {
err = fmt.Errorf("could not get service ticket: %v", err)
return
}
err = tkt.DecryptEncPart(a.serviceSettings.Keytab, a.serviceSettings.KeytabPrincipal())
if err != nil {
err = fmt.Errorf("could not decrypt service ticket: %v", err)
return
}
cl.Credentials.SetAuthTime(time.Now().UTC())
cl.Credentials.SetAuthenticated(true)
isPAC, pac, err := tkt.GetPACType(a.serviceSettings.Keytab, a.serviceSettings.KeytabPrincipal(), a.serviceSettings.Logger())
if isPAC && err != nil {
err = fmt.Errorf("error processing PAC: %v", err)
return
}
if isPAC {
// There is a valid PAC. Adding attributes to creds
cl.Credentials.SetADCredentials(credentials.ADCredentials{
GroupMembershipSIDs: pac.KerbValidationInfo.GetGroupMembershipSIDs(),
LogOnTime: pac.KerbValidationInfo.LogOnTime.Time(),
LogOffTime: pac.KerbValidationInfo.LogOffTime.Time(),
PasswordLastSet: pac.KerbValidationInfo.PasswordLastSet.Time(),
EffectiveName: pac.KerbValidationInfo.EffectiveName.Value,
FullName: pac.KerbValidationInfo.FullName.Value,
UserID: int(pac.KerbValidationInfo.UserID),
PrimaryGroupID: int(pac.KerbValidationInfo.PrimaryGroupID),
LogonServer: pac.KerbValidationInfo.LogonServer.Value,
LogonDomainName: pac.KerbValidationInfo.LogonDomainName.Value,
LogonDomainID: pac.KerbValidationInfo.LogonDomainID.String(),
})
}
ok = true
i = cl.Credentials
return
}
// Mechanism returns the authentication mechanism.
func (a KRB5BasicAuthenticator) Mechanism() string {
return "Kerberos Basic"
}
func parseBasicHeaderValue(s string) (domain, username, password string, err error) {
b, err := base64.StdEncoding.DecodeString(s)
if err != nil {
return
}
v := string(b)
vc := strings.SplitN(v, ":", 2)
password = vc[1]
// Domain and username can be specified in 2 formats:
// <Username> - no domain specified
// <Domain>\<Username>
// <Username>@<Domain>
if strings.Contains(vc[0], `\`) {
u := strings.SplitN(vc[0], `\`, 2)
domain = u[0]
username = u[1]
} else if strings.Contains(vc[0], `@`) {
u := strings.SplitN(vc[0], `@`, 2)
domain = u[1]
username = u[0]
} else {
username = vc[0]
}
return
}