github.com/jcmturner/gokrb5/v8@v8.4.4/credentials/credentials.go (about)

     1  // Package credentials provides credentials management for Kerberos 5 authentication.
     2  package credentials
     3  
     4  import (
     5  	"bytes"
     6  	"encoding/gob"
     7  	"encoding/json"
     8  	"time"
     9  
    10  	"github.com/hashicorp/go-uuid"
    11  	"github.com/jcmturner/gokrb5/v8/iana/nametype"
    12  	"github.com/jcmturner/gokrb5/v8/keytab"
    13  	"github.com/jcmturner/gokrb5/v8/types"
    14  )
    15  
    16  const (
    17  	// AttributeKeyADCredentials assigned number for AD credentials.
    18  	AttributeKeyADCredentials = "gokrb5AttributeKeyADCredentials"
    19  )
    20  
    21  // Credentials struct for a user.
    22  // Contains either a keytab, password or both.
    23  // Keytabs are used over passwords if both are defined.
    24  type Credentials struct {
    25  	username        string
    26  	displayName     string
    27  	realm           string
    28  	cname           types.PrincipalName
    29  	keytab          *keytab.Keytab
    30  	password        string
    31  	attributes      map[string]interface{}
    32  	validUntil      time.Time
    33  	authenticated   bool
    34  	human           bool
    35  	authTime        time.Time
    36  	groupMembership map[string]bool
    37  	sessionID       string
    38  }
    39  
    40  // marshalCredentials is used to enable marshaling and unmarshaling of credentials
    41  // without having exported fields on the Credentials struct
    42  type marshalCredentials struct {
    43  	Username        string
    44  	DisplayName     string
    45  	Realm           string
    46  	CName           types.PrincipalName `json:"-"`
    47  	Keytab          bool
    48  	Password        bool
    49  	Attributes      map[string]interface{} `json:"-"`
    50  	ValidUntil      time.Time
    51  	Authenticated   bool
    52  	Human           bool
    53  	AuthTime        time.Time
    54  	GroupMembership map[string]bool `json:"-"`
    55  	SessionID       string
    56  }
    57  
    58  // ADCredentials contains information obtained from the PAC.
    59  type ADCredentials struct {
    60  	EffectiveName       string
    61  	FullName            string
    62  	UserID              int
    63  	PrimaryGroupID      int
    64  	LogOnTime           time.Time
    65  	LogOffTime          time.Time
    66  	PasswordLastSet     time.Time
    67  	GroupMembershipSIDs []string
    68  	LogonDomainName     string
    69  	LogonDomainID       string
    70  	LogonServer         string
    71  }
    72  
    73  // New creates a new Credentials instance.
    74  func New(username string, realm string) *Credentials {
    75  	uid, err := uuid.GenerateUUID()
    76  	if err != nil {
    77  		uid = "00unique-sess-ions-uuid-unavailable0"
    78  	}
    79  	return &Credentials{
    80  		username:        username,
    81  		displayName:     username,
    82  		realm:           realm,
    83  		cname:           types.NewPrincipalName(nametype.KRB_NT_PRINCIPAL, username),
    84  		keytab:          keytab.New(),
    85  		attributes:      make(map[string]interface{}),
    86  		groupMembership: make(map[string]bool),
    87  		sessionID:       uid,
    88  		human:           true,
    89  	}
    90  }
    91  
    92  // NewFromPrincipalName creates a new Credentials instance with the user details provides as a PrincipalName type.
    93  func NewFromPrincipalName(cname types.PrincipalName, realm string) *Credentials {
    94  	c := New(cname.PrincipalNameString(), realm)
    95  	c.cname = cname
    96  	return c
    97  }
    98  
    99  // WithKeytab sets the Keytab in the Credentials struct.
   100  func (c *Credentials) WithKeytab(kt *keytab.Keytab) *Credentials {
   101  	c.keytab = kt
   102  	c.password = ""
   103  	return c
   104  }
   105  
   106  // Keytab returns the credential's Keytab.
   107  func (c *Credentials) Keytab() *keytab.Keytab {
   108  	return c.keytab
   109  }
   110  
   111  // HasKeytab queries if the Credentials has a keytab defined.
   112  func (c *Credentials) HasKeytab() bool {
   113  	if c.keytab != nil && len(c.keytab.Entries) > 0 {
   114  		return true
   115  	}
   116  	return false
   117  }
   118  
   119  // WithPassword sets the password in the Credentials struct.
   120  func (c *Credentials) WithPassword(password string) *Credentials {
   121  	c.password = password
   122  	c.keytab = keytab.New() // clear any keytab
   123  	return c
   124  }
   125  
   126  // Password returns the credential's password.
   127  func (c *Credentials) Password() string {
   128  	return c.password
   129  }
   130  
   131  // HasPassword queries if the Credentials has a password defined.
   132  func (c *Credentials) HasPassword() bool {
   133  	if c.password != "" {
   134  		return true
   135  	}
   136  	return false
   137  }
   138  
   139  // SetValidUntil sets the expiry time of the credentials
   140  func (c *Credentials) SetValidUntil(t time.Time) {
   141  	c.validUntil = t
   142  }
   143  
   144  // SetADCredentials adds ADCredentials attributes to the credentials
   145  func (c *Credentials) SetADCredentials(a ADCredentials) {
   146  	c.SetAttribute(AttributeKeyADCredentials, a)
   147  	if a.FullName != "" {
   148  		c.SetDisplayName(a.FullName)
   149  	}
   150  	if a.EffectiveName != "" {
   151  		c.SetUserName(a.EffectiveName)
   152  	}
   153  	for i := range a.GroupMembershipSIDs {
   154  		c.AddAuthzAttribute(a.GroupMembershipSIDs[i])
   155  	}
   156  }
   157  
   158  // GetADCredentials returns ADCredentials attributes sorted in the credential
   159  func (c *Credentials) GetADCredentials() ADCredentials {
   160  	if a, ok := c.attributes[AttributeKeyADCredentials].(ADCredentials); ok {
   161  		return a
   162  	}
   163  	return ADCredentials{}
   164  }
   165  
   166  // Methods to implement goidentity.Identity interface
   167  
   168  // UserName returns the credential's username.
   169  func (c *Credentials) UserName() string {
   170  	return c.username
   171  }
   172  
   173  // SetUserName sets the username value on the credential.
   174  func (c *Credentials) SetUserName(s string) {
   175  	c.username = s
   176  }
   177  
   178  // CName returns the credential's client principal name.
   179  func (c *Credentials) CName() types.PrincipalName {
   180  	return c.cname
   181  }
   182  
   183  // SetCName sets the client principal name on the credential.
   184  func (c *Credentials) SetCName(pn types.PrincipalName) {
   185  	c.cname = pn
   186  }
   187  
   188  // Domain returns the credential's domain.
   189  func (c *Credentials) Domain() string {
   190  	return c.realm
   191  }
   192  
   193  // SetDomain sets the domain value on the credential.
   194  func (c *Credentials) SetDomain(s string) {
   195  	c.realm = s
   196  }
   197  
   198  // Realm returns the credential's realm. Same as the domain.
   199  func (c *Credentials) Realm() string {
   200  	return c.Domain()
   201  }
   202  
   203  // SetRealm sets the realm value on the credential. Same as the domain
   204  func (c *Credentials) SetRealm(s string) {
   205  	c.SetDomain(s)
   206  }
   207  
   208  // DisplayName returns the credential's display name.
   209  func (c *Credentials) DisplayName() string {
   210  	return c.displayName
   211  }
   212  
   213  // SetDisplayName sets the display name value on the credential.
   214  func (c *Credentials) SetDisplayName(s string) {
   215  	c.displayName = s
   216  }
   217  
   218  // Human returns if the  credential represents a human or not.
   219  func (c *Credentials) Human() bool {
   220  	return c.human
   221  }
   222  
   223  // SetHuman sets the credential as human.
   224  func (c *Credentials) SetHuman(b bool) {
   225  	c.human = b
   226  }
   227  
   228  // AuthTime returns the time the credential was authenticated.
   229  func (c *Credentials) AuthTime() time.Time {
   230  	return c.authTime
   231  }
   232  
   233  // SetAuthTime sets the time the credential was authenticated.
   234  func (c *Credentials) SetAuthTime(t time.Time) {
   235  	c.authTime = t
   236  }
   237  
   238  // AuthzAttributes returns the credentials authorizing attributes.
   239  func (c *Credentials) AuthzAttributes() []string {
   240  	s := make([]string, len(c.groupMembership))
   241  	i := 0
   242  	for a := range c.groupMembership {
   243  		s[i] = a
   244  		i++
   245  	}
   246  	return s
   247  }
   248  
   249  // Authenticated indicates if the credential has been successfully authenticated or not.
   250  func (c *Credentials) Authenticated() bool {
   251  	return c.authenticated
   252  }
   253  
   254  // SetAuthenticated sets the credential as having been successfully authenticated.
   255  func (c *Credentials) SetAuthenticated(b bool) {
   256  	c.authenticated = b
   257  }
   258  
   259  // AddAuthzAttribute adds an authorization attribute to the credential.
   260  func (c *Credentials) AddAuthzAttribute(a string) {
   261  	c.groupMembership[a] = true
   262  }
   263  
   264  // RemoveAuthzAttribute removes an authorization attribute from the credential.
   265  func (c *Credentials) RemoveAuthzAttribute(a string) {
   266  	if _, ok := c.groupMembership[a]; !ok {
   267  		return
   268  	}
   269  	delete(c.groupMembership, a)
   270  }
   271  
   272  // EnableAuthzAttribute toggles an authorization attribute to an enabled state on the credential.
   273  func (c *Credentials) EnableAuthzAttribute(a string) {
   274  	if enabled, ok := c.groupMembership[a]; ok && !enabled {
   275  		c.groupMembership[a] = true
   276  	}
   277  }
   278  
   279  // DisableAuthzAttribute toggles an authorization attribute to a disabled state on the credential.
   280  func (c *Credentials) DisableAuthzAttribute(a string) {
   281  	if enabled, ok := c.groupMembership[a]; ok && enabled {
   282  		c.groupMembership[a] = false
   283  	}
   284  }
   285  
   286  // Authorized indicates if the credential has the specified authorizing attribute.
   287  func (c *Credentials) Authorized(a string) bool {
   288  	if enabled, ok := c.groupMembership[a]; ok && enabled {
   289  		return true
   290  	}
   291  	return false
   292  }
   293  
   294  // SessionID returns the credential's session ID.
   295  func (c *Credentials) SessionID() string {
   296  	return c.sessionID
   297  }
   298  
   299  // Expired indicates if the credential has expired.
   300  func (c *Credentials) Expired() bool {
   301  	if !c.validUntil.IsZero() && time.Now().UTC().After(c.validUntil) {
   302  		return true
   303  	}
   304  	return false
   305  }
   306  
   307  // ValidUntil returns the credential's valid until date
   308  func (c *Credentials) ValidUntil() time.Time {
   309  	return c.validUntil
   310  }
   311  
   312  // Attributes returns the Credentials' attributes map.
   313  func (c *Credentials) Attributes() map[string]interface{} {
   314  	return c.attributes
   315  }
   316  
   317  // SetAttribute sets the value of an attribute.
   318  func (c *Credentials) SetAttribute(k string, v interface{}) {
   319  	c.attributes[k] = v
   320  }
   321  
   322  // SetAttributes replaces the attributes map with the one provided.
   323  func (c *Credentials) SetAttributes(a map[string]interface{}) {
   324  	c.attributes = a
   325  }
   326  
   327  // RemoveAttribute deletes an attribute from the attribute map that has the key provided.
   328  func (c *Credentials) RemoveAttribute(k string) {
   329  	delete(c.attributes, k)
   330  }
   331  
   332  // Marshal the Credentials into a byte slice
   333  func (c *Credentials) Marshal() ([]byte, error) {
   334  	gob.Register(map[string]interface{}{})
   335  	gob.Register(ADCredentials{})
   336  	buf := new(bytes.Buffer)
   337  	enc := gob.NewEncoder(buf)
   338  	mc := marshalCredentials{
   339  		Username:        c.username,
   340  		DisplayName:     c.displayName,
   341  		Realm:           c.realm,
   342  		CName:           c.cname,
   343  		Keytab:          c.HasKeytab(),
   344  		Password:        c.HasPassword(),
   345  		Attributes:      c.attributes,
   346  		ValidUntil:      c.validUntil,
   347  		Authenticated:   c.authenticated,
   348  		Human:           c.human,
   349  		AuthTime:        c.authTime,
   350  		GroupMembership: c.groupMembership,
   351  		SessionID:       c.sessionID,
   352  	}
   353  	err := enc.Encode(&mc)
   354  	if err != nil {
   355  		return []byte{}, err
   356  	}
   357  	return buf.Bytes(), nil
   358  }
   359  
   360  // Unmarshal a byte slice into Credentials
   361  func (c *Credentials) Unmarshal(b []byte) error {
   362  	gob.Register(map[string]interface{}{})
   363  	gob.Register(ADCredentials{})
   364  	mc := new(marshalCredentials)
   365  	buf := bytes.NewBuffer(b)
   366  	dec := gob.NewDecoder(buf)
   367  	err := dec.Decode(mc)
   368  	if err != nil {
   369  		return err
   370  	}
   371  	c.username = mc.Username
   372  	c.displayName = mc.DisplayName
   373  	c.realm = mc.Realm
   374  	c.cname = mc.CName
   375  	c.attributes = mc.Attributes
   376  	c.validUntil = mc.ValidUntil
   377  	c.authenticated = mc.Authenticated
   378  	c.human = mc.Human
   379  	c.authTime = mc.AuthTime
   380  	c.groupMembership = mc.GroupMembership
   381  	c.sessionID = mc.SessionID
   382  	return nil
   383  }
   384  
   385  // JSON return details of the Credentials in a JSON format.
   386  func (c *Credentials) JSON() (string, error) {
   387  	mc := marshalCredentials{
   388  		Username:      c.username,
   389  		DisplayName:   c.displayName,
   390  		Realm:         c.realm,
   391  		CName:         c.cname,
   392  		Keytab:        c.HasKeytab(),
   393  		Password:      c.HasPassword(),
   394  		ValidUntil:    c.validUntil,
   395  		Authenticated: c.authenticated,
   396  		Human:         c.human,
   397  		AuthTime:      c.authTime,
   398  		SessionID:     c.sessionID,
   399  	}
   400  	b, err := json.MarshalIndent(mc, "", "  ")
   401  	if err != nil {
   402  		return "", err
   403  	}
   404  	return string(b), nil
   405  }