mirror of https://github.com/hashicorp/consul
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1230 lines
32 KiB
1230 lines
32 KiB
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"`
|
|
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 is 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"`
|
|
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"`
|
|
}
|
|
|
|
// 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"`
|
|
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"`
|
|
|
|
// 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"`
|
|
CreateIndex uint64
|
|
ModifyIndex uint64
|
|
|
|
// Namespace is the namespace the ACLAuthMethodListEntry is associated with.
|
|
// Namespacing is a Consul Enterprise feature.
|
|
Namespace string `json:",omitempty"`
|
|
}
|
|
|
|
// 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,
|
|
}
|
|
}
|
|
|
|
type ACLLoginParams struct {
|
|
AuthMethod string
|
|
BearerToken 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
|
|
}
|