open-nomad/api/acl.go
2022-12-06 14:47:05 +01:00

670 lines
19 KiB
Go

package api
import (
"encoding/json"
"errors"
"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.write("/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.write("/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.write("/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.write("/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.write("/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.write("/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.write("/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")
)
// 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.write("/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.write("/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.write("/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.write("/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
}
// 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
}
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
}
// ACLAuthMethodConfig is used to store configuration of an auth method.
type ACLAuthMethodConfig struct {
OIDCDiscoveryURL string
OIDCClientID string
OIDCClientSecret string
BoundAudiences []string
AllowedRedirectURIs []string
DiscoveryCaPem []string
SigningAlgs []string
ClaimMappings map[string]string
ListClaimMappings map[string]string
}
// 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
}
// 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
Hash []byte
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"
)