2017-08-25 00:03:29 +00:00
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
2022-11-28 08:20:24 +00:00
|
|
|
"encoding/json"
|
2022-08-11 07:44:37 +00:00
|
|
|
"errors"
|
2017-08-25 00:03:29 +00:00
|
|
|
"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 == "" {
|
2022-08-05 15:05:47 +00:00
|
|
|
return nil, errors.New("missing policy name")
|
2017-08-25 00:03:29 +00:00
|
|
|
}
|
|
|
|
wm, err := a.client.write("/v1/acl/policy/"+policy.Name, policy, nil, q)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return wm, nil
|
|
|
|
}
|
|
|
|
|
2017-08-25 00:10:10 +00:00
|
|
|
// Delete is used to delete a policy
|
|
|
|
func (a *ACLPolicies) Delete(policyName string, q *WriteOptions) (*WriteMeta, error) {
|
|
|
|
if policyName == "" {
|
2022-08-05 15:05:47 +00:00
|
|
|
return nil, errors.New("missing policy name")
|
2017-08-25 00:10:10 +00:00
|
|
|
}
|
2022-07-06 14:30:11 +00:00
|
|
|
wm, err := a.client.delete("/v1/acl/policy/"+policyName, nil, nil, q)
|
2017-08-25 00:10:10 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return wm, nil
|
|
|
|
}
|
|
|
|
|
2017-08-25 00:12:53 +00:00
|
|
|
// Info is used to query a specific policy
|
|
|
|
func (a *ACLPolicies) Info(policyName string, q *QueryOptions) (*ACLPolicy, *QueryMeta, error) {
|
|
|
|
if policyName == "" {
|
2022-08-05 15:05:47 +00:00
|
|
|
return nil, nil, errors.New("missing policy name")
|
2017-08-25 00:12:53 +00:00
|
|
|
}
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2017-08-25 00:03:29 +00:00
|
|
|
// 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}
|
|
|
|
}
|
|
|
|
|
2022-06-03 11:37:24 +00:00
|
|
|
// DEPRECATED: will be removed in Nomad 1.5.0
|
2017-08-25 00:03:29 +00:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2022-06-03 11:37:24 +00:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2017-08-25 00:15:47 +00:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2017-08-25 00:26:13 +00:00
|
|
|
// Create is used to create a token
|
|
|
|
func (a *ACLTokens) Create(token *ACLToken, q *WriteOptions) (*ACLToken, *WriteMeta, error) {
|
|
|
|
if token.AccessorID != "" {
|
2022-08-05 15:05:47 +00:00
|
|
|
return nil, nil, errors.New("cannot specify Accessor ID")
|
2017-08-25 00:26:13 +00:00
|
|
|
}
|
|
|
|
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 == "" {
|
2022-08-05 15:05:47 +00:00
|
|
|
return nil, nil, errors.New("missing accessor ID")
|
2017-08-25 00:26:13 +00:00
|
|
|
}
|
|
|
|
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 == "" {
|
2022-08-05 15:05:47 +00:00
|
|
|
return nil, errors.New("missing accessor ID")
|
2017-08-25 00:26:13 +00:00
|
|
|
}
|
2022-07-06 14:30:11 +00:00
|
|
|
wm, err := a.client.delete("/v1/acl/token/"+accessorID, nil, nil, q)
|
2017-08-25 00:26:13 +00:00
|
|
|
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 == "" {
|
2022-08-05 15:05:47 +00:00
|
|
|
return nil, nil, errors.New("missing accessor ID")
|
2017-08-25 00:26:13 +00:00
|
|
|
}
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2017-10-13 20:10:26 +00:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2021-02-25 21:41:00 +00:00
|
|
|
// 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 {
|
2022-08-05 15:05:47 +00:00
|
|
|
return nil, nil, errors.New("no one-time token returned")
|
2021-02-25 21:41:00 +00:00
|
|
|
}
|
|
|
|
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 == "" {
|
2022-08-05 15:05:47 +00:00
|
|
|
return nil, nil, errors.New("missing secret ID")
|
2021-02-25 21:41:00 +00:00
|
|
|
}
|
|
|
|
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 {
|
2022-08-05 15:05:47 +00:00
|
|
|
return nil, nil, errors.New("no ACL token returned")
|
2021-02-25 21:41:00 +00:00
|
|
|
}
|
|
|
|
return resp.Token, wm, nil
|
|
|
|
}
|
|
|
|
|
2022-08-11 07:44:37 +00:00
|
|
|
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")
|
2022-11-23 08:38:02 +00:00
|
|
|
|
|
|
|
// 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")
|
2022-12-15 08:18:55 +00:00
|
|
|
|
|
|
|
// errMissingACLBindingRuleID is the generic error to use when a call is
|
|
|
|
// missing the required ACL binding rule ID parameter.
|
|
|
|
errMissingACLBindingRuleID = errors.New("missing ACL binding rule ID")
|
2022-08-11 07:44:37 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// 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.
|
2022-08-22 15:20:23 +00:00
|
|
|
func (a *ACLRoles) List(q *QueryOptions) ([]*ACLRoleListStub, *QueryMeta, error) {
|
|
|
|
var resp []*ACLRoleListStub
|
2022-08-11 07:44:37 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2022-11-23 08:38:02 +00:00
|
|
|
// 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.
|
2022-11-29 06:36:36 +00:00
|
|
|
func (a *ACLAuthMethods) Create(authMethod *ACLAuthMethod, w *WriteOptions) (*ACLAuthMethod, *WriteMeta, error) {
|
2022-11-23 08:38:02 +00:00
|
|
|
if authMethod.Name == "" {
|
2022-11-29 06:36:36 +00:00
|
|
|
return nil, nil, errMissingACLAuthMethodName
|
2022-11-23 08:38:02 +00:00
|
|
|
}
|
2022-11-29 06:36:36 +00:00
|
|
|
var resp ACLAuthMethod
|
|
|
|
wm, err := a.client.write("/v1/acl/auth-method", authMethod, &resp, w)
|
2022-11-23 08:38:02 +00:00
|
|
|
if err != nil {
|
2022-11-29 06:36:36 +00:00
|
|
|
return nil, nil, err
|
2022-11-23 08:38:02 +00:00
|
|
|
}
|
2022-11-29 06:36:36 +00:00
|
|
|
return &resp, wm, nil
|
2022-11-23 08:38:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Update is used to update an existing ACL auth-method.
|
2022-11-29 06:36:36 +00:00
|
|
|
func (a *ACLAuthMethods) Update(authMethod *ACLAuthMethod, w *WriteOptions) (*ACLAuthMethod, *WriteMeta, error) {
|
2022-11-23 08:38:02 +00:00
|
|
|
if authMethod.Name == "" {
|
2022-11-29 06:36:36 +00:00
|
|
|
return nil, nil, errMissingACLAuthMethodName
|
2022-11-23 08:38:02 +00:00
|
|
|
}
|
2022-11-29 06:36:36 +00:00
|
|
|
var resp ACLAuthMethod
|
|
|
|
wm, err := a.client.write("/v1/acl/auth-method/"+authMethod.Name, authMethod, &resp, w)
|
2022-11-23 08:38:02 +00:00
|
|
|
if err != nil {
|
2022-11-29 06:36:36 +00:00
|
|
|
return nil, nil, err
|
2022-11-23 08:38:02 +00:00
|
|
|
}
|
2022-11-29 06:36:36 +00:00
|
|
|
return &resp, wm, nil
|
2022-11-23 08:38:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2022-12-15 08:18:55 +00:00
|
|
|
// ACLBindingRules is used to query the ACL auth-methods endpoints.
|
|
|
|
type ACLBindingRules struct {
|
|
|
|
client *Client
|
|
|
|
}
|
|
|
|
|
|
|
|
// ACLBindingRules returns a new handle on the ACL auth-methods API client.
|
|
|
|
func (c *Client) ACLBindingRules() *ACLBindingRules {
|
|
|
|
return &ACLBindingRules{client: c}
|
|
|
|
}
|
|
|
|
|
|
|
|
// List is used to detail all the ACL binding rules currently stored within
|
|
|
|
// state.
|
|
|
|
func (a *ACLBindingRules) List(q *QueryOptions) ([]*ACLBindingRuleListStub, *QueryMeta, error) {
|
|
|
|
var resp []*ACLBindingRuleListStub
|
|
|
|
qm, err := a.client.query("/v1/acl/binding-rules", &resp, q)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
return resp, qm, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create is used to create an ACL binding rule.
|
|
|
|
func (a *ACLBindingRules) Create(bindingRule *ACLBindingRule, w *WriteOptions) (*ACLBindingRule, *WriteMeta, error) {
|
|
|
|
var resp ACLBindingRule
|
|
|
|
wm, err := a.client.write("/v1/acl/binding-rule", bindingRule, &resp, w)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
return &resp, wm, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update is used to update an existing ACL binding rule.
|
|
|
|
func (a *ACLBindingRules) Update(bindingRule *ACLBindingRule, w *WriteOptions) (*ACLBindingRule, *WriteMeta, error) {
|
|
|
|
if bindingRule.ID == "" {
|
|
|
|
return nil, nil, errMissingACLBindingRuleID
|
|
|
|
}
|
|
|
|
var resp ACLBindingRule
|
|
|
|
wm, err := a.client.write("/v1/acl/binding-rule/"+bindingRule.ID, bindingRule, &resp, w)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
return &resp, wm, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete is used to delete an ACL binding rule.
|
|
|
|
func (a *ACLBindingRules) Delete(bindingRuleID string, w *WriteOptions) (*WriteMeta, error) {
|
|
|
|
if bindingRuleID == "" {
|
|
|
|
return nil, errMissingACLBindingRuleID
|
|
|
|
}
|
|
|
|
wm, err := a.client.delete("/v1/acl/binding-rule/"+bindingRuleID, nil, nil, w)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return wm, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get is used to look up an ACL binding rule.
|
|
|
|
func (a *ACLBindingRules) Get(bindingRuleID string, q *QueryOptions) (*ACLBindingRule, *QueryMeta, error) {
|
|
|
|
if bindingRuleID == "" {
|
|
|
|
return nil, nil, errMissingACLBindingRuleID
|
|
|
|
}
|
|
|
|
var resp ACLBindingRule
|
|
|
|
qm, err := a.client.query("/v1/acl/binding-rule/"+bindingRuleID, &resp, q)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
return &resp, qm, nil
|
|
|
|
}
|
|
|
|
|
2023-01-13 13:15:58 +00:00
|
|
|
// ACLOIDC is used to query the ACL OIDC endpoints.
|
|
|
|
type ACLOIDC struct {
|
|
|
|
client *Client
|
|
|
|
}
|
|
|
|
|
|
|
|
// ACLOIDC returns a new handle on the ACL auth-methods API client.
|
|
|
|
func (c *Client) ACLOIDC() *ACLOIDC {
|
|
|
|
return &ACLOIDC{client: c}
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetAuthURL generates the OIDC provider authentication URL. This URL should
|
|
|
|
// be visited in order to sign in to the provider.
|
|
|
|
func (a *ACLOIDC) GetAuthURL(req *ACLOIDCAuthURLRequest, q *WriteOptions) (*ACLOIDCAuthURLResponse, *WriteMeta, error) {
|
|
|
|
var resp ACLOIDCAuthURLResponse
|
|
|
|
wm, err := a.client.write("/v1/acl/oidc/auth-url", req, &resp, q)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
return &resp, wm, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// CompleteAuth exchanges the OIDC provider token for a Nomad token with the
|
|
|
|
// appropriate claims attached.
|
|
|
|
func (a *ACLOIDC) CompleteAuth(req *ACLOIDCCompleteAuthRequest, q *WriteOptions) (*ACLToken, *WriteMeta, error) {
|
|
|
|
var resp ACLToken
|
|
|
|
wm, err := a.client.write("/v1/acl/oidc/complete-auth", req, &resp, q)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
return &resp, wm, nil
|
|
|
|
}
|
|
|
|
|
2017-08-25 00:03:29 +00:00
|
|
|
// 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
|
2022-08-22 20:41:21 +00:00
|
|
|
JobACL *JobACL
|
|
|
|
|
2017-08-25 00:03:29 +00:00
|
|
|
CreateIndex uint64
|
|
|
|
ModifyIndex uint64
|
|
|
|
}
|
|
|
|
|
2022-08-22 20:41:21 +00:00
|
|
|
// JobACL represents an ACL policy's attachment to a job, group, or task.
|
|
|
|
type JobACL struct {
|
|
|
|
Namespace string
|
|
|
|
JobID string
|
|
|
|
Group string
|
|
|
|
Task string
|
|
|
|
}
|
|
|
|
|
2017-08-25 00:03:29 +00:00
|
|
|
// ACLToken represents a client token which is used to Authenticate
|
|
|
|
type ACLToken struct {
|
2022-07-20 08:06:23 +00:00
|
|
|
AccessorID string
|
|
|
|
SecretID string
|
|
|
|
Name string
|
|
|
|
Type string
|
|
|
|
Policies []string
|
2022-08-17 13:45:32 +00:00
|
|
|
|
|
|
|
// 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
|
|
|
|
|
2022-07-20 08:06:23 +00:00
|
|
|
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"`
|
|
|
|
|
2017-08-25 00:03:29 +00:00
|
|
|
CreateIndex uint64
|
|
|
|
ModifyIndex uint64
|
|
|
|
}
|
2017-08-25 00:15:47 +00:00
|
|
|
|
2022-08-17 13:45:32 +00:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2017-08-25 00:15:47 +00:00
|
|
|
type ACLTokenListStub struct {
|
2022-07-20 08:06:23 +00:00
|
|
|
AccessorID string
|
|
|
|
Name string
|
|
|
|
Type string
|
|
|
|
Policies []string
|
2022-08-17 13:45:32 +00:00
|
|
|
Roles []*ACLTokenRoleLink
|
2022-07-20 08:06:23 +00:00
|
|
|
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.
|
2022-09-12 13:50:35 +00:00
|
|
|
ExpirationTime *time.Time `json:",omitempty"`
|
2022-07-20 08:06:23 +00:00
|
|
|
|
2017-08-25 00:15:47 +00:00
|
|
|
CreateIndex uint64
|
|
|
|
ModifyIndex uint64
|
|
|
|
}
|
2021-02-25 21:41:00 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
2022-06-03 11:37:24 +00:00
|
|
|
|
|
|
|
// BootstrapRequest is used for when operators provide an ACL Bootstrap Token
|
|
|
|
type BootstrapRequest struct {
|
|
|
|
BootstrapSecret string
|
|
|
|
}
|
2022-08-11 07:44:37 +00:00
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
2022-08-22 15:20:23 +00:00
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
2022-11-23 08:38:02 +00:00
|
|
|
|
|
|
|
// 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
|
2023-01-13 13:15:58 +00:00
|
|
|
OIDCScopes []string
|
2022-11-23 08:38:02 +00:00
|
|
|
BoundAudiences []string
|
|
|
|
AllowedRedirectURIs []string
|
|
|
|
DiscoveryCaPem []string
|
|
|
|
SigningAlgs []string
|
|
|
|
ClaimMappings map[string]string
|
|
|
|
ListClaimMappings map[string]string
|
|
|
|
}
|
|
|
|
|
2022-11-28 08:20:24 +00:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2022-11-23 08:38:02 +00:00
|
|
|
// 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
|
2022-12-06 13:47:05 +00:00
|
|
|
Type string
|
2022-11-23 08:38:02 +00:00
|
|
|
Default bool
|
|
|
|
|
|
|
|
CreateIndex uint64
|
|
|
|
ModifyIndex uint64
|
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
|
|
|
// ACLAuthMethodTokenLocalityLocal is the ACLAuthMethod.TokenLocality that
|
|
|
|
// will generate ACL tokens which can only be used on the local cluster the
|
|
|
|
// request was made.
|
|
|
|
ACLAuthMethodTokenLocalityLocal = "local"
|
|
|
|
|
|
|
|
// ACLAuthMethodTokenLocalityGlobal is the ACLAuthMethod.TokenLocality that
|
|
|
|
// will generate ACL tokens which can be used on all federated clusters.
|
|
|
|
ACLAuthMethodTokenLocalityGlobal = "global"
|
|
|
|
|
|
|
|
// ACLAuthMethodTypeOIDC the ACLAuthMethod.Type and represents an
|
|
|
|
// auth-method which uses the OIDC protocol.
|
|
|
|
ACLAuthMethodTypeOIDC = "OIDC"
|
|
|
|
)
|
2022-12-15 08:18:55 +00:00
|
|
|
|
|
|
|
// ACLBindingRule contains a direct relation to an ACLAuthMethod and represents
|
|
|
|
// a rule to apply when logging in via the named AuthMethod. This allows the
|
|
|
|
// transformation of OIDC provider claims, to Nomad based ACL concepts such as
|
|
|
|
// ACL Roles and Policies.
|
|
|
|
type ACLBindingRule struct {
|
|
|
|
|
|
|
|
// ID is an internally generated UUID for this rule and is controlled by
|
|
|
|
// Nomad.
|
|
|
|
ID string
|
|
|
|
|
|
|
|
// Description is a human-readable, operator set description that can
|
|
|
|
// provide additional context about the binding rule. This is an
|
|
|
|
// operational field.
|
|
|
|
Description string
|
|
|
|
|
|
|
|
// AuthMethod is the name of the auth method for which this rule applies
|
|
|
|
// to. This is required and the method must exist within state before the
|
|
|
|
// cluster administrator can create the rule.
|
|
|
|
AuthMethod string
|
|
|
|
|
|
|
|
// Selector is an expression that matches against verified identity
|
|
|
|
// attributes returned from the auth method during login. This is optional
|
|
|
|
// and when not set, provides a catch-all rule.
|
|
|
|
Selector string
|
|
|
|
|
|
|
|
// BindType adjusts how this binding rule is applied at login time. The
|
2023-01-26 08:57:44 +00:00
|
|
|
// valid values are ACLBindingRuleBindTypeRole,
|
|
|
|
// ACLBindingRuleBindTypePolicy, and ACLBindingRuleBindTypeManagement.
|
2022-12-15 08:18:55 +00:00
|
|
|
BindType string
|
|
|
|
|
|
|
|
// BindName is the target of the binding. Can be lightly templated using
|
|
|
|
// HIL ${foo} syntax from available field names. How it is used depends
|
|
|
|
// upon the BindType.
|
|
|
|
BindName string
|
|
|
|
|
|
|
|
CreateTime time.Time
|
|
|
|
ModifyTime time.Time
|
|
|
|
CreateIndex uint64
|
|
|
|
ModifyIndex uint64
|
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
|
|
|
// ACLBindingRuleBindTypeRole is the ACL binding rule bind type that only
|
|
|
|
// allows the binding rule to function if a role exists at login-time. The
|
|
|
|
// role will be specified within the ACLBindingRule.BindName parameter, and
|
|
|
|
// will identify whether this is an ID or Name.
|
|
|
|
ACLBindingRuleBindTypeRole = "role"
|
|
|
|
|
|
|
|
// ACLBindingRuleBindTypePolicy is the ACL binding rule bind type that
|
|
|
|
// assigns a policy to the generate ACL token. The role will be specified
|
|
|
|
// within the ACLBindingRule.BindName parameter, and will be the policy
|
|
|
|
// name.
|
|
|
|
ACLBindingRuleBindTypePolicy = "policy"
|
2023-01-26 08:57:44 +00:00
|
|
|
|
|
|
|
// ACLBindingRuleBindTypeManagement is the ACL binding rule bind type that
|
|
|
|
// will generate management ACL tokens when matched.
|
|
|
|
ACLBindingRuleBindTypeManagement = "management"
|
2022-12-15 08:18:55 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// ACLBindingRuleListStub is the stub object returned when performing a listing
|
|
|
|
// of ACL binding rules.
|
|
|
|
type ACLBindingRuleListStub struct {
|
|
|
|
|
|
|
|
// ID is an internally generated UUID for this role and is controlled by
|
|
|
|
// Nomad.
|
|
|
|
ID string
|
|
|
|
|
|
|
|
// Description is a human-readable, operator set description that can
|
|
|
|
// provide additional context about the binding role. This is an
|
|
|
|
// operational field.
|
|
|
|
Description string
|
|
|
|
|
|
|
|
// AuthMethod is the name of the auth method for which this rule applies
|
|
|
|
// to. This is required and the method must exist within state before the
|
|
|
|
// cluster administrator can create the rule.
|
|
|
|
AuthMethod string
|
|
|
|
|
|
|
|
CreateIndex uint64
|
|
|
|
ModifyIndex uint64
|
|
|
|
}
|
2023-01-13 13:15:58 +00:00
|
|
|
|
|
|
|
// ACLOIDCAuthURLRequest is the request to make when starting the OIDC
|
|
|
|
// authentication login flow.
|
|
|
|
type ACLOIDCAuthURLRequest struct {
|
|
|
|
|
|
|
|
// AuthMethodName is the OIDC auth-method to use. This is a required
|
|
|
|
// parameter.
|
|
|
|
AuthMethodName string
|
|
|
|
|
|
|
|
// RedirectURI is the URL that authorization should redirect to. This is a
|
|
|
|
// required parameter.
|
|
|
|
RedirectURI string
|
|
|
|
|
|
|
|
// ClientNonce is a randomly generated string to prevent replay attacks. It
|
|
|
|
// is up to the client to generate this and Go integrations should use the
|
|
|
|
// oidc.NewID function within the hashicorp/cap library.
|
|
|
|
ClientNonce string
|
|
|
|
}
|
|
|
|
|
|
|
|
// ACLOIDCAuthURLResponse is the response when starting the OIDC authentication
|
|
|
|
// login flow.
|
|
|
|
type ACLOIDCAuthURLResponse struct {
|
|
|
|
|
|
|
|
// AuthURL is URL to begin authorization and is where the user logging in
|
|
|
|
// should go.
|
|
|
|
AuthURL string
|
|
|
|
}
|
|
|
|
|
|
|
|
// ACLOIDCCompleteAuthRequest is the request object to begin completing the
|
|
|
|
// OIDC auth cycle after receiving the callback from the OIDC provider.
|
|
|
|
type ACLOIDCCompleteAuthRequest struct {
|
|
|
|
|
|
|
|
// AuthMethodName is the name of the auth method being used to login via
|
|
|
|
// OIDC. This will match AuthUrlArgs.AuthMethodName. This is a required
|
|
|
|
// parameter.
|
|
|
|
AuthMethodName string
|
|
|
|
|
|
|
|
// ClientNonce, State, and Code are provided from the parameters given to
|
|
|
|
// the redirect URL. These are all required parameters.
|
|
|
|
ClientNonce string
|
|
|
|
State string
|
|
|
|
Code string
|
|
|
|
|
|
|
|
// RedirectURI is the URL that authorization should redirect to. This is a
|
|
|
|
// required parameter.
|
|
|
|
RedirectURI string
|
|
|
|
}
|