// Package credentials provides credentials management for Kerberos 5 authentication. package credentials import ( "time" "github.com/hashicorp/go-uuid" "gopkg.in/jcmturner/gokrb5.v7/iana/nametype" "gopkg.in/jcmturner/gokrb5.v7/keytab" "gopkg.in/jcmturner/gokrb5.v7/types" ) const ( // AttributeKeyADCredentials assigned number for AD credentials. AttributeKeyADCredentials = "gokrb5AttributeKeyADCredentials" ) // Credentials struct for a user. // Contains either a keytab, password or both. // Keytabs are used over passwords if both are defined. type Credentials struct { username string displayName string realm string cname types.PrincipalName keytab *keytab.Keytab password string attributes map[string]interface{} validUntil time.Time authenticated bool human bool authTime time.Time groupMembership map[string]bool sessionID string } // ADCredentials contains information obtained from the PAC. type ADCredentials struct { EffectiveName string FullName string UserID int PrimaryGroupID int LogOnTime time.Time LogOffTime time.Time PasswordLastSet time.Time GroupMembershipSIDs []string LogonDomainName string LogonDomainID string LogonServer string } // New creates a new Credentials instance. func New(username string, realm string) *Credentials { uid, err := uuid.GenerateUUID() if err != nil { uid = "00unique-sess-ions-uuid-unavailable0" } return &Credentials{ username: username, displayName: username, realm: realm, cname: types.NewPrincipalName(nametype.KRB_NT_PRINCIPAL, username), keytab: keytab.New(), attributes: make(map[string]interface{}), groupMembership: make(map[string]bool), sessionID: uid, human: true, } } // NewFromPrincipalName creates a new Credentials instance with the user details provides as a PrincipalName type. func NewFromPrincipalName(cname types.PrincipalName, realm string) *Credentials { uid, err := uuid.GenerateUUID() if err != nil { uid = "00unique-sess-ions-uuid-unavailable0" } return &Credentials{ username: cname.PrincipalNameString(), displayName: cname.PrincipalNameString(), realm: realm, cname: cname, keytab: keytab.New(), attributes: make(map[string]interface{}), groupMembership: make(map[string]bool), sessionID: uid, human: true, } } // WithKeytab sets the Keytab in the Credentials struct. func (c *Credentials) WithKeytab(kt *keytab.Keytab) *Credentials { c.keytab = kt c.password = "" return c } // Keytab returns the credential's Keytab. func (c *Credentials) Keytab() *keytab.Keytab { return c.keytab } // HasKeytab queries if the Credentials has a keytab defined. func (c *Credentials) HasKeytab() bool { if c.keytab != nil && len(c.keytab.Entries) > 0 { return true } return false } // WithPassword sets the password in the Credentials struct. func (c *Credentials) WithPassword(password string) *Credentials { c.password = password c.keytab = keytab.New() // clear any keytab return c } // Password returns the credential's password. func (c *Credentials) Password() string { return c.password } // HasPassword queries if the Credentials has a password defined. func (c *Credentials) HasPassword() bool { if c.password != "" { return true } return false } // SetValidUntil sets the expiry time of the credentials func (c *Credentials) SetValidUntil(t time.Time) { c.validUntil = t } // SetADCredentials adds ADCredentials attributes to the credentials func (c *Credentials) SetADCredentials(a ADCredentials) { c.SetAttribute(AttributeKeyADCredentials, a) if a.FullName != "" { c.SetDisplayName(a.FullName) } if a.EffectiveName != "" { c.SetUserName(a.EffectiveName) } for i := range a.GroupMembershipSIDs { c.AddAuthzAttribute(a.GroupMembershipSIDs[i]) } } // Methods to implement goidentity.Identity interface // UserName returns the credential's username. func (c *Credentials) UserName() string { return c.username } // SetUserName sets the username value on the credential. func (c *Credentials) SetUserName(s string) { c.username = s } // CName returns the credential's client principal name. func (c *Credentials) CName() types.PrincipalName { return c.cname } // SetCName sets the client principal name on the credential. func (c *Credentials) SetCName(pn types.PrincipalName) { c.cname = pn } // Domain returns the credential's domain. func (c *Credentials) Domain() string { return c.realm } // SetDomain sets the domain value on the credential. func (c *Credentials) SetDomain(s string) { c.realm = s } // Realm returns the credential's realm. Same as the domain. func (c *Credentials) Realm() string { return c.Domain() } // SetRealm sets the realm value on the credential. Same as the domain func (c *Credentials) SetRealm(s string) { c.SetDomain(s) } // DisplayName returns the credential's display name. func (c *Credentials) DisplayName() string { return c.displayName } // SetDisplayName sets the display name value on the credential. func (c *Credentials) SetDisplayName(s string) { c.displayName = s } // Human returns if the credential represents a human or not. func (c *Credentials) Human() bool { return c.human } // SetHuman sets the credential as human. func (c *Credentials) SetHuman(b bool) { c.human = b } // AuthTime returns the time the credential was authenticated. func (c *Credentials) AuthTime() time.Time { return c.authTime } // SetAuthTime sets the time the credential was authenticated. func (c *Credentials) SetAuthTime(t time.Time) { c.authTime = t } // AuthzAttributes returns the credentials authorizing attributes. func (c *Credentials) AuthzAttributes() []string { s := make([]string, len(c.groupMembership)) i := 0 for a := range c.groupMembership { s[i] = a i++ } return s } // Authenticated indicates if the credential has been successfully authenticated or not. func (c *Credentials) Authenticated() bool { return c.authenticated } // SetAuthenticated sets the credential as having been successfully authenticated. func (c *Credentials) SetAuthenticated(b bool) { c.authenticated = b } // AddAuthzAttribute adds an authorization attribute to the credential. func (c *Credentials) AddAuthzAttribute(a string) { c.groupMembership[a] = true } // RemoveAuthzAttribute removes an authorization attribute from the credential. func (c *Credentials) RemoveAuthzAttribute(a string) { if _, ok := c.groupMembership[a]; !ok { return } delete(c.groupMembership, a) } // EnableAuthzAttribute toggles an authorization attribute to an enabled state on the credential. func (c *Credentials) EnableAuthzAttribute(a string) { if enabled, ok := c.groupMembership[a]; ok && !enabled { c.groupMembership[a] = true } } // DisableAuthzAttribute toggles an authorization attribute to a disabled state on the credential. func (c *Credentials) DisableAuthzAttribute(a string) { if enabled, ok := c.groupMembership[a]; ok && enabled { c.groupMembership[a] = false } } // Authorized indicates if the credential has the specified authorizing attribute. func (c *Credentials) Authorized(a string) bool { if enabled, ok := c.groupMembership[a]; ok && enabled { return true } return false } // SessionID returns the credential's session ID. func (c *Credentials) SessionID() string { return c.sessionID } // Expired indicates if the credential has expired. func (c *Credentials) Expired() bool { if !c.validUntil.IsZero() && time.Now().UTC().After(c.validUntil) { return true } return false } // ValidUntil returns the credential's valid until date func (c *Credentials) ValidUntil() time.Time { return c.validUntil } // Attributes returns the Credentials' attributes map. func (c *Credentials) Attributes() map[string]interface{} { return c.attributes } // SetAttribute sets the value of an attribute. func (c *Credentials) SetAttribute(k string, v interface{}) { c.attributes[k] = v } // SetAttributes replaces the attributes map with the one provided. func (c *Credentials) SetAttributes(a map[string]interface{}) { c.attributes = a } // RemoveAttribute deletes an attribute from the attribute map that has the key provided. func (c *Credentials) RemoveAttribute(k string) { delete(c.attributes, k) }