github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/nomad/structs/acl.go (about)

     1  package structs
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"regexp"
     9  	"strconv"
    10  	"time"
    11  
    12  	"github.com/hashicorp/go-multierror"
    13  	"github.com/hashicorp/go-set"
    14  	"github.com/hashicorp/nomad/helper/pointer"
    15  	"github.com/hashicorp/nomad/helper/uuid"
    16  	"golang.org/x/crypto/blake2b"
    17  	"golang.org/x/exp/slices"
    18  )
    19  
    20  const (
    21  	// ACLUpsertPoliciesRPCMethod is the RPC method for batch creating or
    22  	// modifying ACL policies.
    23  	//
    24  	// Args: ACLPolicyUpsertRequest
    25  	// Reply: GenericResponse
    26  	ACLUpsertPoliciesRPCMethod = "ACL.UpsertPolicies"
    27  
    28  	// ACLUpsertTokensRPCMethod is the RPC method for batch creating or
    29  	// modifying ACL tokens.
    30  	//
    31  	// Args: ACLTokenUpsertRequest
    32  	// Reply: ACLTokenUpsertResponse
    33  	ACLUpsertTokensRPCMethod = "ACL.UpsertTokens"
    34  
    35  	// ACLDeleteTokensRPCMethod is the RPC method for batch deleting ACL
    36  	// tokens.
    37  	//
    38  	// Args: ACLTokenDeleteRequest
    39  	// Reply: GenericResponse
    40  	ACLDeleteTokensRPCMethod = "ACL.DeleteTokens"
    41  
    42  	// ACLUpsertRolesRPCMethod is the RPC method for batch creating or
    43  	// modifying ACL roles.
    44  	//
    45  	// Args: ACLRolesUpsertRequest
    46  	// Reply: ACLRolesUpsertResponse
    47  	ACLUpsertRolesRPCMethod = "ACL.UpsertRoles"
    48  
    49  	// ACLDeleteRolesByIDRPCMethod the RPC method for batch deleting ACL
    50  	// roles by their ID.
    51  	//
    52  	// Args: ACLRolesDeleteByIDRequest
    53  	// Reply: ACLRolesDeleteByIDResponse
    54  	ACLDeleteRolesByIDRPCMethod = "ACL.DeleteRolesByID"
    55  
    56  	// ACLListRolesRPCMethod is the RPC method for listing ACL roles.
    57  	//
    58  	// Args: ACLRolesListRequest
    59  	// Reply: ACLRolesListResponse
    60  	ACLListRolesRPCMethod = "ACL.ListRoles"
    61  
    62  	// ACLGetRolesByIDRPCMethod is the RPC method for detailing a number of ACL
    63  	// roles using their ID. This is an internal only RPC endpoint and used by
    64  	// the ACL Role replication process.
    65  	//
    66  	// Args: ACLRolesByIDRequest
    67  	// Reply: ACLRolesByIDResponse
    68  	ACLGetRolesByIDRPCMethod = "ACL.GetRolesByID"
    69  
    70  	// ACLGetRoleByIDRPCMethod is the RPC method for detailing an individual
    71  	// ACL role using its ID.
    72  	//
    73  	// Args: ACLRoleByIDRequest
    74  	// Reply: ACLRoleByIDResponse
    75  	ACLGetRoleByIDRPCMethod = "ACL.GetRoleByID"
    76  
    77  	// ACLGetRoleByNameRPCMethod is the RPC method for detailing an individual
    78  	// ACL role using its name.
    79  	//
    80  	// Args: ACLRoleByNameRequest
    81  	// Reply: ACLRoleByNameResponse
    82  	ACLGetRoleByNameRPCMethod = "ACL.GetRoleByName"
    83  
    84  	// ACLUpsertAuthMethodsRPCMethod is the RPC method for batch creating or
    85  	// modifying auth methods.
    86  	//
    87  	// Args: ACLAuthMethodsUpsertRequest
    88  	// Reply: ACLAuthMethodUpsertResponse
    89  	ACLUpsertAuthMethodsRPCMethod = "ACL.UpsertAuthMethods"
    90  
    91  	// ACLDeleteAuthMethodsRPCMethod is the RPC method for batch deleting auth
    92  	// methods.
    93  	//
    94  	// Args: ACLAuthMethodDeleteRequest
    95  	// Reply: ACLAuthMethodDeleteResponse
    96  	ACLDeleteAuthMethodsRPCMethod = "ACL.DeleteAuthMethods"
    97  
    98  	// ACLListAuthMethodsRPCMethod is the RPC method for listing auth methods.
    99  	//
   100  	// Args: ACLAuthMethodListRequest
   101  	// Reply: ACLAuthMethodListResponse
   102  	ACLListAuthMethodsRPCMethod = "ACL.ListAuthMethods"
   103  
   104  	// ACLGetAuthMethodRPCMethod is the RPC method for detailing an individual
   105  	// auth method using its name.
   106  	//
   107  	// Args: ACLAuthMethodGetRequest
   108  	// Reply: ACLAuthMethodGetResponse
   109  	ACLGetAuthMethodRPCMethod = "ACL.GetAuthMethod"
   110  
   111  	// ACLGetAuthMethodsRPCMethod is the RPC method for getting multiple auth
   112  	// methods using their names.
   113  	//
   114  	// Args: ACLAuthMethodsGetRequest
   115  	// Reply: ACLAuthMethodsGetResponse
   116  	ACLGetAuthMethodsRPCMethod = "ACL.GetAuthMethods"
   117  
   118  	// ACLUpsertBindingRulesRPCMethod is the RPC method for batch creating or
   119  	// modifying binding rules.
   120  	//
   121  	// Args: ACLBindingRulesUpsertRequest
   122  	// Reply: ACLBindingRulesUpsertResponse
   123  	ACLUpsertBindingRulesRPCMethod = "ACL.UpsertBindingRules"
   124  
   125  	// ACLDeleteBindingRulesRPCMethod is the RPC method for batch deleting
   126  	// binding rules.
   127  	//
   128  	// Args: ACLBindingRulesDeleteRequest
   129  	// Reply: ACLBindingRulesDeleteResponse
   130  	ACLDeleteBindingRulesRPCMethod = "ACL.DeleteBindingRules"
   131  
   132  	// ACLListBindingRulesRPCMethod is the RPC method listing binding rules.
   133  	//
   134  	// Args: ACLBindingRulesListRequest
   135  	// Reply: ACLBindingRulesListResponse
   136  	ACLListBindingRulesRPCMethod = "ACL.ListBindingRules"
   137  
   138  	// ACLGetBindingRulesRPCMethod is the RPC method for getting multiple
   139  	// binding rules using their IDs.
   140  	//
   141  	// Args: ACLBindingRulesRequest
   142  	// Reply: ACLBindingRulesResponse
   143  	ACLGetBindingRulesRPCMethod = "ACL.GetBindingRules"
   144  
   145  	// ACLGetBindingRuleRPCMethod is the RPC method for detailing an individual
   146  	// binding rule using its ID.
   147  	//
   148  	// Args: ACLBindingRuleRequest
   149  	// Reply: ACLBindingRuleResponse
   150  	ACLGetBindingRuleRPCMethod = "ACL.GetBindingRule"
   151  )
   152  
   153  const (
   154  	// ACLMaxExpiredBatchSize is the maximum number of expired ACL tokens that
   155  	// will be garbage collected in a single trigger. This number helps limit
   156  	// the replication pressure due to expired token deletion. If there are a
   157  	// large number of expired tokens pending garbage collection, this value is
   158  	// a potential limiting factor.
   159  	ACLMaxExpiredBatchSize = 4096
   160  
   161  	// maxACLRoleDescriptionLength limits an ACL roles description length.
   162  	maxACLRoleDescriptionLength = 256
   163  
   164  	// maxACLBindingRuleDescriptionLength limits an ACL binding rules
   165  	// description length and should be used to validate the object.
   166  	maxACLBindingRuleDescriptionLength = 256
   167  )
   168  
   169  var (
   170  	// validACLRoleName is used to validate an ACL role name.
   171  	validACLRoleName = regexp.MustCompile("^[a-zA-Z0-9-]{1,128}$")
   172  
   173  	// validACLAuthMethodName is used to validate an ACL auth method name.
   174  	validACLAuthMethod = regexp.MustCompile("^[a-zA-Z0-9-]{1,128}$")
   175  )
   176  
   177  // ACLTokenRoleLink is used to link an ACL token to an ACL role. The ACL token
   178  // can therefore inherit all the ACL policy permissions that the ACL role
   179  // contains.
   180  type ACLTokenRoleLink struct {
   181  
   182  	// ID is the ACLRole.ID UUID. This field is immutable and represents the
   183  	// absolute truth for the link.
   184  	ID string
   185  
   186  	// Name is the human friendly identifier for the ACL role and is a
   187  	// convenience field for operators. This field is always resolved to the
   188  	// ID and discarded before the token is stored in state. This is because
   189  	// operators can change the name of an ACL role.
   190  	Name string
   191  }
   192  
   193  // Canonicalize performs basic canonicalization on the ACL token object. It is
   194  // important for callers to understand certain fields such as AccessorID are
   195  // set if it is empty, so copies should be taken if needed before calling this
   196  // function.
   197  func (a *ACLToken) Canonicalize() {
   198  
   199  	// If the accessor ID is empty, it means this is creation of a new token,
   200  	// therefore we need to generate base information.
   201  	if a.AccessorID == "" {
   202  
   203  		a.AccessorID = uuid.Generate()
   204  		a.SecretID = uuid.Generate()
   205  		a.CreateTime = time.Now().UTC()
   206  
   207  		// If the user has not set the expiration time, but has provided a TTL, we
   208  		// calculate and populate the former filed.
   209  		if a.ExpirationTime == nil && a.ExpirationTTL != 0 {
   210  			a.ExpirationTime = pointer.Of(a.CreateTime.Add(a.ExpirationTTL))
   211  		}
   212  	}
   213  }
   214  
   215  // Validate is used to check a token for reasonableness
   216  func (a *ACLToken) Validate(minTTL, maxTTL time.Duration, existing *ACLToken) error {
   217  	var mErr multierror.Error
   218  
   219  	// The human friendly name of an ACL token cannot exceed 256 characters.
   220  	if len(a.Name) > maxTokenNameLength {
   221  		mErr.Errors = append(mErr.Errors, errors.New("token name too long"))
   222  	}
   223  
   224  	// The type of an ACL token must be set. An ACL token of type client must
   225  	// have associated policies or roles, whereas a management token cannot be
   226  	// associated with policies.
   227  	switch a.Type {
   228  	case ACLClientToken:
   229  		if len(a.Policies) == 0 && len(a.Roles) == 0 {
   230  			mErr.Errors = append(mErr.Errors, errors.New("client token missing policies or roles"))
   231  		}
   232  	case ACLManagementToken:
   233  		if len(a.Policies) != 0 || len(a.Roles) != 0 {
   234  			mErr.Errors = append(mErr.Errors, errors.New("management token cannot be associated with policies or roles"))
   235  		}
   236  	default:
   237  		mErr.Errors = append(mErr.Errors, errors.New("token type must be client or management"))
   238  	}
   239  
   240  	// There are different validation rules depending on whether the ACL token
   241  	// is being created or updated.
   242  	switch existing {
   243  	case nil:
   244  		if a.ExpirationTTL < 0 {
   245  			mErr.Errors = append(mErr.Errors,
   246  				fmt.Errorf("token expiration TTL '%s' should not be negative", a.ExpirationTTL))
   247  		}
   248  
   249  		if a.ExpirationTime != nil && !a.ExpirationTime.IsZero() {
   250  
   251  			if a.CreateTime.After(*a.ExpirationTime) {
   252  				mErr.Errors = append(mErr.Errors, errors.New("expiration time cannot be before create time"))
   253  			}
   254  
   255  			// Create a time duration which details the time-til-expiry, so we can
   256  			// check this against the regions max and min values.
   257  			expiresIn := a.ExpirationTime.Sub(a.CreateTime)
   258  			if expiresIn > maxTTL {
   259  				mErr.Errors = append(mErr.Errors,
   260  					fmt.Errorf("expiration time cannot be more than %s in the future (was %s)",
   261  						maxTTL, expiresIn))
   262  
   263  			} else if expiresIn < minTTL {
   264  				mErr.Errors = append(mErr.Errors,
   265  					fmt.Errorf("expiration time cannot be less than %s in the future (was %s)",
   266  						minTTL, expiresIn))
   267  			}
   268  		}
   269  	default:
   270  		if existing.Global != a.Global {
   271  			mErr.Errors = append(mErr.Errors, errors.New("cannot toggle global mode"))
   272  		}
   273  		if existing.ExpirationTTL != a.ExpirationTTL {
   274  			mErr.Errors = append(mErr.Errors, errors.New("cannot update expiration TTL"))
   275  		}
   276  		if existing.ExpirationTime != a.ExpirationTime {
   277  			mErr.Errors = append(mErr.Errors, errors.New("cannot update expiration time"))
   278  		}
   279  	}
   280  
   281  	return mErr.ErrorOrNil()
   282  }
   283  
   284  // HasExpirationTime checks whether the ACL token has an expiration time value
   285  // set.
   286  func (a *ACLToken) HasExpirationTime() bool {
   287  	if a == nil || a.ExpirationTime == nil {
   288  		return false
   289  	}
   290  	return !a.ExpirationTime.IsZero()
   291  }
   292  
   293  // IsExpired compares the ACLToken.ExpirationTime against the passed t to
   294  // identify whether the token is considered expired. The function can be called
   295  // without checking whether the ACL token has an expiry time.
   296  func (a *ACLToken) IsExpired(t time.Time) bool {
   297  
   298  	// Check the token has an expiration time before potentially modifying the
   299  	// supplied time. This allows us to avoid extra work, if it isn't needed.
   300  	if !a.HasExpirationTime() {
   301  		return false
   302  	}
   303  
   304  	// Check and ensure the time location is set to UTC. This is vital for
   305  	// consistency with multi-region global tokens.
   306  	if t.Location() != time.UTC {
   307  		t = t.UTC()
   308  	}
   309  
   310  	return a.ExpirationTime.Before(t) || t.IsZero()
   311  }
   312  
   313  // HasRoles checks if a given set of role IDs are assigned to the ACL token. It
   314  // does not account for management tokens, therefore it is the responsibility
   315  // of the caller to perform this check, if required.
   316  func (a *ACLToken) HasRoles(roleIDs []string) bool {
   317  
   318  	// Generate a set of role IDs that the token is assigned.
   319  	roleSet := set.FromFunc(a.Roles, func(roleLink *ACLTokenRoleLink) string { return roleLink.ID })
   320  
   321  	// Iterate the role IDs within the request and check whether these are
   322  	// present within the token assignment.
   323  	for _, roleID := range roleIDs {
   324  		if !roleSet.Contains(roleID) {
   325  			return false
   326  		}
   327  	}
   328  	return true
   329  }
   330  
   331  // ACLRole is an abstraction for the ACL system which allows the grouping of
   332  // ACL policies into a single object. ACL tokens can be created and linked to
   333  // a role; the token then inherits all the permissions granted by the policies.
   334  type ACLRole struct {
   335  
   336  	// ID is an internally generated UUID for this role and is controlled by
   337  	// Nomad.
   338  	ID string
   339  
   340  	// Name is unique across the entire set of federated clusters and is
   341  	// supplied by the operator on role creation. The name can be modified by
   342  	// updating the role and including the Nomad generated ID. This update will
   343  	// not affect tokens created and linked to this role. This is a required
   344  	// field.
   345  	Name string
   346  
   347  	// Description is a human-readable, operator set description that can
   348  	// provide additional context about the role. This is an operational field.
   349  	Description string
   350  
   351  	// Policies is an array of ACL policy links. Although currently policies
   352  	// can only be linked using their name, in the future we will want to add
   353  	// IDs also and thus allow operators to specify either a name, an ID, or
   354  	// both.
   355  	Policies []*ACLRolePolicyLink
   356  
   357  	// Hash is the hashed value of the role and is generated using all fields
   358  	// above this point.
   359  	Hash []byte
   360  
   361  	CreateIndex uint64
   362  	ModifyIndex uint64
   363  }
   364  
   365  // ACLRolePolicyLink is used to link a policy to an ACL role. We use a struct
   366  // rather than a list of strings as in the future we will want to add IDs to
   367  // policies and then link via these.
   368  type ACLRolePolicyLink struct {
   369  
   370  	// Name is the ACLPolicy.Name value which will be linked to the ACL role.
   371  	Name string
   372  }
   373  
   374  // SetHash is used to compute and set the hash of the ACL role. This should be
   375  // called every and each time a user specified field on the role is changed
   376  // before updating the Nomad state store.
   377  func (a *ACLRole) SetHash() []byte {
   378  
   379  	// Initialize a 256bit Blake2 hash (32 bytes).
   380  	hash, err := blake2b.New256(nil)
   381  	if err != nil {
   382  		panic(err)
   383  	}
   384  
   385  	// Write all the user set fields.
   386  	_, _ = hash.Write([]byte(a.Name))
   387  	_, _ = hash.Write([]byte(a.Description))
   388  
   389  	for _, policyLink := range a.Policies {
   390  		_, _ = hash.Write([]byte(policyLink.Name))
   391  	}
   392  
   393  	// Finalize the hash.
   394  	hashVal := hash.Sum(nil)
   395  
   396  	// Set and return the hash.
   397  	a.Hash = hashVal
   398  	return hashVal
   399  }
   400  
   401  // Validate ensure the ACL role contains valid information which meets Nomad's
   402  // internal requirements. This does not include any state calls, such as
   403  // ensuring the linked policies exist.
   404  func (a *ACLRole) Validate() error {
   405  
   406  	var mErr multierror.Error
   407  
   408  	if !validACLRoleName.MatchString(a.Name) {
   409  		mErr.Errors = append(mErr.Errors, fmt.Errorf("invalid name '%s'", a.Name))
   410  	}
   411  
   412  	if len(a.Description) > maxACLRoleDescriptionLength {
   413  		mErr.Errors = append(mErr.Errors, fmt.Errorf("description longer than %d", maxACLRoleDescriptionLength))
   414  	}
   415  
   416  	if len(a.Policies) < 1 {
   417  		mErr.Errors = append(mErr.Errors, errors.New("at least one policy should be specified"))
   418  	}
   419  
   420  	return mErr.ErrorOrNil()
   421  }
   422  
   423  // Canonicalize performs basic canonicalization on the ACL role object. It is
   424  // important for callers to understand certain fields such as ID are set if it
   425  // is empty, so copies should be taken if needed before calling this function.
   426  func (a *ACLRole) Canonicalize() {
   427  	if a.ID == "" {
   428  		a.ID = uuid.Generate()
   429  	}
   430  }
   431  
   432  // Equal performs an equality check on the two service registrations. It
   433  // handles nil objects.
   434  func (a *ACLRole) Equal(o *ACLRole) bool {
   435  	if a == nil || o == nil {
   436  		return a == o
   437  	}
   438  	if len(a.Hash) == 0 {
   439  		a.SetHash()
   440  	}
   441  	if len(o.Hash) == 0 {
   442  		o.SetHash()
   443  	}
   444  	return bytes.Equal(a.Hash, o.Hash)
   445  }
   446  
   447  // Copy creates a deep copy of the ACL role. This copy can then be safely
   448  // modified. It handles nil objects.
   449  func (a *ACLRole) Copy() *ACLRole {
   450  	if a == nil {
   451  		return nil
   452  	}
   453  
   454  	c := new(ACLRole)
   455  	*c = *a
   456  
   457  	c.Policies = slices.Clone(a.Policies)
   458  	c.Hash = slices.Clone(a.Hash)
   459  
   460  	return c
   461  }
   462  
   463  // Stub converts the ACLRole object into a ACLRoleListStub object.
   464  func (a *ACLRole) Stub() *ACLRoleListStub {
   465  	return &ACLRoleListStub{
   466  		ID:          a.ID,
   467  		Name:        a.Name,
   468  		Description: a.Description,
   469  		Policies:    a.Policies,
   470  		Hash:        a.Hash,
   471  		CreateIndex: a.CreateIndex,
   472  		ModifyIndex: a.ModifyIndex,
   473  	}
   474  }
   475  
   476  // ACLRoleListStub is the stub object returned when performing a listing of ACL
   477  // roles. While it might not currently be different to the full response
   478  // object, it allows us to future-proof the RPC in the event the ACLRole object
   479  // grows over time.
   480  type ACLRoleListStub struct {
   481  
   482  	// ID is an internally generated UUID for this role and is controlled by
   483  	// Nomad.
   484  	ID string
   485  
   486  	// Name is unique across the entire set of federated clusters and is
   487  	// supplied by the operator on role creation. The name can be modified by
   488  	// updating the role and including the Nomad generated ID. This update will
   489  	// not affect tokens created and linked to this role. This is a required
   490  	// field.
   491  	Name string
   492  
   493  	// Description is a human-readable, operator set description that can
   494  	// provide additional context about the role. This is an operational field.
   495  	Description string
   496  
   497  	// Policies is an array of ACL policy links. Although currently policies
   498  	// can only be linked using their name, in the future we will want to add
   499  	// IDs also and thus allow operators to specify either a name, an ID, or
   500  	// both.
   501  	Policies []*ACLRolePolicyLink
   502  
   503  	// Hash is the hashed value of the role and is generated using all fields
   504  	// above this point.
   505  	Hash []byte
   506  
   507  	CreateIndex uint64
   508  	ModifyIndex uint64
   509  }
   510  
   511  // ACLRolesUpsertRequest is the request object used to upsert one or more ACL
   512  // roles.
   513  type ACLRolesUpsertRequest struct {
   514  	ACLRoles []*ACLRole
   515  
   516  	// AllowMissingPolicies skips the ACL Role policy link verification and is
   517  	// used by the replication process. The replication cannot ensure policies
   518  	// are present before ACL Roles are replicated.
   519  	AllowMissingPolicies bool
   520  
   521  	WriteRequest
   522  }
   523  
   524  // ACLRolesUpsertResponse is the response object when one or more ACL roles
   525  // have been successfully upserted into state.
   526  type ACLRolesUpsertResponse struct {
   527  	ACLRoles []*ACLRole
   528  	WriteMeta
   529  }
   530  
   531  // ACLRolesDeleteByIDRequest is the request object to delete one or more ACL
   532  // roles using the role ID.
   533  type ACLRolesDeleteByIDRequest struct {
   534  	ACLRoleIDs []string
   535  	WriteRequest
   536  }
   537  
   538  // ACLRolesDeleteByIDResponse is the response object when performing a deletion
   539  // of one or more ACL roles using the role ID.
   540  type ACLRolesDeleteByIDResponse struct {
   541  	WriteMeta
   542  }
   543  
   544  // ACLRolesListRequest is the request object when performing ACL role listings.
   545  type ACLRolesListRequest struct {
   546  	QueryOptions
   547  }
   548  
   549  // ACLRolesListResponse is the response object when performing ACL role
   550  // listings.
   551  type ACLRolesListResponse struct {
   552  	ACLRoles []*ACLRoleListStub
   553  	QueryMeta
   554  }
   555  
   556  // ACLRolesByIDRequest is the request object when performing a lookup of
   557  // multiple roles by the ID.
   558  type ACLRolesByIDRequest struct {
   559  	ACLRoleIDs []string
   560  	QueryOptions
   561  }
   562  
   563  // ACLRolesByIDResponse is the response object when performing a lookup of
   564  // multiple roles by their IDs.
   565  type ACLRolesByIDResponse struct {
   566  	ACLRoles map[string]*ACLRole
   567  	QueryMeta
   568  }
   569  
   570  // ACLRoleByIDRequest is the request object to perform a lookup of an ACL
   571  // role using a specific ID.
   572  type ACLRoleByIDRequest struct {
   573  	RoleID string
   574  	QueryOptions
   575  }
   576  
   577  // ACLRoleByIDResponse is the response object when performing a lookup of an
   578  // ACL role matching a specific ID.
   579  type ACLRoleByIDResponse struct {
   580  	ACLRole *ACLRole
   581  	QueryMeta
   582  }
   583  
   584  // ACLRoleByNameRequest is the request object to perform a lookup of an ACL
   585  // role using a specific name.
   586  type ACLRoleByNameRequest struct {
   587  	RoleName string
   588  	QueryOptions
   589  }
   590  
   591  // ACLRoleByNameResponse is the response object when performing a lookup of an
   592  // ACL role matching a specific name.
   593  type ACLRoleByNameResponse struct {
   594  	ACLRole *ACLRole
   595  	QueryMeta
   596  }
   597  
   598  // ACLAuthMethod is used to capture the properties of an authentication method
   599  // used for single sing-on
   600  type ACLAuthMethod struct {
   601  	Name          string
   602  	Type          string
   603  	TokenLocality string // is the token valid locally or globally?
   604  	MaxTokenTTL   time.Duration
   605  	Default       bool
   606  	Config        *ACLAuthMethodConfig
   607  
   608  	Hash []byte
   609  
   610  	CreateTime  time.Time
   611  	ModifyTime  time.Time
   612  	CreateIndex uint64
   613  	ModifyIndex uint64
   614  }
   615  
   616  // SetHash is used to compute and set the hash of the ACL auth method. This
   617  // should be called every and each time a user specified field on the method is
   618  // changed before updating the Nomad state store.
   619  func (a *ACLAuthMethod) SetHash() []byte {
   620  
   621  	// Initialize a 256bit Blake2 hash (32 bytes).
   622  	hash, err := blake2b.New256(nil)
   623  	if err != nil {
   624  		panic(err)
   625  	}
   626  
   627  	_, _ = hash.Write([]byte(a.Name))
   628  	_, _ = hash.Write([]byte(a.Type))
   629  	_, _ = hash.Write([]byte(a.TokenLocality))
   630  	_, _ = hash.Write([]byte(a.MaxTokenTTL.String()))
   631  	_, _ = hash.Write([]byte(strconv.FormatBool(a.Default)))
   632  
   633  	if a.Config != nil {
   634  		_, _ = hash.Write([]byte(a.Config.OIDCDiscoveryURL))
   635  		_, _ = hash.Write([]byte(a.Config.OIDCClientID))
   636  		_, _ = hash.Write([]byte(a.Config.OIDCClientSecret))
   637  		for _, ba := range a.Config.BoundAudiences {
   638  			_, _ = hash.Write([]byte(ba))
   639  		}
   640  		for _, uri := range a.Config.AllowedRedirectURIs {
   641  			_, _ = hash.Write([]byte(uri))
   642  		}
   643  		for _, pem := range a.Config.DiscoveryCaPem {
   644  			_, _ = hash.Write([]byte(pem))
   645  		}
   646  		for _, sa := range a.Config.SigningAlgs {
   647  			_, _ = hash.Write([]byte(sa))
   648  		}
   649  		for k, v := range a.Config.ClaimMappings {
   650  			_, _ = hash.Write([]byte(k))
   651  			_, _ = hash.Write([]byte(v))
   652  		}
   653  		for k, v := range a.Config.ListClaimMappings {
   654  			_, _ = hash.Write([]byte(k))
   655  			_, _ = hash.Write([]byte(v))
   656  		}
   657  	}
   658  
   659  	// Finalize the hash.
   660  	hashVal := hash.Sum(nil)
   661  
   662  	// Set and return the hash.
   663  	a.Hash = hashVal
   664  	return hashVal
   665  }
   666  
   667  // MarshalJSON implements the json.Marshaler interface and allows
   668  // ACLAuthMethod.MaxTokenTTL to be marshaled correctly.
   669  func (a *ACLAuthMethod) MarshalJSON() ([]byte, error) {
   670  	type Alias ACLAuthMethod
   671  	exported := &struct {
   672  		MaxTokenTTL string
   673  		*Alias
   674  	}{
   675  		MaxTokenTTL: a.MaxTokenTTL.String(),
   676  		Alias:       (*Alias)(a),
   677  	}
   678  	if a.MaxTokenTTL == 0 {
   679  		exported.MaxTokenTTL = ""
   680  	}
   681  	return json.Marshal(exported)
   682  }
   683  
   684  // UnmarshalJSON implements the json.Unmarshaler interface and allows
   685  // ACLAuthMethod.MaxTokenTTL to be unmarshalled correctly.
   686  func (a *ACLAuthMethod) UnmarshalJSON(data []byte) (err error) {
   687  	type Alias ACLAuthMethod
   688  	aux := &struct {
   689  		MaxTokenTTL interface{}
   690  		*Alias
   691  	}{
   692  		Alias: (*Alias)(a),
   693  	}
   694  	if err = json.Unmarshal(data, &aux); err != nil {
   695  		return err
   696  	}
   697  	if aux.MaxTokenTTL != nil {
   698  		switch v := aux.MaxTokenTTL.(type) {
   699  		case string:
   700  			if a.MaxTokenTTL, err = time.ParseDuration(v); err != nil {
   701  				return err
   702  			}
   703  		case float64:
   704  			a.MaxTokenTTL = time.Duration(v)
   705  		}
   706  	}
   707  	return nil
   708  }
   709  
   710  func (a *ACLAuthMethod) Stub() *ACLAuthMethodStub {
   711  	return &ACLAuthMethodStub{
   712  		Name:        a.Name,
   713  		Type:        a.Type,
   714  		Default:     a.Default,
   715  		Hash:        a.Hash,
   716  		CreateIndex: a.CreateIndex,
   717  		ModifyIndex: a.ModifyIndex,
   718  	}
   719  }
   720  
   721  func (a *ACLAuthMethod) Equal(other *ACLAuthMethod) bool {
   722  	if a == nil || other == nil {
   723  		return a == other
   724  	}
   725  	if len(a.Hash) == 0 {
   726  		a.SetHash()
   727  	}
   728  	if len(other.Hash) == 0 {
   729  		other.SetHash()
   730  	}
   731  	return bytes.Equal(a.Hash, other.Hash)
   732  
   733  }
   734  
   735  // Copy creates a deep copy of the ACL auth method. This copy can then be safely
   736  // modified. It handles nil objects.
   737  func (a *ACLAuthMethod) Copy() *ACLAuthMethod {
   738  	if a == nil {
   739  		return nil
   740  	}
   741  
   742  	c := new(ACLAuthMethod)
   743  	*c = *a
   744  
   745  	c.Hash = slices.Clone(a.Hash)
   746  	c.Config = a.Config.Copy()
   747  
   748  	return c
   749  }
   750  
   751  // Canonicalize performs basic canonicalization on the ACL auth method object.
   752  func (a *ACLAuthMethod) Canonicalize() {
   753  	t := time.Now().UTC()
   754  
   755  	if a.CreateTime.IsZero() {
   756  		a.CreateTime = t
   757  	}
   758  	a.ModifyTime = t
   759  }
   760  
   761  // Validate returns an error is the ACLAuthMethod is invalid.
   762  //
   763  // TODO revisit possible other validity conditions in the future
   764  func (a *ACLAuthMethod) Validate(minTTL, maxTTL time.Duration) error {
   765  	var mErr multierror.Error
   766  
   767  	if !validACLAuthMethod.MatchString(a.Name) {
   768  		mErr.Errors = append(mErr.Errors, fmt.Errorf("invalid name '%s'", a.Name))
   769  	}
   770  
   771  	if !slices.Contains([]string{"local", "global"}, a.TokenLocality) {
   772  		mErr.Errors = append(
   773  			mErr.Errors, fmt.Errorf("invalid token locality '%s'", a.TokenLocality))
   774  	}
   775  
   776  	if a.Type != "OIDC" {
   777  		mErr.Errors = append(
   778  			mErr.Errors, fmt.Errorf("invalid token type '%s'", a.Type))
   779  	}
   780  
   781  	if minTTL > a.MaxTokenTTL || a.MaxTokenTTL > maxTTL {
   782  		mErr.Errors = append(mErr.Errors, fmt.Errorf(
   783  			"invalid MaxTokenTTL value '%s' (should be between %s and %s)",
   784  			a.MaxTokenTTL.String(), minTTL.String(), maxTTL.String()))
   785  	}
   786  
   787  	return mErr.ErrorOrNil()
   788  }
   789  
   790  // ACLAuthMethodConfig is used to store configuration of an auth method
   791  type ACLAuthMethodConfig struct {
   792  	OIDCDiscoveryURL    string
   793  	OIDCClientID        string
   794  	OIDCClientSecret    string
   795  	BoundAudiences      []string
   796  	AllowedRedirectURIs []string
   797  	DiscoveryCaPem      []string
   798  	SigningAlgs         []string
   799  	ClaimMappings       map[string]string
   800  	ListClaimMappings   map[string]string
   801  }
   802  
   803  func (a *ACLAuthMethodConfig) Copy() *ACLAuthMethodConfig {
   804  	if a == nil {
   805  		return nil
   806  	}
   807  
   808  	c := new(ACLAuthMethodConfig)
   809  	*c = *a
   810  
   811  	c.BoundAudiences = slices.Clone(a.BoundAudiences)
   812  	c.AllowedRedirectURIs = slices.Clone(a.AllowedRedirectURIs)
   813  	c.DiscoveryCaPem = slices.Clone(a.DiscoveryCaPem)
   814  	c.SigningAlgs = slices.Clone(a.SigningAlgs)
   815  
   816  	return c
   817  }
   818  
   819  // ACLAuthMethodStub is used for listing ACL auth methods
   820  type ACLAuthMethodStub struct {
   821  	Name    string
   822  	Type    string
   823  	Default bool
   824  
   825  	// Hash is the hashed value of the auth-method and is generated using all
   826  	// fields from the full object except the create and modify times and
   827  	// indexes.
   828  	Hash []byte
   829  
   830  	CreateIndex uint64
   831  	ModifyIndex uint64
   832  }
   833  
   834  // ACLAuthMethodListRequest is used to list auth methods
   835  type ACLAuthMethodListRequest struct {
   836  	QueryOptions
   837  }
   838  
   839  // ACLAuthMethodListResponse is used to list auth methods
   840  type ACLAuthMethodListResponse struct {
   841  	AuthMethods []*ACLAuthMethodStub
   842  	QueryMeta
   843  }
   844  
   845  // ACLAuthMethodGetRequest is used to query a specific auth method
   846  type ACLAuthMethodGetRequest struct {
   847  	MethodName string
   848  	QueryOptions
   849  }
   850  
   851  // ACLAuthMethodGetResponse is used to return a single auth method
   852  type ACLAuthMethodGetResponse struct {
   853  	AuthMethod *ACLAuthMethod
   854  	QueryMeta
   855  }
   856  
   857  // ACLAuthMethodsGetRequest is used to query a set of auth methods
   858  type ACLAuthMethodsGetRequest struct {
   859  	Names []string
   860  	QueryOptions
   861  }
   862  
   863  // ACLAuthMethodsGetResponse is used to return a set of auth methods
   864  type ACLAuthMethodsGetResponse struct {
   865  	AuthMethods map[string]*ACLAuthMethod
   866  	QueryMeta
   867  }
   868  
   869  // ACLAuthMethodUpsertRequest is used to upsert a set of auth methods
   870  type ACLAuthMethodUpsertRequest struct {
   871  	AuthMethods []*ACLAuthMethod
   872  	WriteRequest
   873  }
   874  
   875  // ACLAuthMethodUpsertResponse is a response of the upsert ACL auth methods
   876  // operation
   877  type ACLAuthMethodUpsertResponse struct {
   878  	AuthMethods []*ACLAuthMethod
   879  	WriteMeta
   880  }
   881  
   882  // ACLAuthMethodDeleteRequest is used to delete a set of auth methods by their
   883  // name
   884  type ACLAuthMethodDeleteRequest struct {
   885  	Names []string
   886  	WriteRequest
   887  }
   888  
   889  // ACLAuthMethodDeleteResponse is a response of the delete ACL auth methods
   890  // operation
   891  type ACLAuthMethodDeleteResponse struct {
   892  	WriteMeta
   893  }
   894  
   895  type ACLWhoAmIResponse struct {
   896  	Identity *AuthenticatedIdentity
   897  	QueryMeta
   898  }
   899  
   900  // ACLBindingRule contains a direct relation to an ACLAuthMethod and represents
   901  // a rule to apply when logging in via the named AuthMethod. This allows the
   902  // transformation of OIDC provider claims, to Nomad based ACL concepts such as
   903  // ACL Roles and Policies.
   904  type ACLBindingRule struct {
   905  
   906  	// ID is an internally generated UUID for this role and is controlled by
   907  	// Nomad.
   908  	ID string
   909  
   910  	// Description is a human-readable, operator set description that can
   911  	// provide additional context about the binding role. This is an
   912  	// operational field.
   913  	Description string
   914  
   915  	// AuthMethod is the name of the auth method for which this rule applies
   916  	// to. This is required and the method must exist within state before the
   917  	// cluster administrator can create the rule.
   918  	AuthMethod string
   919  
   920  	// Selector is an expression that matches against verified identity
   921  	// attributes returned from the auth method during login. This is optional
   922  	// and when not set, provides a catch-all rule.
   923  	Selector string
   924  
   925  	// BindType adjusts how this binding rule is applied at login time. The
   926  	// valid values are ACLBindingRuleBindTypeRole and
   927  	// ACLBindingRuleBindTypePolicy.
   928  	BindType string
   929  
   930  	// BindName is the target of the binding. Can be lightly templated using
   931  	// HIL ${foo} syntax from available field names. How it is used depends
   932  	// upon the BindType.
   933  	BindName string
   934  
   935  	// Hash is the hashed value of the binding rule and is generated using all
   936  	// fields from the full object except the create and modify times and
   937  	// indexes.
   938  	Hash []byte
   939  
   940  	CreateTime  time.Time
   941  	ModifyTime  time.Time
   942  	CreateIndex uint64
   943  	ModifyIndex uint64
   944  }
   945  
   946  const (
   947  	// ACLBindingRuleBindTypeRole is the ACL binding rule bind type that only
   948  	// allows the binding rule to function if a role exists at login-time. The
   949  	// role will be specified within the ACLBindingRule.BindName parameter, and
   950  	// will identify whether this is an ID or Name.
   951  	ACLBindingRuleBindTypeRole = "role"
   952  
   953  	// ACLBindingRuleBindTypePolicy is the ACL binding rule bind type that
   954  	// assigns a policy to the generate ACL token. The role will be specified
   955  	// within the ACLBindingRule.BindName parameter, and will be the policy
   956  	// name.
   957  	ACLBindingRuleBindTypePolicy = "policy"
   958  )
   959  
   960  // Canonicalize performs basic canonicalization on the ACL token object. It is
   961  // important for callers to understand certain fields such as ID are set if it
   962  // is empty, so copies should be taken if needed before calling this function.
   963  func (a *ACLBindingRule) Canonicalize() {
   964  
   965  	now := time.Now().UTC()
   966  
   967  	// If the ID is empty, it means this is creation of a new binding rule,
   968  	// therefore we need to generate base information.
   969  	if a.ID == "" {
   970  		a.ID = uuid.Generate()
   971  		a.CreateTime = now
   972  	}
   973  
   974  	// The fact this function is being called indicates we are attempting an
   975  	// upsert into state. Therefore, update the modify time.
   976  	a.ModifyTime = now
   977  }
   978  
   979  // Validate ensures the ACL binding rule contains valid information which meets
   980  // Nomad's internal requirements.
   981  func (a *ACLBindingRule) Validate() error {
   982  
   983  	var mErr multierror.Error
   984  
   985  	if a.AuthMethod == "" {
   986  		mErr.Errors = append(mErr.Errors, errors.New("auth method is missing"))
   987  	}
   988  	if a.BindName == "" {
   989  		mErr.Errors = append(mErr.Errors, errors.New("bind name is missing"))
   990  	}
   991  	if len(a.Description) > maxACLBindingRuleDescriptionLength {
   992  		mErr.Errors = append(mErr.Errors, fmt.Errorf("description longer than %d", maxACLRoleDescriptionLength))
   993  	}
   994  
   995  	// Be specific about the error as returning an error that includes an empty
   996  	// quote ("") can be a little confusing.
   997  	if a.BindType == "" {
   998  		mErr.Errors = append(mErr.Errors, errors.New("bind type is missing"))
   999  	} else {
  1000  		switch a.BindType {
  1001  		case ACLBindingRuleBindTypeRole, ACLBindingRuleBindTypePolicy: // fall-through.
  1002  		default:
  1003  			mErr.Errors = append(mErr.Errors, fmt.Errorf("unsupported bind type: %q", a.BindType))
  1004  		}
  1005  	}
  1006  
  1007  	return mErr.ErrorOrNil()
  1008  }
  1009  
  1010  // SetHash is used to compute and set the hash of the ACL binding rule. This
  1011  // should be called every and each time a user specified field on the method is
  1012  // changed before updating the Nomad state store.
  1013  func (a *ACLBindingRule) SetHash() []byte {
  1014  
  1015  	// Initialize a 256bit Blake2 hash (32 bytes).
  1016  	hash, err := blake2b.New256(nil)
  1017  	if err != nil {
  1018  		panic(err)
  1019  	}
  1020  
  1021  	_, _ = hash.Write([]byte(a.ID))
  1022  	_, _ = hash.Write([]byte(a.Description))
  1023  	_, _ = hash.Write([]byte(a.AuthMethod))
  1024  	_, _ = hash.Write([]byte(a.Selector))
  1025  	_, _ = hash.Write([]byte(a.BindType))
  1026  	_, _ = hash.Write([]byte(a.BindName))
  1027  
  1028  	// Finalize the hash.
  1029  	hashVal := hash.Sum(nil)
  1030  
  1031  	// Set and return the hash.
  1032  	a.Hash = hashVal
  1033  	return hashVal
  1034  }
  1035  
  1036  // Equal performs an equality check on the two ACL binding rules. It handles
  1037  // nil objects.
  1038  func (a *ACLBindingRule) Equal(other *ACLBindingRule) bool {
  1039  	if a == nil || other == nil {
  1040  		return a == other
  1041  	}
  1042  	if len(a.Hash) == 0 {
  1043  		a.SetHash()
  1044  	}
  1045  	if len(other.Hash) == 0 {
  1046  		other.SetHash()
  1047  	}
  1048  	return bytes.Equal(a.Hash, other.Hash)
  1049  }
  1050  
  1051  // Copy creates a deep copy of the ACL binding rule. This copy can then be
  1052  // safely modified. It handles nil objects.
  1053  func (a *ACLBindingRule) Copy() *ACLBindingRule {
  1054  	if a == nil {
  1055  		return nil
  1056  	}
  1057  
  1058  	c := new(ACLBindingRule)
  1059  	*c = *a
  1060  	c.Hash = slices.Clone(a.Hash)
  1061  
  1062  	return c
  1063  }
  1064  
  1065  // Stub converts the ACLBindingRule object into a ACLBindingRuleListStub
  1066  // object.
  1067  func (a *ACLBindingRule) Stub() *ACLBindingRuleListStub {
  1068  	return &ACLBindingRuleListStub{
  1069  		ID:          a.ID,
  1070  		Description: a.Description,
  1071  		AuthMethod:  a.AuthMethod,
  1072  		Hash:        a.Hash,
  1073  		CreateIndex: a.CreateIndex,
  1074  		ModifyIndex: a.ModifyIndex,
  1075  	}
  1076  }
  1077  
  1078  // ACLBindingRuleListStub is the stub object returned when performing a listing
  1079  // of ACL binding rules.
  1080  type ACLBindingRuleListStub struct {
  1081  
  1082  	// ID is an internally generated UUID for this role and is controlled by
  1083  	// Nomad.
  1084  	ID string
  1085  
  1086  	// Description is a human-readable, operator set description that can
  1087  	// provide additional context about the binding role. This is an
  1088  	// operational field.
  1089  	Description string
  1090  
  1091  	// AuthMethod is the name of the auth method for which this rule applies
  1092  	// to. This is required and the method must exist within state before the
  1093  	// cluster administrator can create the rule.
  1094  	AuthMethod string
  1095  
  1096  	// Hash is the hashed value of the binding rule and is generated using all
  1097  	// fields from the full object except the create and modify times and
  1098  	// indexes.
  1099  	Hash []byte
  1100  
  1101  	CreateIndex uint64
  1102  	ModifyIndex uint64
  1103  }
  1104  
  1105  // ACLBindingRulesUpsertRequest is used to upsert a set of ACL binding rules.
  1106  type ACLBindingRulesUpsertRequest struct {
  1107  	ACLBindingRules []*ACLBindingRule
  1108  	WriteRequest
  1109  }
  1110  
  1111  // ACLBindingRulesUpsertResponse is a response of the upsert ACL binding rules
  1112  // operation.
  1113  type ACLBindingRulesUpsertResponse struct {
  1114  	ACLBindingRules []*ACLBindingRule
  1115  	WriteMeta
  1116  }
  1117  
  1118  // ACLBindingRulesDeleteRequest is used to delete a set of ACL binding rules by
  1119  // their IDs.
  1120  type ACLBindingRulesDeleteRequest struct {
  1121  	ACLBindingRuleIDs []string
  1122  	WriteRequest
  1123  }
  1124  
  1125  // ACLBindingRulesDeleteResponse is a response of the delete ACL binding rules
  1126  // operation.
  1127  type ACLBindingRulesDeleteResponse struct {
  1128  	WriteMeta
  1129  }
  1130  
  1131  // ACLBindingRulesListRequest  is the request object when performing ACL
  1132  // binding rules listings.
  1133  type ACLBindingRulesListRequest struct {
  1134  	QueryOptions
  1135  }
  1136  
  1137  // ACLBindingRulesListResponse is the response object when performing ACL
  1138  // binding rule listings.
  1139  type ACLBindingRulesListResponse struct {
  1140  	ACLBindingRules []*ACLBindingRuleListStub
  1141  	QueryMeta
  1142  }
  1143  
  1144  // ACLBindingRulesRequest is the request object when performing a lookup of
  1145  // multiple binding rules by the ID.
  1146  type ACLBindingRulesRequest struct {
  1147  	ACLBindingRuleIDs []string
  1148  	QueryOptions
  1149  }
  1150  
  1151  // ACLBindingRulesResponse is the response object when performing a lookup of
  1152  // multiple binding rules by their IDs.
  1153  type ACLBindingRulesResponse struct {
  1154  	ACLBindingRules map[string]*ACLBindingRule
  1155  	QueryMeta
  1156  }
  1157  
  1158  // ACLBindingRuleRequest is the request object to perform a lookup of an ACL
  1159  // binding rule using a specific ID.
  1160  type ACLBindingRuleRequest struct {
  1161  	ACLBindingRuleID string
  1162  	QueryOptions
  1163  }
  1164  
  1165  // ACLBindingRuleResponse is the response object when performing a lookup of an
  1166  // ACL binding rule matching a specific ID.
  1167  type ACLBindingRuleResponse struct {
  1168  	ACLBindingRule *ACLBindingRule
  1169  	QueryMeta
  1170  }