open-nomad/api/acl.go

1112 lines
32 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package api
import (
"encoding/json"
"errors"
"fmt"
"time"
)
// ACLPolicies is used to query the ACL Policy endpoints.
type ACLPolicies struct {
client *Client
}
// ACLPolicies returns a new handle on the ACL policies.
func (c *Client) ACLPolicies() *ACLPolicies {
return &ACLPolicies{client: c}
}
// List is used to dump all of the policies.
func (a *ACLPolicies) List(q *QueryOptions) ([]*ACLPolicyListStub, *QueryMeta, error) {
var resp []*ACLPolicyListStub
qm, err := a.client.query("/v1/acl/policies", &resp, q)
if err != nil {
return nil, nil, err
}
return resp, qm, nil
}
// Upsert is used to create or update a policy
func (a *ACLPolicies) Upsert(policy *ACLPolicy, q *WriteOptions) (*WriteMeta, error) {
if policy == nil || policy.Name == "" {
return nil, errors.New("missing policy name")
}
wm, err := a.client.put("/v1/acl/policy/"+policy.Name, policy, nil, q)
if err != nil {
return nil, err
}
return wm, nil
}
// Delete is used to delete a policy
func (a *ACLPolicies) Delete(policyName string, q *WriteOptions) (*WriteMeta, error) {
if policyName == "" {
return nil, errors.New("missing policy name")
}
wm, err := a.client.delete("/v1/acl/policy/"+policyName, nil, nil, q)
if err != nil {
return nil, err
}
return wm, nil
}
// Info is used to query a specific policy
func (a *ACLPolicies) Info(policyName string, q *QueryOptions) (*ACLPolicy, *QueryMeta, error) {
if policyName == "" {
return nil, nil, errors.New("missing policy name")
}
var resp ACLPolicy
wm, err := a.client.query("/v1/acl/policy/"+policyName, &resp, q)
if err != nil {
return nil, nil, err
}
return &resp, wm, nil
}
// ACLTokens is used to query the ACL token endpoints.
type ACLTokens struct {
client *Client
}
// ACLTokens returns a new handle on the ACL tokens.
func (c *Client) ACLTokens() *ACLTokens {
return &ACLTokens{client: c}
}
// DEPRECATED: will be removed in Nomad 1.5.0
// Bootstrap is used to get the initial bootstrap token
func (a *ACLTokens) Bootstrap(q *WriteOptions) (*ACLToken, *WriteMeta, error) {
var resp ACLToken
wm, err := a.client.put("/v1/acl/bootstrap", nil, &resp, q)
if err != nil {
return nil, nil, err
}
return &resp, wm, nil
}
// BootstrapOpts is used to get the initial bootstrap token or pass in the one that was provided in the API
func (a *ACLTokens) BootstrapOpts(btoken string, q *WriteOptions) (*ACLToken, *WriteMeta, error) {
if q == nil {
q = &WriteOptions{}
}
req := &BootstrapRequest{
BootstrapSecret: btoken,
}
var resp ACLToken
wm, err := a.client.put("/v1/acl/bootstrap", req, &resp, q)
if err != nil {
return nil, nil, err
}
return &resp, wm, nil
}
// List is used to dump all of the tokens.
func (a *ACLTokens) List(q *QueryOptions) ([]*ACLTokenListStub, *QueryMeta, error) {
var resp []*ACLTokenListStub
qm, err := a.client.query("/v1/acl/tokens", &resp, q)
if err != nil {
return nil, nil, err
}
return resp, qm, nil
}
// Create is used to create a token
func (a *ACLTokens) Create(token *ACLToken, q *WriteOptions) (*ACLToken, *WriteMeta, error) {
if token.AccessorID != "" {
return nil, nil, errors.New("cannot specify Accessor ID")
}
var resp ACLToken
wm, err := a.client.put("/v1/acl/token", token, &resp, q)
if err != nil {
return nil, nil, err
}
return &resp, wm, nil
}
// Update is used to update an existing token
func (a *ACLTokens) Update(token *ACLToken, q *WriteOptions) (*ACLToken, *WriteMeta, error) {
if token.AccessorID == "" {
return nil, nil, errors.New("missing accessor ID")
}
var resp ACLToken
wm, err := a.client.put("/v1/acl/token/"+token.AccessorID,
token, &resp, q)
if err != nil {
return nil, nil, err
}
return &resp, wm, nil
}
// Delete is used to delete a token
func (a *ACLTokens) Delete(accessorID string, q *WriteOptions) (*WriteMeta, error) {
if accessorID == "" {
return nil, errors.New("missing accessor ID")
}
wm, err := a.client.delete("/v1/acl/token/"+accessorID, nil, nil, q)
if err != nil {
return nil, err
}
return wm, nil
}
// Info is used to query a token
func (a *ACLTokens) Info(accessorID string, q *QueryOptions) (*ACLToken, *QueryMeta, error) {
if accessorID == "" {
return nil, nil, errors.New("missing accessor ID")
}
var resp ACLToken
wm, err := a.client.query("/v1/acl/token/"+accessorID, &resp, q)
if err != nil {
return nil, nil, err
}
return &resp, wm, nil
}
// Self is used to query our own token
func (a *ACLTokens) Self(q *QueryOptions) (*ACLToken, *QueryMeta, error) {
var resp ACLToken
wm, err := a.client.query("/v1/acl/token/self", &resp, q)
if err != nil {
return nil, nil, err
}
return &resp, wm, nil
}
// UpsertOneTimeToken is used to create a one-time token
func (a *ACLTokens) UpsertOneTimeToken(q *WriteOptions) (*OneTimeToken, *WriteMeta, error) {
var resp *OneTimeTokenUpsertResponse
wm, err := a.client.put("/v1/acl/token/onetime", nil, &resp, q)
if err != nil {
return nil, nil, err
}
if resp == nil {
return nil, nil, errors.New("no one-time token returned")
}
return resp.OneTimeToken, wm, nil
}
// ExchangeOneTimeToken is used to create a one-time token
func (a *ACLTokens) ExchangeOneTimeToken(secret string, q *WriteOptions) (*ACLToken, *WriteMeta, error) {
if secret == "" {
return nil, nil, errors.New("missing secret ID")
}
req := &OneTimeTokenExchangeRequest{OneTimeSecretID: secret}
var resp *OneTimeTokenExchangeResponse
wm, err := a.client.put("/v1/acl/token/onetime/exchange", req, &resp, q)
if err != nil {
return nil, nil, err
}
if resp == nil {
return nil, nil, errors.New("no ACL token returned")
}
return resp.Token, wm, nil
}
var (
// errMissingACLRoleID is the generic errors to use when a call is missing
// the required ACL Role ID parameter.
errMissingACLRoleID = errors.New("missing ACL role ID")
// errMissingACLAuthMethodName is the generic error to use when a call is
// missing the required ACL auth-method name parameter.
errMissingACLAuthMethodName = errors.New("missing ACL auth-method name")
// errMissingACLBindingRuleID is the generic error to use when a call is
// missing the required ACL binding rule ID parameter.
errMissingACLBindingRuleID = errors.New("missing ACL binding rule ID")
)
// ACLRoles is used to query the ACL Role endpoints.
type ACLRoles struct {
client *Client
}
// ACLRoles returns a new handle on the ACL roles API client.
func (c *Client) ACLRoles() *ACLRoles {
return &ACLRoles{client: c}
}
// List is used to detail all the ACL roles currently stored within state.
func (a *ACLRoles) List(q *QueryOptions) ([]*ACLRoleListStub, *QueryMeta, error) {
var resp []*ACLRoleListStub
qm, err := a.client.query("/v1/acl/roles", &resp, q)
if err != nil {
return nil, nil, err
}
return resp, qm, nil
}
// Create is used to create an ACL role.
func (a *ACLRoles) Create(role *ACLRole, w *WriteOptions) (*ACLRole, *WriteMeta, error) {
if role.ID != "" {
return nil, nil, errors.New("cannot specify ACL role ID")
}
var resp ACLRole
wm, err := a.client.put("/v1/acl/role", role, &resp, w)
if err != nil {
return nil, nil, err
}
return &resp, wm, nil
}
// Update is used to update an existing ACL role.
func (a *ACLRoles) Update(role *ACLRole, w *WriteOptions) (*ACLRole, *WriteMeta, error) {
if role.ID == "" {
return nil, nil, errMissingACLRoleID
}
var resp ACLRole
wm, err := a.client.put("/v1/acl/role/"+role.ID, role, &resp, w)
if err != nil {
return nil, nil, err
}
return &resp, wm, nil
}
// Delete is used to delete an ACL role.
func (a *ACLRoles) Delete(roleID string, w *WriteOptions) (*WriteMeta, error) {
if roleID == "" {
return nil, errMissingACLRoleID
}
wm, err := a.client.delete("/v1/acl/role/"+roleID, nil, nil, w)
if err != nil {
return nil, err
}
return wm, nil
}
// Get is used to look up an ACL role.
func (a *ACLRoles) Get(roleID string, q *QueryOptions) (*ACLRole, *QueryMeta, error) {
if roleID == "" {
return nil, nil, errMissingACLRoleID
}
var resp ACLRole
qm, err := a.client.query("/v1/acl/role/"+roleID, &resp, q)
if err != nil {
return nil, nil, err
}
return &resp, qm, nil
}
// GetByName is used to look up an ACL role using its name.
func (a *ACLRoles) GetByName(roleName string, q *QueryOptions) (*ACLRole, *QueryMeta, error) {
if roleName == "" {
return nil, nil, errors.New("missing ACL role name")
}
var resp ACLRole
qm, err := a.client.query("/v1/acl/role/name/"+roleName, &resp, q)
if err != nil {
return nil, nil, err
}
return &resp, qm, nil
}
// ACLAuthMethods is used to query the ACL auth-methods endpoints.
type ACLAuthMethods struct {
client *Client
}
// ACLAuthMethods returns a new handle on the ACL auth-methods API client.
func (c *Client) ACLAuthMethods() *ACLAuthMethods {
return &ACLAuthMethods{client: c}
}
// List is used to detail all the ACL auth-methods currently stored within
// state.
func (a *ACLAuthMethods) List(q *QueryOptions) ([]*ACLAuthMethodListStub, *QueryMeta, error) {
var resp []*ACLAuthMethodListStub
qm, err := a.client.query("/v1/acl/auth-methods", &resp, q)
if err != nil {
return nil, nil, err
}
return resp, qm, nil
}
// Create is used to create an ACL auth-method.
func (a *ACLAuthMethods) Create(authMethod *ACLAuthMethod, w *WriteOptions) (*ACLAuthMethod, *WriteMeta, error) {
if authMethod.Name == "" {
return nil, nil, errMissingACLAuthMethodName
}
var resp ACLAuthMethod
wm, err := a.client.put("/v1/acl/auth-method", authMethod, &resp, w)
if err != nil {
return nil, nil, err
}
return &resp, wm, nil
}
// Update is used to update an existing ACL auth-method.
func (a *ACLAuthMethods) Update(authMethod *ACLAuthMethod, w *WriteOptions) (*ACLAuthMethod, *WriteMeta, error) {
if authMethod.Name == "" {
return nil, nil, errMissingACLAuthMethodName
}
var resp ACLAuthMethod
wm, err := a.client.put("/v1/acl/auth-method/"+authMethod.Name, authMethod, &resp, w)
if err != nil {
return nil, nil, err
}
return &resp, wm, nil
}
// Delete is used to delete an ACL auth-method.
func (a *ACLAuthMethods) Delete(authMethodName string, w *WriteOptions) (*WriteMeta, error) {
if authMethodName == "" {
return nil, errMissingACLAuthMethodName
}
wm, err := a.client.delete("/v1/acl/auth-method/"+authMethodName, nil, nil, w)
if err != nil {
return nil, err
}
return wm, nil
}
// Get is used to look up an ACL auth-method.
func (a *ACLAuthMethods) Get(authMethodName string, q *QueryOptions) (*ACLAuthMethod, *QueryMeta, error) {
if authMethodName == "" {
return nil, nil, errMissingACLAuthMethodName
}
var resp ACLAuthMethod
qm, err := a.client.query("/v1/acl/auth-method/"+authMethodName, &resp, q)
if err != nil {
return nil, nil, err
}
return &resp, qm, nil
}
// ACLBindingRules is used to query the ACL auth-methods endpoints.
type ACLBindingRules struct {
client *Client
}
// ACLBindingRules returns a new handle on the ACL auth-methods API client.
func (c *Client) ACLBindingRules() *ACLBindingRules {
return &ACLBindingRules{client: c}
}
// List is used to detail all the ACL binding rules currently stored within
// state.
func (a *ACLBindingRules) List(q *QueryOptions) ([]*ACLBindingRuleListStub, *QueryMeta, error) {
var resp []*ACLBindingRuleListStub
qm, err := a.client.query("/v1/acl/binding-rules", &resp, q)
if err != nil {
return nil, nil, err
}
return resp, qm, nil
}
// Create is used to create an ACL binding rule.
func (a *ACLBindingRules) Create(bindingRule *ACLBindingRule, w *WriteOptions) (*ACLBindingRule, *WriteMeta, error) {
var resp ACLBindingRule
wm, err := a.client.put("/v1/acl/binding-rule", bindingRule, &resp, w)
if err != nil {
return nil, nil, err
}
return &resp, wm, nil
}
// Update is used to update an existing ACL binding rule.
func (a *ACLBindingRules) Update(bindingRule *ACLBindingRule, w *WriteOptions) (*ACLBindingRule, *WriteMeta, error) {
if bindingRule.ID == "" {
return nil, nil, errMissingACLBindingRuleID
}
var resp ACLBindingRule
wm, err := a.client.put("/v1/acl/binding-rule/"+bindingRule.ID, bindingRule, &resp, w)
if err != nil {
return nil, nil, err
}
return &resp, wm, nil
}
// Delete is used to delete an ACL binding rule.
func (a *ACLBindingRules) Delete(bindingRuleID string, w *WriteOptions) (*WriteMeta, error) {
if bindingRuleID == "" {
return nil, errMissingACLBindingRuleID
}
wm, err := a.client.delete("/v1/acl/binding-rule/"+bindingRuleID, nil, nil, w)
if err != nil {
return nil, err
}
return wm, nil
}
// Get is used to look up an ACL binding rule.
func (a *ACLBindingRules) Get(bindingRuleID string, q *QueryOptions) (*ACLBindingRule, *QueryMeta, error) {
if bindingRuleID == "" {
return nil, nil, errMissingACLBindingRuleID
}
var resp ACLBindingRule
qm, err := a.client.query("/v1/acl/binding-rule/"+bindingRuleID, &resp, q)
if err != nil {
return nil, nil, err
}
return &resp, qm, nil
}
// ACLOIDC is used to query the ACL OIDC endpoints.
//
// Deprecated: ACLOIDC is deprecated, use ACLAuth instead.
type ACLOIDC struct {
client *Client
ACLAuth
}
// ACLOIDC returns a new handle on the ACL auth-methods API client.
//
// Deprecated: c.ACLOIDC() is deprecated, use c.ACLAuth() instead.
func (c *Client) ACLOIDC() *ACLOIDC {
return &ACLOIDC{client: c}
}
// ACLAuth is used to query the ACL auth endpoints.
type ACLAuth struct {
client *Client
}
// ACLAuth returns a new handle on the ACL auth-methods API client.
func (c *Client) ACLAuth() *ACLAuth {
return &ACLAuth{client: c}
}
// GetAuthURL generates the OIDC provider authentication URL. This URL should
// be visited in order to sign in to the provider.
func (a *ACLAuth) GetAuthURL(req *ACLOIDCAuthURLRequest, q *WriteOptions) (*ACLOIDCAuthURLResponse, *WriteMeta, error) {
var resp ACLOIDCAuthURLResponse
wm, err := a.client.put("/v1/acl/oidc/auth-url", req, &resp, q)
if err != nil {
return nil, nil, err
}
return &resp, wm, nil
}
// CompleteAuth exchanges the OIDC provider token for a Nomad token with the
// appropriate claims attached.
func (a *ACLAuth) CompleteAuth(req *ACLOIDCCompleteAuthRequest, q *WriteOptions) (*ACLToken, *WriteMeta, error) {
var resp ACLToken
wm, err := a.client.put("/v1/acl/oidc/complete-auth", req, &resp, q)
if err != nil {
return nil, nil, err
}
return &resp, wm, nil
}
// Login exchanges the third party token for a Nomad token with the appropriate
// claims attached.
func (a *ACLAuth) Login(req *ACLLoginRequest, q *WriteOptions) (*ACLToken, *WriteMeta, error) {
var resp ACLToken
wm, err := a.client.put("/v1/acl/login", req, &resp, q)
if err != nil {
return nil, nil, err
}
return &resp, wm, nil
}
// ACLPolicyListStub is used to for listing ACL policies
type ACLPolicyListStub struct {
Name string
Description string
CreateIndex uint64
ModifyIndex uint64
}
// ACLPolicy is used to represent an ACL policy
type ACLPolicy struct {
Name string
Description string
Rules string
JobACL *JobACL
CreateIndex uint64
ModifyIndex uint64
}
// JobACL represents an ACL policy's attachment to a job, group, or task.
type JobACL struct {
Namespace string
JobID string
Group string
Task string
}
// ACLToken represents a client token which is used to Authenticate
type ACLToken struct {
AccessorID string
SecretID string
Name string
Type string
Policies []string
// Roles represents the ACL roles that this token is tied to. The token
// will inherit the permissions of all policies detailed within the role.
Roles []*ACLTokenRoleLink
Global bool
CreateTime time.Time
// ExpirationTime represents the point after which a token should be
// considered revoked and is eligible for destruction. The zero value of
// time.Time does not respect json omitempty directives, so we must use a
// pointer.
ExpirationTime *time.Time `json:",omitempty"`
// ExpirationTTL is a convenience field for helping set ExpirationTime to a
// value of CreateTime+ExpirationTTL. This can only be set during token
// creation. This is a string version of a time.Duration like "2m".
ExpirationTTL time.Duration `json:",omitempty"`
CreateIndex uint64
ModifyIndex uint64
}
// ACLTokenRoleLink is used to link an ACL token to an ACL role. The ACL token
// can therefore inherit all the ACL policy permissions that the ACL role
// contains.
type ACLTokenRoleLink struct {
// ID is the ACLRole.ID UUID. This field is immutable and represents the
// absolute truth for the link.
ID string
// Name is the human friendly identifier for the ACL role and is a
// convenience field for operators.
Name string
}
// MarshalJSON implements the json.Marshaler interface and allows
// ACLToken.ExpirationTTL to be marshaled correctly.
func (a *ACLToken) MarshalJSON() ([]byte, error) {
type Alias ACLToken
exported := &struct {
ExpirationTTL string
*Alias
}{
ExpirationTTL: a.ExpirationTTL.String(),
Alias: (*Alias)(a),
}
if a.ExpirationTTL == 0 {
exported.ExpirationTTL = ""
}
return json.Marshal(exported)
}
// UnmarshalJSON implements the json.Unmarshaler interface and allows
// ACLToken.ExpirationTTL to be unmarshalled correctly.
func (a *ACLToken) UnmarshalJSON(data []byte) (err error) {
type Alias ACLToken
aux := &struct {
ExpirationTTL any
*Alias
}{
Alias: (*Alias)(a),
}
if err = json.Unmarshal(data, &aux); err != nil {
return err
}
if aux.ExpirationTTL != nil {
switch v := aux.ExpirationTTL.(type) {
case string:
if v != "" {
if a.ExpirationTTL, err = time.ParseDuration(v); err != nil {
return err
}
}
case float64:
a.ExpirationTTL = time.Duration(v)
}
}
return nil
}
type ACLTokenListStub struct {
AccessorID string
Name string
Type string
Policies []string
Roles []*ACLTokenRoleLink
Global bool
CreateTime time.Time
// ExpirationTime represents the point after which a token should be
// considered revoked and is eligible for destruction. A nil value
// indicates no expiration has been set on the token.
ExpirationTime *time.Time `json:",omitempty"`
CreateIndex uint64
ModifyIndex uint64
}
type OneTimeToken struct {
OneTimeSecretID string
AccessorID string
ExpiresAt time.Time
CreateIndex uint64
ModifyIndex uint64
}
type OneTimeTokenUpsertResponse struct {
OneTimeToken *OneTimeToken
}
type OneTimeTokenExchangeRequest struct {
OneTimeSecretID string
}
type OneTimeTokenExchangeResponse struct {
Token *ACLToken
}
// BootstrapRequest is used for when operators provide an ACL Bootstrap Token
type BootstrapRequest struct {
BootstrapSecret string
}
// ACLRole is an abstraction for the ACL system which allows the grouping of
// ACL policies into a single object. ACL tokens can be created and linked to
// a role; the token then inherits all the permissions granted by the policies.
type ACLRole struct {
// ID is an internally generated UUID for this role and is controlled by
// Nomad. It can be used after role creation to update the existing role.
ID string
// Name is unique across the entire set of federated clusters and is
// supplied by the operator on role creation. The name can be modified by
// updating the role and including the Nomad generated ID. This update will
// not affect tokens created and linked to this role. This is a required
// field.
Name string
// Description is a human-readable, operator set description that can
// provide additional context about the role. This is an optional field.
Description string
// Policies is an array of ACL policy links. Although currently policies
// can only be linked using their name, in the future we will want to add
// IDs also and thus allow operators to specify either a name, an ID, or
// both. At least one entry is required.
Policies []*ACLRolePolicyLink
CreateIndex uint64
ModifyIndex uint64
}
// ACLRolePolicyLink is used to link a policy to an ACL role. We use a struct
// rather than a list of strings as in the future we will want to add IDs to
// policies and then link via these.
type ACLRolePolicyLink struct {
// Name is the ACLPolicy.Name value which will be linked to the ACL role.
Name string
}
// ACLRoleListStub is the stub object returned when performing a listing of ACL
// roles. While it might not currently be different to the full response
// object, it allows us to future-proof the RPC in the event the ACLRole object
// grows over time.
type ACLRoleListStub struct {
// ID is an internally generated UUID for this role and is controlled by
// Nomad.
ID string
// Name is unique across the entire set of federated clusters and is
// supplied by the operator on role creation. The name can be modified by
// updating the role and including the Nomad generated ID. This update will
// not affect tokens created and linked to this role. This is a required
// field.
Name string
// Description is a human-readable, operator set description that can
// provide additional context about the role. This is an operational field.
Description string
// Policies is an array of ACL policy links. Although currently policies
// can only be linked using their name, in the future we will want to add
// IDs also and thus allow operators to specify either a name, an ID, or
// both.
Policies []*ACLRolePolicyLink
CreateIndex uint64
ModifyIndex uint64
}
// ACLAuthMethod is used to capture the properties of an authentication method
// used for single sing-on.
type ACLAuthMethod struct {
// Name is the identifier for this auth-method and is a required parameter.
Name string
// Type is the SSO identifier this auth-method is. Nomad currently only
// supports "oidc" and the API contains ACLAuthMethodTypeOIDC for
// convenience.
Type string
// Defines whether the auth-method creates a local or global token when
// performing SSO login. This should be set to either "local" or "global"
// and the API contains ACLAuthMethodTokenLocalityLocal and
// ACLAuthMethodTokenLocalityGlobal for convenience.
TokenLocality string
// MaxTokenTTL is the maximum life of a token created by this method.
MaxTokenTTL time.Duration
// Default identifies whether this is the default auth-method to use when
// attempting to login without specifying an auth-method name to use.
Default bool
// Config contains the detailed configuration which is specific to the
// auth-method.
Config *ACLAuthMethodConfig
CreateTime time.Time
ModifyTime time.Time
CreateIndex uint64
ModifyIndex uint64
}
// MarshalJSON implements the json.Marshaler interface and allows
// ACLAuthMethod.MaxTokenTTL to be marshaled correctly.
func (m *ACLAuthMethod) MarshalJSON() ([]byte, error) {
type Alias ACLAuthMethod
exported := &struct {
MaxTokenTTL string
*Alias
}{
MaxTokenTTL: m.MaxTokenTTL.String(),
Alias: (*Alias)(m),
}
if m.MaxTokenTTL == 0 {
exported.MaxTokenTTL = ""
}
return json.Marshal(exported)
}
// UnmarshalJSON implements the json.Unmarshaler interface and allows
// ACLAuthMethod.MaxTokenTTL to be unmarshalled correctly.
func (m *ACLAuthMethod) UnmarshalJSON(data []byte) error {
type Alias ACLAuthMethod
aux := &struct {
MaxTokenTTL string
*Alias
}{
Alias: (*Alias)(m),
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
var err error
if aux.MaxTokenTTL != "" {
if m.MaxTokenTTL, err = time.ParseDuration(aux.MaxTokenTTL); err != nil {
return err
}
}
return nil
}
// ACLAuthMethodConfig is used to store configuration of an auth method.
type ACLAuthMethodConfig struct {
// A list of PEM-encoded public keys to use to authenticate signatures
// locally
JWTValidationPubKeys []string
// JSON Web Key Sets url for authenticating signatures
JWKSURL string
// The OIDC Discovery URL, without any .well-known component (base path)
OIDCDiscoveryURL string
// The OAuth Client ID configured with the OIDC provider
OIDCClientID string
// The OAuth Client Secret configured with the OIDC provider
OIDCClientSecret string
// List of OIDC scopes
OIDCScopes []string
// List of auth claims that are valid for login
BoundAudiences []string
// The value against which to match the iss claim in a JWT
BoundIssuer []string
// A list of allowed values for redirect_uri
AllowedRedirectURIs []string
// PEM encoded CA certs for use by the TLS client used to talk with the
// OIDC Discovery URL.
DiscoveryCaPem []string
// PEM encoded CA cert for use by the TLS client used to talk with the JWKS
// URL
JWKSCACert string
// A list of supported signing algorithms
SigningAlgs []string
// Duration in seconds of leeway when validating expiration of a token to
// account for clock skew
ExpirationLeeway time.Duration
// Duration in seconds of leeway when validating not before values of a
// token to account for clock skew.
NotBeforeLeeway time.Duration
// Duration in seconds of leeway when validating all claims to account for
// clock skew.
ClockSkewLeeway time.Duration
// Mappings of claims (key) that will be copied to a metadata field
// (value).
ClaimMappings map[string]string
ListClaimMappings map[string]string
}
// MarshalJSON implements the json.Marshaler interface and allows
// time.Duration fields to be marshaled correctly.
func (c *ACLAuthMethodConfig) MarshalJSON() ([]byte, error) {
type Alias ACLAuthMethodConfig
exported := &struct {
ExpirationLeeway string
NotBeforeLeeway string
ClockSkewLeeway string
*Alias
}{
ExpirationLeeway: c.ExpirationLeeway.String(),
NotBeforeLeeway: c.NotBeforeLeeway.String(),
ClockSkewLeeway: c.ClockSkewLeeway.String(),
Alias: (*Alias)(c),
}
if c.ExpirationLeeway == 0 {
exported.ExpirationLeeway = ""
}
if c.NotBeforeLeeway == 0 {
exported.NotBeforeLeeway = ""
}
if c.ClockSkewLeeway == 0 {
exported.ClockSkewLeeway = ""
}
return json.Marshal(exported)
}
// UnmarshalJSON implements the json.Unmarshaler interface and allows
// time.Duration fields to be unmarshalled correctly.
func (c *ACLAuthMethodConfig) UnmarshalJSON(data []byte) error {
type Alias ACLAuthMethodConfig
aux := &struct {
ExpirationLeeway any
NotBeforeLeeway any
ClockSkewLeeway any
*Alias
}{
Alias: (*Alias)(c),
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
var err error
if aux.ExpirationLeeway != nil {
switch v := aux.ExpirationLeeway.(type) {
case string:
if v != "" {
if c.ExpirationLeeway, err = time.ParseDuration(v); err != nil {
return err
}
}
case float64:
c.ExpirationLeeway = time.Duration(v)
default:
return fmt.Errorf("unexpected ExpirationLeeway type: %v", v)
}
}
if aux.NotBeforeLeeway != nil {
switch v := aux.NotBeforeLeeway.(type) {
case string:
if v != "" {
if c.NotBeforeLeeway, err = time.ParseDuration(v); err != nil {
return err
}
}
case float64:
c.NotBeforeLeeway = time.Duration(v)
default:
return fmt.Errorf("unexpected NotBeforeLeeway type: %v", v)
}
}
if aux.ClockSkewLeeway != nil {
switch v := aux.ClockSkewLeeway.(type) {
case string:
if v != "" {
if c.ClockSkewLeeway, err = time.ParseDuration(v); err != nil {
return err
}
}
case float64:
c.ClockSkewLeeway = time.Duration(v)
default:
return fmt.Errorf("unexpected ClockSkewLeeway type: %v", v)
}
}
return nil
}
// ACLAuthMethodListStub is the stub object returned when performing a listing
// of ACL auth-methods. It is intentionally minimal due to the unauthenticated
// nature of the list endpoint.
type ACLAuthMethodListStub struct {
Name string
Type string
Default bool
CreateIndex uint64
ModifyIndex uint64
}
const (
// ACLAuthMethodTokenLocalityLocal is the ACLAuthMethod.TokenLocality that
// will generate ACL tokens which can only be used on the local cluster the
// request was made.
ACLAuthMethodTokenLocalityLocal = "local"
// ACLAuthMethodTokenLocalityGlobal is the ACLAuthMethod.TokenLocality that
// will generate ACL tokens which can be used on all federated clusters.
ACLAuthMethodTokenLocalityGlobal = "global"
// ACLAuthMethodTypeOIDC the ACLAuthMethod.Type and represents an
// auth-method which uses the OIDC protocol.
ACLAuthMethodTypeOIDC = "OIDC"
// ACLAuthMethodTypeJWT the ACLAuthMethod.Type and represents an auth-method
// which uses the JWT type.
ACLAuthMethodTypeJWT = "JWT"
)
// ACLBindingRule contains a direct relation to an ACLAuthMethod and represents
// a rule to apply when logging in via the named AuthMethod. This allows the
// transformation of OIDC provider claims, to Nomad based ACL concepts such as
// ACL Roles and Policies.
type ACLBindingRule struct {
// ID is an internally generated UUID for this rule and is controlled by
// Nomad.
ID string
// Description is a human-readable, operator set description that can
// provide additional context about the binding rule. This is an
// operational field.
Description string
// AuthMethod is the name of the auth method for which this rule applies
// to. This is required and the method must exist within state before the
// cluster administrator can create the rule.
AuthMethod string
// Selector is an expression that matches against verified identity
// attributes returned from the auth method during login. This is optional
// and when not set, provides a catch-all rule.
Selector string
// BindType adjusts how this binding rule is applied at login time. The
// valid values are ACLBindingRuleBindTypeRole,
// ACLBindingRuleBindTypePolicy, and ACLBindingRuleBindTypeManagement.
BindType string
// BindName is the target of the binding. Can be lightly templated using
// HIL ${foo} syntax from available field names. How it is used depends
// upon the BindType.
BindName string
CreateTime time.Time
ModifyTime time.Time
CreateIndex uint64
ModifyIndex uint64
}
const (
// ACLBindingRuleBindTypeRole is the ACL binding rule bind type that only
// allows the binding rule to function if a role exists at login-time. The
// role will be specified within the ACLBindingRule.BindName parameter, and
// will identify whether this is an ID or Name.
ACLBindingRuleBindTypeRole = "role"
// ACLBindingRuleBindTypePolicy is the ACL binding rule bind type that
// assigns a policy to the generate ACL token. The role will be specified
// within the ACLBindingRule.BindName parameter, and will be the policy
// name.
ACLBindingRuleBindTypePolicy = "policy"
// ACLBindingRuleBindTypeManagement is the ACL binding rule bind type that
// will generate management ACL tokens when matched.
ACLBindingRuleBindTypeManagement = "management"
)
// ACLBindingRuleListStub is the stub object returned when performing a listing
// of ACL binding rules.
type ACLBindingRuleListStub struct {
// ID is an internally generated UUID for this role and is controlled by
// Nomad.
ID string
// Description is a human-readable, operator set description that can
// provide additional context about the binding role. This is an
// operational field.
Description string
// AuthMethod is the name of the auth method for which this rule applies
// to. This is required and the method must exist within state before the
// cluster administrator can create the rule.
AuthMethod string
CreateIndex uint64
ModifyIndex uint64
}
// ACLOIDCAuthURLRequest is the request to make when starting the OIDC
// authentication login flow.
type ACLOIDCAuthURLRequest struct {
// AuthMethodName is the OIDC auth-method to use. This is a required
// parameter.
AuthMethodName string
// RedirectURI is the URL that authorization should redirect to. This is a
// required parameter.
RedirectURI string
// ClientNonce is a randomly generated string to prevent replay attacks. It
// is up to the client to generate this and Go integrations should use the
// oidc.NewID function within the hashicorp/cap library.
ClientNonce string
}
// ACLOIDCAuthURLResponse is the response when starting the OIDC authentication
// login flow.
type ACLOIDCAuthURLResponse struct {
// AuthURL is URL to begin authorization and is where the user logging in
// should go.
AuthURL string
}
// ACLOIDCCompleteAuthRequest is the request object to begin completing the
// OIDC auth cycle after receiving the callback from the OIDC provider.
type ACLOIDCCompleteAuthRequest struct {
// AuthMethodName is the name of the auth method being used to login via
// OIDC. This will match AuthUrlArgs.AuthMethodName. This is a required
// parameter.
AuthMethodName string
// ClientNonce, State, and Code are provided from the parameters given to
// the redirect URL. These are all required parameters.
ClientNonce string
State string
Code string
// RedirectURI is the URL that authorization should redirect to. This is a
// required parameter.
RedirectURI string
}
// ACLLoginRequest is the request object to begin auth with an external bearer
// token provider.
type ACLLoginRequest struct {
// AuthMethodName is the name of the auth method being used to login. This
// is a required parameter.
AuthMethodName string
// LoginToken is the token used to login. This is a required parameter.
LoginToken string
}