1cf6a435dc
* A GET of the /acl/auth-method/:name endpoint returns the fields MaxTokenTTL and TokenLocality, while a LIST (/acl/auth-methods) does not. The list command returns a filtered subset of the full set. This is somewhat deliberate, so that secrets aren't shown, but the TTL and Locality fields aren't (IMO) security critical, and it is useful for the front end to be able to show them. For consistency these changes mirror the 'omit empty' and string representation choices made for the GET call. This includes changes to the gRPC and API code in the client. The new output looks similar to this curl 'http://localhost:8500/v1/acl/auth-methods' | jq '.' { "MaxTokenTTL": "8m20s", "Name": "minikube-ttl-local2", "Type": "kubernetes", "Description": "minikube auth method", "TokenLocality": "local", "CreateIndex": 530, "ModifyIndex": 530, "Namespace": "default" } ] Signed-off-by: Mark Anderson <manderson@hashicorp.com> * Add changelog Signed-off-by: Mark Anderson <manderson@hashicorp.com>
1409 lines
38 KiB
Go
1409 lines
38 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"net/url"
|
|
"time"
|
|
|
|
"github.com/mitchellh/mapstructure"
|
|
)
|
|
|
|
const (
|
|
// ACLClientType is the client type token
|
|
ACLClientType = "client"
|
|
|
|
// ACLManagementType is the management type token
|
|
ACLManagementType = "management"
|
|
)
|
|
|
|
type ACLLink struct {
|
|
ID string
|
|
Name string
|
|
}
|
|
|
|
type ACLTokenPolicyLink = ACLLink
|
|
type ACLTokenRoleLink = ACLLink
|
|
|
|
// ACLToken represents an ACL Token
|
|
type ACLToken struct {
|
|
CreateIndex uint64
|
|
ModifyIndex uint64
|
|
AccessorID string
|
|
SecretID string
|
|
Description string
|
|
Policies []*ACLTokenPolicyLink `json:",omitempty"`
|
|
Roles []*ACLTokenRoleLink `json:",omitempty"`
|
|
ServiceIdentities []*ACLServiceIdentity `json:",omitempty"`
|
|
NodeIdentities []*ACLNodeIdentity `json:",omitempty"`
|
|
Local bool
|
|
AuthMethod string `json:",omitempty"`
|
|
ExpirationTTL time.Duration `json:",omitempty"`
|
|
ExpirationTime *time.Time `json:",omitempty"`
|
|
CreateTime time.Time `json:",omitempty"`
|
|
Hash []byte `json:",omitempty"`
|
|
|
|
// DEPRECATED (ACL-Legacy-Compat)
|
|
// Rules will only be present for legacy tokens returned via the new APIs
|
|
Rules string `json:",omitempty"`
|
|
|
|
// Namespace is the namespace the ACLToken is associated with.
|
|
// Namespaces are a Consul Enterprise feature.
|
|
Namespace string `json:",omitempty"`
|
|
}
|
|
|
|
type ACLTokenListEntry struct {
|
|
CreateIndex uint64
|
|
ModifyIndex uint64
|
|
AccessorID string
|
|
Description string
|
|
Policies []*ACLTokenPolicyLink `json:",omitempty"`
|
|
Roles []*ACLTokenRoleLink `json:",omitempty"`
|
|
ServiceIdentities []*ACLServiceIdentity `json:",omitempty"`
|
|
NodeIdentities []*ACLNodeIdentity `json:",omitempty"`
|
|
Local bool
|
|
AuthMethod string `json:",omitempty"`
|
|
ExpirationTime *time.Time `json:",omitempty"`
|
|
CreateTime time.Time
|
|
Hash []byte
|
|
Legacy bool
|
|
|
|
// Namespace is the namespace the ACLTokenListEntry is associated with.
|
|
// Namespacing is a Consul Enterprise feature.
|
|
Namespace string `json:",omitempty"`
|
|
}
|
|
|
|
// ACLEntry is used to represent a legacy ACL token
|
|
// The legacy tokens are deprecated.
|
|
type ACLEntry struct {
|
|
CreateIndex uint64
|
|
ModifyIndex uint64
|
|
ID string
|
|
Name string
|
|
Type string
|
|
Rules string
|
|
}
|
|
|
|
// ACLReplicationStatus is used to represent the status of ACL replication.
|
|
type ACLReplicationStatus struct {
|
|
Enabled bool
|
|
Running bool
|
|
SourceDatacenter string
|
|
ReplicationType string
|
|
ReplicatedIndex uint64
|
|
ReplicatedRoleIndex uint64
|
|
ReplicatedTokenIndex uint64
|
|
LastSuccess time.Time
|
|
LastError time.Time
|
|
}
|
|
|
|
// ACLServiceIdentity represents a high-level grant of all necessary privileges
|
|
// to assume the identity of the named Service in the Catalog and within
|
|
// Connect.
|
|
type ACLServiceIdentity struct {
|
|
ServiceName string
|
|
Datacenters []string `json:",omitempty"`
|
|
}
|
|
|
|
// ACLNodeIdentity represents a high-level grant of all necessary privileges
|
|
// to assume the identity of the named Node in the Catalog and within Connect.
|
|
type ACLNodeIdentity struct {
|
|
NodeName string
|
|
Datacenter string
|
|
}
|
|
|
|
// ACLPolicy represents an ACL Policy.
|
|
type ACLPolicy struct {
|
|
ID string
|
|
Name string
|
|
Description string
|
|
Rules string
|
|
Datacenters []string
|
|
Hash []byte
|
|
CreateIndex uint64
|
|
ModifyIndex uint64
|
|
|
|
// Namespace is the namespace the ACLPolicy is associated with.
|
|
// Namespacing is a Consul Enterprise feature.
|
|
Namespace string `json:",omitempty"`
|
|
}
|
|
|
|
type ACLPolicyListEntry struct {
|
|
ID string
|
|
Name string
|
|
Description string
|
|
Datacenters []string
|
|
Hash []byte
|
|
CreateIndex uint64
|
|
ModifyIndex uint64
|
|
|
|
// Namespace is the namespace the ACLPolicyListEntry is associated with.
|
|
// Namespacing is a Consul Enterprise feature.
|
|
Namespace string `json:",omitempty"`
|
|
}
|
|
|
|
type ACLRolePolicyLink = ACLLink
|
|
|
|
// ACLRole represents an ACL Role.
|
|
type ACLRole struct {
|
|
ID string
|
|
Name string
|
|
Description string
|
|
Policies []*ACLRolePolicyLink `json:",omitempty"`
|
|
ServiceIdentities []*ACLServiceIdentity `json:",omitempty"`
|
|
NodeIdentities []*ACLNodeIdentity `json:",omitempty"`
|
|
Hash []byte
|
|
CreateIndex uint64
|
|
ModifyIndex uint64
|
|
|
|
// Namespace is the namespace the ACLRole is associated with.
|
|
// Namespacing is a Consul Enterprise feature.
|
|
Namespace string `json:",omitempty"`
|
|
}
|
|
|
|
// BindingRuleBindType is the type of binding rule mechanism used.
|
|
type BindingRuleBindType string
|
|
|
|
const (
|
|
// BindingRuleBindTypeService binds to a service identity with the given name.
|
|
BindingRuleBindTypeService BindingRuleBindType = "service"
|
|
|
|
// BindingRuleBindTypeRole binds to pre-existing roles with the given name.
|
|
BindingRuleBindTypeRole BindingRuleBindType = "role"
|
|
)
|
|
|
|
type ACLBindingRule struct {
|
|
ID string
|
|
Description string
|
|
AuthMethod string
|
|
Selector string
|
|
BindType BindingRuleBindType
|
|
BindName string
|
|
|
|
CreateIndex uint64
|
|
ModifyIndex uint64
|
|
|
|
// Namespace is the namespace the ACLBindingRule is associated with.
|
|
// Namespacing is a Consul Enterprise feature.
|
|
Namespace string `json:",omitempty"`
|
|
}
|
|
|
|
type ACLAuthMethod struct {
|
|
Name string
|
|
Type string
|
|
DisplayName string `json:",omitempty"`
|
|
Description string `json:",omitempty"`
|
|
MaxTokenTTL time.Duration `json:",omitempty"`
|
|
|
|
// TokenLocality defines the kind of token that this auth method produces.
|
|
// This can be either 'local' or 'global'. If empty 'local' is assumed.
|
|
TokenLocality string `json:",omitempty"`
|
|
|
|
// Configuration is arbitrary configuration for the auth method. This
|
|
// should only contain primitive values and containers (such as lists and
|
|
// maps).
|
|
Config map[string]interface{}
|
|
|
|
CreateIndex uint64
|
|
ModifyIndex uint64
|
|
|
|
// NamespaceRules apply only on auth methods defined in the default namespace.
|
|
// Namespacing is a Consul Enterprise feature.
|
|
NamespaceRules []*ACLAuthMethodNamespaceRule `json:",omitempty"`
|
|
|
|
// Namespace is the namespace the ACLAuthMethod is associated with.
|
|
// Namespacing is a Consul Enterprise feature.
|
|
Namespace string `json:",omitempty"`
|
|
}
|
|
|
|
func (m *ACLAuthMethod) MarshalJSON() ([]byte, error) {
|
|
type Alias ACLAuthMethod
|
|
exported := &struct {
|
|
MaxTokenTTL string `json:",omitempty"`
|
|
*Alias
|
|
}{
|
|
MaxTokenTTL: m.MaxTokenTTL.String(),
|
|
Alias: (*Alias)(m),
|
|
}
|
|
if m.MaxTokenTTL == 0 {
|
|
exported.MaxTokenTTL = ""
|
|
}
|
|
|
|
return json.Marshal(exported)
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
type ACLAuthMethodNamespaceRule struct {
|
|
// Selector is an expression that matches against verified identity
|
|
// attributes returned from the auth method during login.
|
|
Selector string `json:",omitempty"`
|
|
|
|
// BindNamespace is the target namespace of the binding. Can be lightly
|
|
// templated using HIL ${foo} syntax from available field names.
|
|
//
|
|
// If empty it's created in the same namespace as the auth method.
|
|
BindNamespace string `json:",omitempty"`
|
|
}
|
|
|
|
type ACLAuthMethodListEntry struct {
|
|
Name string
|
|
Type string
|
|
DisplayName string `json:",omitempty"`
|
|
Description string `json:",omitempty"`
|
|
MaxTokenTTL time.Duration `json:",omitempty"`
|
|
|
|
// TokenLocality defines the kind of token that this auth method produces.
|
|
// This can be either 'local' or 'global'. If empty 'local' is assumed.
|
|
TokenLocality string `json:",omitempty"`
|
|
CreateIndex uint64
|
|
ModifyIndex uint64
|
|
|
|
// Namespace is the namespace the ACLAuthMethodListEntry is associated with.
|
|
// Namespacing is a Consul Enterprise feature.
|
|
Namespace string `json:",omitempty"`
|
|
}
|
|
|
|
// This is nearly identical to the ACLAuthMethod MarshalJSON
|
|
func (m *ACLAuthMethodListEntry) MarshalJSON() ([]byte, error) {
|
|
type Alias ACLAuthMethodListEntry
|
|
exported := &struct {
|
|
MaxTokenTTL string `json:",omitempty"`
|
|
*Alias
|
|
}{
|
|
MaxTokenTTL: m.MaxTokenTTL.String(),
|
|
Alias: (*Alias)(m),
|
|
}
|
|
if m.MaxTokenTTL == 0 {
|
|
exported.MaxTokenTTL = ""
|
|
}
|
|
|
|
return json.Marshal(exported)
|
|
}
|
|
|
|
// This is nearly identical to the ACLAuthMethod UnmarshalJSON
|
|
func (m *ACLAuthMethodListEntry) UnmarshalJSON(data []byte) error {
|
|
type Alias ACLAuthMethodListEntry
|
|
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
|
|
}
|
|
|
|
// ParseKubernetesAuthMethodConfig takes a raw config map and returns a parsed
|
|
// KubernetesAuthMethodConfig.
|
|
func ParseKubernetesAuthMethodConfig(raw map[string]interface{}) (*KubernetesAuthMethodConfig, error) {
|
|
var config KubernetesAuthMethodConfig
|
|
decodeConf := &mapstructure.DecoderConfig{
|
|
Result: &config,
|
|
WeaklyTypedInput: true,
|
|
}
|
|
|
|
decoder, err := mapstructure.NewDecoder(decodeConf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := decoder.Decode(raw); err != nil {
|
|
return nil, fmt.Errorf("error decoding config: %s", err)
|
|
}
|
|
|
|
return &config, nil
|
|
}
|
|
|
|
// KubernetesAuthMethodConfig is the config for the built-in Consul auth method
|
|
// for Kubernetes.
|
|
type KubernetesAuthMethodConfig struct {
|
|
Host string `json:",omitempty"`
|
|
CACert string `json:",omitempty"`
|
|
ServiceAccountJWT string `json:",omitempty"`
|
|
}
|
|
|
|
// RenderToConfig converts this into a map[string]interface{} suitable for use
|
|
// in the ACLAuthMethod.Config field.
|
|
func (c *KubernetesAuthMethodConfig) RenderToConfig() map[string]interface{} {
|
|
return map[string]interface{}{
|
|
"Host": c.Host,
|
|
"CACert": c.CACert,
|
|
"ServiceAccountJWT": c.ServiceAccountJWT,
|
|
}
|
|
}
|
|
|
|
// OIDCAuthMethodConfig is the config for the built-in Consul auth method for
|
|
// OIDC and JWT.
|
|
type OIDCAuthMethodConfig struct {
|
|
// common for type=oidc and type=jwt
|
|
JWTSupportedAlgs []string `json:",omitempty"`
|
|
BoundAudiences []string `json:",omitempty"`
|
|
ClaimMappings map[string]string `json:",omitempty"`
|
|
ListClaimMappings map[string]string `json:",omitempty"`
|
|
OIDCDiscoveryURL string `json:",omitempty"`
|
|
OIDCDiscoveryCACert string `json:",omitempty"`
|
|
// just for type=oidc
|
|
OIDCClientID string `json:",omitempty"`
|
|
OIDCClientSecret string `json:",omitempty"`
|
|
OIDCScopes []string `json:",omitempty"`
|
|
AllowedRedirectURIs []string `json:",omitempty"`
|
|
VerboseOIDCLogging bool `json:",omitempty"`
|
|
// just for type=jwt
|
|
JWKSURL string `json:",omitempty"`
|
|
JWKSCACert string `json:",omitempty"`
|
|
JWTValidationPubKeys []string `json:",omitempty"`
|
|
BoundIssuer string `json:",omitempty"`
|
|
ExpirationLeeway time.Duration `json:",omitempty"`
|
|
NotBeforeLeeway time.Duration `json:",omitempty"`
|
|
ClockSkewLeeway time.Duration `json:",omitempty"`
|
|
}
|
|
|
|
// RenderToConfig converts this into a map[string]interface{} suitable for use
|
|
// in the ACLAuthMethod.Config field.
|
|
func (c *OIDCAuthMethodConfig) RenderToConfig() map[string]interface{} {
|
|
return map[string]interface{}{
|
|
// common for type=oidc and type=jwt
|
|
"JWTSupportedAlgs": c.JWTSupportedAlgs,
|
|
"BoundAudiences": c.BoundAudiences,
|
|
"ClaimMappings": c.ClaimMappings,
|
|
"ListClaimMappings": c.ListClaimMappings,
|
|
"OIDCDiscoveryURL": c.OIDCDiscoveryURL,
|
|
"OIDCDiscoveryCACert": c.OIDCDiscoveryCACert,
|
|
// just for type=oidc
|
|
"OIDCClientID": c.OIDCClientID,
|
|
"OIDCClientSecret": c.OIDCClientSecret,
|
|
"OIDCScopes": c.OIDCScopes,
|
|
"AllowedRedirectURIs": c.AllowedRedirectURIs,
|
|
"VerboseOIDCLogging": c.VerboseOIDCLogging,
|
|
// just for type=jwt
|
|
"JWKSURL": c.JWKSURL,
|
|
"JWKSCACert": c.JWKSCACert,
|
|
"JWTValidationPubKeys": c.JWTValidationPubKeys,
|
|
"BoundIssuer": c.BoundIssuer,
|
|
"ExpirationLeeway": c.ExpirationLeeway,
|
|
"NotBeforeLeeway": c.NotBeforeLeeway,
|
|
"ClockSkewLeeway": c.ClockSkewLeeway,
|
|
}
|
|
}
|
|
|
|
type ACLLoginParams struct {
|
|
AuthMethod string
|
|
BearerToken string
|
|
Meta map[string]string `json:",omitempty"`
|
|
}
|
|
|
|
type ACLOIDCAuthURLParams struct {
|
|
AuthMethod string
|
|
RedirectURI string
|
|
ClientNonce string
|
|
Meta map[string]string `json:",omitempty"`
|
|
}
|
|
|
|
// ACL can be used to query the ACL endpoints
|
|
type ACL struct {
|
|
c *Client
|
|
}
|
|
|
|
// ACL returns a handle to the ACL endpoints
|
|
func (c *Client) ACL() *ACL {
|
|
return &ACL{c}
|
|
}
|
|
|
|
// Bootstrap is used to perform a one-time ACL bootstrap operation on a cluster
|
|
// to get the first management token.
|
|
func (a *ACL) Bootstrap() (*ACLToken, *WriteMeta, error) {
|
|
r := a.c.newRequest("PUT", "/v1/acl/bootstrap")
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
wm := &WriteMeta{RequestTime: rtt}
|
|
var out ACLToken
|
|
if err := decodeBody(resp, &out); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return &out, wm, nil
|
|
}
|
|
|
|
// Create is used to generate a new token with the given parameters
|
|
//
|
|
// Deprecated: Use TokenCreate instead.
|
|
func (a *ACL) Create(acl *ACLEntry, q *WriteOptions) (string, *WriteMeta, error) {
|
|
r := a.c.newRequest("PUT", "/v1/acl/create")
|
|
r.setWriteOptions(q)
|
|
r.obj = acl
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
wm := &WriteMeta{RequestTime: rtt}
|
|
var out struct{ ID string }
|
|
if err := decodeBody(resp, &out); err != nil {
|
|
return "", nil, err
|
|
}
|
|
return out.ID, wm, nil
|
|
}
|
|
|
|
// Update is used to update the rules of an existing token
|
|
//
|
|
// Deprecated: Use TokenUpdate instead.
|
|
func (a *ACL) Update(acl *ACLEntry, q *WriteOptions) (*WriteMeta, error) {
|
|
r := a.c.newRequest("PUT", "/v1/acl/update")
|
|
r.setWriteOptions(q)
|
|
r.obj = acl
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
wm := &WriteMeta{RequestTime: rtt}
|
|
return wm, nil
|
|
}
|
|
|
|
// Destroy is used to destroy a given ACL token ID
|
|
//
|
|
// Deprecated: Use TokenDelete instead.
|
|
func (a *ACL) Destroy(id string, q *WriteOptions) (*WriteMeta, error) {
|
|
r := a.c.newRequest("PUT", "/v1/acl/destroy/"+id)
|
|
r.setWriteOptions(q)
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resp.Body.Close()
|
|
|
|
wm := &WriteMeta{RequestTime: rtt}
|
|
return wm, nil
|
|
}
|
|
|
|
// Clone is used to return a new token cloned from an existing one
|
|
//
|
|
// Deprecated: Use TokenClone instead.
|
|
func (a *ACL) Clone(id string, q *WriteOptions) (string, *WriteMeta, error) {
|
|
r := a.c.newRequest("PUT", "/v1/acl/clone/"+id)
|
|
r.setWriteOptions(q)
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
wm := &WriteMeta{RequestTime: rtt}
|
|
var out struct{ ID string }
|
|
if err := decodeBody(resp, &out); err != nil {
|
|
return "", nil, err
|
|
}
|
|
return out.ID, wm, nil
|
|
}
|
|
|
|
// Info is used to query for information about an ACL token
|
|
//
|
|
// Deprecated: Use TokenRead instead.
|
|
func (a *ACL) Info(id string, q *QueryOptions) (*ACLEntry, *QueryMeta, error) {
|
|
r := a.c.newRequest("GET", "/v1/acl/info/"+id)
|
|
r.setQueryOptions(q)
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
qm := &QueryMeta{}
|
|
parseQueryMeta(resp, qm)
|
|
qm.RequestTime = rtt
|
|
|
|
var entries []*ACLEntry
|
|
if err := decodeBody(resp, &entries); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
if len(entries) > 0 {
|
|
return entries[0], qm, nil
|
|
}
|
|
return nil, qm, nil
|
|
}
|
|
|
|
// List is used to get all the ACL tokens
|
|
//
|
|
// Deprecated: Use TokenList instead.
|
|
func (a *ACL) List(q *QueryOptions) ([]*ACLEntry, *QueryMeta, error) {
|
|
r := a.c.newRequest("GET", "/v1/acl/list")
|
|
r.setQueryOptions(q)
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
qm := &QueryMeta{}
|
|
parseQueryMeta(resp, qm)
|
|
qm.RequestTime = rtt
|
|
|
|
var entries []*ACLEntry
|
|
if err := decodeBody(resp, &entries); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return entries, qm, nil
|
|
}
|
|
|
|
// Replication returns the status of the ACL replication process in the datacenter
|
|
func (a *ACL) Replication(q *QueryOptions) (*ACLReplicationStatus, *QueryMeta, error) {
|
|
r := a.c.newRequest("GET", "/v1/acl/replication")
|
|
r.setQueryOptions(q)
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
qm := &QueryMeta{}
|
|
parseQueryMeta(resp, qm)
|
|
qm.RequestTime = rtt
|
|
|
|
var entries *ACLReplicationStatus
|
|
if err := decodeBody(resp, &entries); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return entries, qm, nil
|
|
}
|
|
|
|
// TokenCreate creates a new ACL token. If either the AccessorID or SecretID fields
|
|
// of the ACLToken structure are empty they will be filled in by Consul.
|
|
func (a *ACL) TokenCreate(token *ACLToken, q *WriteOptions) (*ACLToken, *WriteMeta, error) {
|
|
r := a.c.newRequest("PUT", "/v1/acl/token")
|
|
r.setWriteOptions(q)
|
|
r.obj = token
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
wm := &WriteMeta{RequestTime: rtt}
|
|
var out ACLToken
|
|
if err := decodeBody(resp, &out); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return &out, wm, nil
|
|
}
|
|
|
|
// TokenUpdate updates a token in place without modifying its AccessorID or SecretID. A valid
|
|
// AccessorID must be set in the ACLToken structure passed to this function but the SecretID may
|
|
// be omitted and will be filled in by Consul with its existing value.
|
|
func (a *ACL) TokenUpdate(token *ACLToken, q *WriteOptions) (*ACLToken, *WriteMeta, error) {
|
|
if token.AccessorID == "" {
|
|
return nil, nil, fmt.Errorf("Must specify an AccessorID for Token Updating")
|
|
}
|
|
r := a.c.newRequest("PUT", "/v1/acl/token/"+token.AccessorID)
|
|
r.setWriteOptions(q)
|
|
r.obj = token
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
wm := &WriteMeta{RequestTime: rtt}
|
|
var out ACLToken
|
|
if err := decodeBody(resp, &out); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return &out, wm, nil
|
|
}
|
|
|
|
// TokenClone will create a new token with the same policies and locality as the original
|
|
// token but will have its own auto-generated AccessorID and SecretID as well having the
|
|
// description passed to this function. The tokenID parameter must be a valid Accessor ID
|
|
// of an existing token.
|
|
func (a *ACL) TokenClone(tokenID string, description string, q *WriteOptions) (*ACLToken, *WriteMeta, error) {
|
|
if tokenID == "" {
|
|
return nil, nil, fmt.Errorf("Must specify a tokenID for Token Cloning")
|
|
}
|
|
|
|
r := a.c.newRequest("PUT", "/v1/acl/token/"+tokenID+"/clone")
|
|
r.setWriteOptions(q)
|
|
r.obj = struct{ Description string }{description}
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
wm := &WriteMeta{RequestTime: rtt}
|
|
var out ACLToken
|
|
if err := decodeBody(resp, &out); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return &out, wm, nil
|
|
}
|
|
|
|
// TokenDelete removes a single ACL token. The tokenID parameter must be a valid
|
|
// Accessor ID of an existing token.
|
|
func (a *ACL) TokenDelete(tokenID string, q *WriteOptions) (*WriteMeta, error) {
|
|
r := a.c.newRequest("DELETE", "/v1/acl/token/"+tokenID)
|
|
r.setWriteOptions(q)
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resp.Body.Close()
|
|
|
|
wm := &WriteMeta{RequestTime: rtt}
|
|
return wm, nil
|
|
}
|
|
|
|
// TokenRead retrieves the full token details. The tokenID parameter must be a valid
|
|
// Accessor ID of an existing token.
|
|
func (a *ACL) TokenRead(tokenID string, q *QueryOptions) (*ACLToken, *QueryMeta, error) {
|
|
r := a.c.newRequest("GET", "/v1/acl/token/"+tokenID)
|
|
r.setQueryOptions(q)
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
qm := &QueryMeta{}
|
|
parseQueryMeta(resp, qm)
|
|
qm.RequestTime = rtt
|
|
|
|
var out ACLToken
|
|
if err := decodeBody(resp, &out); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return &out, qm, nil
|
|
}
|
|
|
|
// TokenReadSelf retrieves the full token details of the token currently
|
|
// assigned to the API Client. In this manner its possible to read a token
|
|
// by its Secret ID.
|
|
func (a *ACL) TokenReadSelf(q *QueryOptions) (*ACLToken, *QueryMeta, error) {
|
|
r := a.c.newRequest("GET", "/v1/acl/token/self")
|
|
r.setQueryOptions(q)
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
qm := &QueryMeta{}
|
|
parseQueryMeta(resp, qm)
|
|
qm.RequestTime = rtt
|
|
|
|
var out ACLToken
|
|
if err := decodeBody(resp, &out); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return &out, qm, nil
|
|
}
|
|
|
|
// TokenList lists all tokens. The listing does not contain any SecretIDs as those
|
|
// may only be retrieved by a call to TokenRead.
|
|
func (a *ACL) TokenList(q *QueryOptions) ([]*ACLTokenListEntry, *QueryMeta, error) {
|
|
r := a.c.newRequest("GET", "/v1/acl/tokens")
|
|
r.setQueryOptions(q)
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
qm := &QueryMeta{}
|
|
parseQueryMeta(resp, qm)
|
|
qm.RequestTime = rtt
|
|
|
|
var entries []*ACLTokenListEntry
|
|
if err := decodeBody(resp, &entries); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return entries, qm, nil
|
|
}
|
|
|
|
// PolicyCreate will create a new policy. It is not allowed for the policy parameters
|
|
// ID field to be set as this will be generated by Consul while processing the request.
|
|
func (a *ACL) PolicyCreate(policy *ACLPolicy, q *WriteOptions) (*ACLPolicy, *WriteMeta, error) {
|
|
if policy.ID != "" {
|
|
return nil, nil, fmt.Errorf("Cannot specify an ID in Policy Creation")
|
|
}
|
|
r := a.c.newRequest("PUT", "/v1/acl/policy")
|
|
r.setWriteOptions(q)
|
|
r.obj = policy
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
wm := &WriteMeta{RequestTime: rtt}
|
|
var out ACLPolicy
|
|
if err := decodeBody(resp, &out); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return &out, wm, nil
|
|
}
|
|
|
|
// PolicyUpdate updates a policy. The ID field of the policy parameter must be set to an
|
|
// existing policy ID
|
|
func (a *ACL) PolicyUpdate(policy *ACLPolicy, q *WriteOptions) (*ACLPolicy, *WriteMeta, error) {
|
|
if policy.ID == "" {
|
|
return nil, nil, fmt.Errorf("Must specify an ID in Policy Update")
|
|
}
|
|
|
|
r := a.c.newRequest("PUT", "/v1/acl/policy/"+policy.ID)
|
|
r.setWriteOptions(q)
|
|
r.obj = policy
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
wm := &WriteMeta{RequestTime: rtt}
|
|
var out ACLPolicy
|
|
if err := decodeBody(resp, &out); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return &out, wm, nil
|
|
}
|
|
|
|
// PolicyDelete deletes a policy given its ID.
|
|
func (a *ACL) PolicyDelete(policyID string, q *WriteOptions) (*WriteMeta, error) {
|
|
r := a.c.newRequest("DELETE", "/v1/acl/policy/"+policyID)
|
|
r.setWriteOptions(q)
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resp.Body.Close()
|
|
|
|
wm := &WriteMeta{RequestTime: rtt}
|
|
return wm, nil
|
|
}
|
|
|
|
// PolicyRead retrieves the policy details including the rule set.
|
|
func (a *ACL) PolicyRead(policyID string, q *QueryOptions) (*ACLPolicy, *QueryMeta, error) {
|
|
r := a.c.newRequest("GET", "/v1/acl/policy/"+policyID)
|
|
r.setQueryOptions(q)
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
qm := &QueryMeta{}
|
|
parseQueryMeta(resp, qm)
|
|
qm.RequestTime = rtt
|
|
|
|
var out ACLPolicy
|
|
if err := decodeBody(resp, &out); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return &out, qm, nil
|
|
}
|
|
|
|
// PolicyReadByName retrieves the policy details including the rule set with name.
|
|
func (a *ACL) PolicyReadByName(policyName string, q *QueryOptions) (*ACLPolicy, *QueryMeta, error) {
|
|
r := a.c.newRequest("GET", "/v1/acl/policy/name/"+url.QueryEscape(policyName))
|
|
r.setQueryOptions(q)
|
|
found, rtt, resp, err := requireNotFoundOrOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
qm := &QueryMeta{}
|
|
parseQueryMeta(resp, qm)
|
|
qm.RequestTime = rtt
|
|
|
|
if !found {
|
|
return nil, qm, nil
|
|
}
|
|
|
|
var out ACLPolicy
|
|
if err := decodeBody(resp, &out); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return &out, qm, nil
|
|
}
|
|
|
|
// PolicyList retrieves a listing of all policies. The listing does not include the
|
|
// rules for any policy as those should be retrieved by subsequent calls to PolicyRead.
|
|
func (a *ACL) PolicyList(q *QueryOptions) ([]*ACLPolicyListEntry, *QueryMeta, error) {
|
|
r := a.c.newRequest("GET", "/v1/acl/policies")
|
|
r.setQueryOptions(q)
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
qm := &QueryMeta{}
|
|
parseQueryMeta(resp, qm)
|
|
qm.RequestTime = rtt
|
|
|
|
var entries []*ACLPolicyListEntry
|
|
if err := decodeBody(resp, &entries); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return entries, qm, nil
|
|
}
|
|
|
|
// RulesTranslate translates the legacy rule syntax into the current syntax.
|
|
//
|
|
// Deprecated: Support for the legacy syntax translation will be removed
|
|
// when legacy ACL support is removed.
|
|
func (a *ACL) RulesTranslate(rules io.Reader) (string, error) {
|
|
r := a.c.newRequest("POST", "/v1/acl/rules/translate")
|
|
r.body = rules
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer resp.Body.Close()
|
|
qm := &QueryMeta{}
|
|
parseQueryMeta(resp, qm)
|
|
qm.RequestTime = rtt
|
|
|
|
ruleBytes, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return "", fmt.Errorf("Failed to read translated rule body: %v", err)
|
|
}
|
|
|
|
return string(ruleBytes), nil
|
|
}
|
|
|
|
// RulesTranslateToken translates the rules associated with the legacy syntax
|
|
// into the current syntax and returns the results.
|
|
//
|
|
// Deprecated: Support for the legacy syntax translation will be removed
|
|
// when legacy ACL support is removed.
|
|
func (a *ACL) RulesTranslateToken(tokenID string) (string, error) {
|
|
r := a.c.newRequest("GET", "/v1/acl/rules/translate/"+tokenID)
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer resp.Body.Close()
|
|
qm := &QueryMeta{}
|
|
parseQueryMeta(resp, qm)
|
|
qm.RequestTime = rtt
|
|
|
|
ruleBytes, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return "", fmt.Errorf("Failed to read translated rule body: %v", err)
|
|
}
|
|
|
|
return string(ruleBytes), nil
|
|
}
|
|
|
|
// RoleCreate will create a new role. It is not allowed for the role parameters
|
|
// ID field to be set as this will be generated by Consul while processing the request.
|
|
func (a *ACL) RoleCreate(role *ACLRole, q *WriteOptions) (*ACLRole, *WriteMeta, error) {
|
|
if role.ID != "" {
|
|
return nil, nil, fmt.Errorf("Cannot specify an ID in Role Creation")
|
|
}
|
|
|
|
r := a.c.newRequest("PUT", "/v1/acl/role")
|
|
r.setWriteOptions(q)
|
|
r.obj = role
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
wm := &WriteMeta{RequestTime: rtt}
|
|
var out ACLRole
|
|
if err := decodeBody(resp, &out); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return &out, wm, nil
|
|
}
|
|
|
|
// RoleUpdate updates a role. The ID field of the role parameter must be set to an
|
|
// existing role ID
|
|
func (a *ACL) RoleUpdate(role *ACLRole, q *WriteOptions) (*ACLRole, *WriteMeta, error) {
|
|
if role.ID == "" {
|
|
return nil, nil, fmt.Errorf("Must specify an ID in Role Update")
|
|
}
|
|
|
|
r := a.c.newRequest("PUT", "/v1/acl/role/"+role.ID)
|
|
r.setWriteOptions(q)
|
|
r.obj = role
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
wm := &WriteMeta{RequestTime: rtt}
|
|
var out ACLRole
|
|
if err := decodeBody(resp, &out); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return &out, wm, nil
|
|
}
|
|
|
|
// RoleDelete deletes a role given its ID.
|
|
func (a *ACL) RoleDelete(roleID string, q *WriteOptions) (*WriteMeta, error) {
|
|
r := a.c.newRequest("DELETE", "/v1/acl/role/"+roleID)
|
|
r.setWriteOptions(q)
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resp.Body.Close()
|
|
|
|
wm := &WriteMeta{RequestTime: rtt}
|
|
return wm, nil
|
|
}
|
|
|
|
// RoleRead retrieves the role details (by ID). Returns nil if not found.
|
|
func (a *ACL) RoleRead(roleID string, q *QueryOptions) (*ACLRole, *QueryMeta, error) {
|
|
r := a.c.newRequest("GET", "/v1/acl/role/"+roleID)
|
|
r.setQueryOptions(q)
|
|
found, rtt, resp, err := requireNotFoundOrOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
qm := &QueryMeta{}
|
|
parseQueryMeta(resp, qm)
|
|
qm.RequestTime = rtt
|
|
|
|
if !found {
|
|
return nil, qm, nil
|
|
}
|
|
|
|
var out ACLRole
|
|
if err := decodeBody(resp, &out); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return &out, qm, nil
|
|
}
|
|
|
|
// RoleReadByName retrieves the role details (by name). Returns nil if not found.
|
|
func (a *ACL) RoleReadByName(roleName string, q *QueryOptions) (*ACLRole, *QueryMeta, error) {
|
|
r := a.c.newRequest("GET", "/v1/acl/role/name/"+url.QueryEscape(roleName))
|
|
r.setQueryOptions(q)
|
|
found, rtt, resp, err := requireNotFoundOrOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
qm := &QueryMeta{}
|
|
parseQueryMeta(resp, qm)
|
|
qm.RequestTime = rtt
|
|
|
|
if !found {
|
|
return nil, qm, nil
|
|
}
|
|
|
|
var out ACLRole
|
|
if err := decodeBody(resp, &out); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return &out, qm, nil
|
|
}
|
|
|
|
// RoleList retrieves a listing of all roles. The listing does not include some
|
|
// metadata for the role as those should be retrieved by subsequent calls to
|
|
// RoleRead.
|
|
func (a *ACL) RoleList(q *QueryOptions) ([]*ACLRole, *QueryMeta, error) {
|
|
r := a.c.newRequest("GET", "/v1/acl/roles")
|
|
r.setQueryOptions(q)
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
qm := &QueryMeta{}
|
|
parseQueryMeta(resp, qm)
|
|
qm.RequestTime = rtt
|
|
|
|
var entries []*ACLRole
|
|
if err := decodeBody(resp, &entries); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return entries, qm, nil
|
|
}
|
|
|
|
// AuthMethodCreate will create a new auth method.
|
|
func (a *ACL) AuthMethodCreate(method *ACLAuthMethod, q *WriteOptions) (*ACLAuthMethod, *WriteMeta, error) {
|
|
if method.Name == "" {
|
|
return nil, nil, fmt.Errorf("Must specify a Name in Auth Method Creation")
|
|
}
|
|
|
|
r := a.c.newRequest("PUT", "/v1/acl/auth-method")
|
|
r.setWriteOptions(q)
|
|
r.obj = method
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
wm := &WriteMeta{RequestTime: rtt}
|
|
var out ACLAuthMethod
|
|
if err := decodeBody(resp, &out); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return &out, wm, nil
|
|
}
|
|
|
|
// AuthMethodUpdate updates an auth method.
|
|
func (a *ACL) AuthMethodUpdate(method *ACLAuthMethod, q *WriteOptions) (*ACLAuthMethod, *WriteMeta, error) {
|
|
if method.Name == "" {
|
|
return nil, nil, fmt.Errorf("Must specify a Name in Auth Method Update")
|
|
}
|
|
|
|
r := a.c.newRequest("PUT", "/v1/acl/auth-method/"+url.QueryEscape(method.Name))
|
|
r.setWriteOptions(q)
|
|
r.obj = method
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
wm := &WriteMeta{RequestTime: rtt}
|
|
var out ACLAuthMethod
|
|
if err := decodeBody(resp, &out); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return &out, wm, nil
|
|
}
|
|
|
|
// AuthMethodDelete deletes an auth method given its Name.
|
|
func (a *ACL) AuthMethodDelete(methodName string, q *WriteOptions) (*WriteMeta, error) {
|
|
if methodName == "" {
|
|
return nil, fmt.Errorf("Must specify a Name in Auth Method Delete")
|
|
}
|
|
|
|
r := a.c.newRequest("DELETE", "/v1/acl/auth-method/"+url.QueryEscape(methodName))
|
|
r.setWriteOptions(q)
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resp.Body.Close()
|
|
|
|
wm := &WriteMeta{RequestTime: rtt}
|
|
return wm, nil
|
|
}
|
|
|
|
// AuthMethodRead retrieves the auth method. Returns nil if not found.
|
|
func (a *ACL) AuthMethodRead(methodName string, q *QueryOptions) (*ACLAuthMethod, *QueryMeta, error) {
|
|
if methodName == "" {
|
|
return nil, nil, fmt.Errorf("Must specify a Name in Auth Method Read")
|
|
}
|
|
|
|
r := a.c.newRequest("GET", "/v1/acl/auth-method/"+url.QueryEscape(methodName))
|
|
r.setQueryOptions(q)
|
|
found, rtt, resp, err := requireNotFoundOrOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
qm := &QueryMeta{}
|
|
parseQueryMeta(resp, qm)
|
|
qm.RequestTime = rtt
|
|
|
|
if !found {
|
|
return nil, qm, nil
|
|
}
|
|
|
|
var out ACLAuthMethod
|
|
if err := decodeBody(resp, &out); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return &out, qm, nil
|
|
}
|
|
|
|
// AuthMethodList retrieves a listing of all auth methods. The listing does not
|
|
// include some metadata for the auth method as those should be retrieved by
|
|
// subsequent calls to AuthMethodRead.
|
|
func (a *ACL) AuthMethodList(q *QueryOptions) ([]*ACLAuthMethodListEntry, *QueryMeta, error) {
|
|
r := a.c.newRequest("GET", "/v1/acl/auth-methods")
|
|
r.setQueryOptions(q)
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
qm := &QueryMeta{}
|
|
parseQueryMeta(resp, qm)
|
|
qm.RequestTime = rtt
|
|
|
|
var entries []*ACLAuthMethodListEntry
|
|
if err := decodeBody(resp, &entries); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return entries, qm, nil
|
|
}
|
|
|
|
// BindingRuleCreate will create a new binding rule. It is not allowed for the
|
|
// binding rule parameter's ID field to be set as this will be generated by
|
|
// Consul while processing the request.
|
|
func (a *ACL) BindingRuleCreate(rule *ACLBindingRule, q *WriteOptions) (*ACLBindingRule, *WriteMeta, error) {
|
|
if rule.ID != "" {
|
|
return nil, nil, fmt.Errorf("Cannot specify an ID in Binding Rule Creation")
|
|
}
|
|
|
|
r := a.c.newRequest("PUT", "/v1/acl/binding-rule")
|
|
r.setWriteOptions(q)
|
|
r.obj = rule
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
wm := &WriteMeta{RequestTime: rtt}
|
|
var out ACLBindingRule
|
|
if err := decodeBody(resp, &out); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return &out, wm, nil
|
|
}
|
|
|
|
// BindingRuleUpdate updates a binding rule. The ID field of the role binding
|
|
// rule parameter must be set to an existing binding rule ID.
|
|
func (a *ACL) BindingRuleUpdate(rule *ACLBindingRule, q *WriteOptions) (*ACLBindingRule, *WriteMeta, error) {
|
|
if rule.ID == "" {
|
|
return nil, nil, fmt.Errorf("Must specify an ID in Binding Rule Update")
|
|
}
|
|
|
|
r := a.c.newRequest("PUT", "/v1/acl/binding-rule/"+rule.ID)
|
|
r.setWriteOptions(q)
|
|
r.obj = rule
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
wm := &WriteMeta{RequestTime: rtt}
|
|
var out ACLBindingRule
|
|
if err := decodeBody(resp, &out); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return &out, wm, nil
|
|
}
|
|
|
|
// BindingRuleDelete deletes a binding rule given its ID.
|
|
func (a *ACL) BindingRuleDelete(bindingRuleID string, q *WriteOptions) (*WriteMeta, error) {
|
|
r := a.c.newRequest("DELETE", "/v1/acl/binding-rule/"+bindingRuleID)
|
|
r.setWriteOptions(q)
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resp.Body.Close()
|
|
|
|
wm := &WriteMeta{RequestTime: rtt}
|
|
return wm, nil
|
|
}
|
|
|
|
// BindingRuleRead retrieves the binding rule details. Returns nil if not found.
|
|
func (a *ACL) BindingRuleRead(bindingRuleID string, q *QueryOptions) (*ACLBindingRule, *QueryMeta, error) {
|
|
r := a.c.newRequest("GET", "/v1/acl/binding-rule/"+bindingRuleID)
|
|
r.setQueryOptions(q)
|
|
found, rtt, resp, err := requireNotFoundOrOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
qm := &QueryMeta{}
|
|
parseQueryMeta(resp, qm)
|
|
qm.RequestTime = rtt
|
|
|
|
if !found {
|
|
return nil, qm, nil
|
|
}
|
|
|
|
var out ACLBindingRule
|
|
if err := decodeBody(resp, &out); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return &out, qm, nil
|
|
}
|
|
|
|
// BindingRuleList retrieves a listing of all binding rules.
|
|
func (a *ACL) BindingRuleList(methodName string, q *QueryOptions) ([]*ACLBindingRule, *QueryMeta, error) {
|
|
r := a.c.newRequest("GET", "/v1/acl/binding-rules")
|
|
if methodName != "" {
|
|
r.params.Set("authmethod", methodName)
|
|
}
|
|
r.setQueryOptions(q)
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
qm := &QueryMeta{}
|
|
parseQueryMeta(resp, qm)
|
|
qm.RequestTime = rtt
|
|
|
|
var entries []*ACLBindingRule
|
|
if err := decodeBody(resp, &entries); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return entries, qm, nil
|
|
}
|
|
|
|
// Login is used to exchange auth method credentials for a newly-minted Consul Token.
|
|
func (a *ACL) Login(auth *ACLLoginParams, q *WriteOptions) (*ACLToken, *WriteMeta, error) {
|
|
r := a.c.newRequest("POST", "/v1/acl/login")
|
|
r.setWriteOptions(q)
|
|
r.obj = auth
|
|
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
wm := &WriteMeta{RequestTime: rtt}
|
|
var out ACLToken
|
|
if err := decodeBody(resp, &out); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return &out, wm, nil
|
|
}
|
|
|
|
// Logout is used to destroy a Consul Token created via Login().
|
|
func (a *ACL) Logout(q *WriteOptions) (*WriteMeta, error) {
|
|
r := a.c.newRequest("POST", "/v1/acl/logout")
|
|
r.setWriteOptions(q)
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resp.Body.Close()
|
|
|
|
wm := &WriteMeta{RequestTime: rtt}
|
|
return wm, nil
|
|
}
|
|
|
|
// OIDCAuthURL requests an authorization URL to start an OIDC login flow.
|
|
func (a *ACL) OIDCAuthURL(auth *ACLOIDCAuthURLParams, q *WriteOptions) (string, *WriteMeta, error) {
|
|
if auth.AuthMethod == "" {
|
|
return "", nil, fmt.Errorf("Must specify an auth method name")
|
|
}
|
|
|
|
r := a.c.newRequest("POST", "/v1/acl/oidc/auth-url")
|
|
r.setWriteOptions(q)
|
|
r.obj = auth
|
|
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
wm := &WriteMeta{RequestTime: rtt}
|
|
var out aclOIDCAuthURLResponse
|
|
if err := decodeBody(resp, &out); err != nil {
|
|
return "", nil, err
|
|
}
|
|
return out.AuthURL, wm, nil
|
|
}
|
|
|
|
type aclOIDCAuthURLResponse struct {
|
|
AuthURL string
|
|
}
|
|
|
|
type ACLOIDCCallbackParams struct {
|
|
AuthMethod string
|
|
State string
|
|
Code string
|
|
ClientNonce string
|
|
}
|
|
|
|
// OIDCCallback is the callback endpoint to complete an OIDC login.
|
|
func (a *ACL) OIDCCallback(auth *ACLOIDCCallbackParams, q *WriteOptions) (*ACLToken, *WriteMeta, error) {
|
|
if auth.AuthMethod == "" {
|
|
return nil, nil, fmt.Errorf("Must specify an auth method name")
|
|
}
|
|
|
|
r := a.c.newRequest("POST", "/v1/acl/oidc/callback")
|
|
r.setWriteOptions(q)
|
|
r.obj = auth
|
|
|
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
wm := &WriteMeta{RequestTime: rtt}
|
|
var out ACLToken
|
|
if err := decodeBody(resp, &out); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return &out, wm, nil
|
|
}
|