Add API/CLI for managed groups (#1265)

This commit is contained in:
Jeff Mitchell authored and GitHub committed 2021-06-02 14:05:10 -04:00
1 parent 179a49657e
commit 306d4fb4d3
42 files changed
+1481 -40

No files matched your search

+323
View File
@@ -0,0 +1,323 @@
// Code generated by "make api"; DO NOT EDIT.
package managedgroups
import (
"context"
"errors"
"fmt"
"net/url"
"time"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/scopes"
)
type ManagedGroup struct {
Id string `json:"id,omitempty"`
Scope *scopes.ScopeInfo `json:"scope,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
CreatedTime time.Time `json:"created_time,omitempty"`
UpdatedTime time.Time `json:"updated_time,omitempty"`
Version uint32 `json:"version,omitempty"`
Type string `json:"type,omitempty"`
AuthMethodId string `json:"auth_method_id,omitempty"`
Attributes map[string]interface{} `json:"attributes,omitempty"`
MemberIds []string `json:"member_ids,omitempty"`
AuthorizedActions []string `json:"authorized_actions,omitempty"`
response *api.Response
}
type ManagedGroupReadResult struct {
Item *ManagedGroup
response *api.Response
}
func (n ManagedGroupReadResult) GetItem() interface{} {
return n.Item
}
func (n ManagedGroupReadResult) GetResponse() *api.Response {
return n.response
}
type (
ManagedGroupCreateResult = ManagedGroupReadResult
ManagedGroupUpdateResult = ManagedGroupReadResult
)
type ManagedGroupDeleteResult struct {
response *api.Response
}
// GetItem will always be nil for ManagedGroupDeleteResult
func (n ManagedGroupDeleteResult) GetItem() interface{} {
return nil
}
func (n ManagedGroupDeleteResult) GetResponse() *api.Response {
return n.response
}
type ManagedGroupListResult struct {
Items []*ManagedGroup
response *api.Response
}
func (n ManagedGroupListResult) GetItems() interface{} {
return n.Items
}
func (n ManagedGroupListResult) GetResponse() *api.Response {
return n.response
}
// Client is a client for this collection
type Client struct {
client *api.Client
}
// Creates a new client for this collection. The submitted API client is cloned;
// modifications to it after generating this client will not have effect. If you
// need to make changes to the underlying API client, use ApiClient() to access
// it.
func NewClient(c *api.Client) *Client {
return &Client{client: c.Clone()}
}
// ApiClient returns the underlying API client
func (c *Client) ApiClient() *api.Client {
return c.client
}
func (c *Client) Create(ctx context.Context, authMethodId string, opt ...Option) (*ManagedGroupCreateResult, error) {
if authMethodId == "" {
return nil, fmt.Errorf("empty authMethodId value passed into Create request")
}
opts, apiOpts := getOpts(opt...)
if c.client == nil {
return nil, fmt.Errorf("nil client")
}
opts.postMap["auth_method_id"] = authMethodId
req, err := c.client.NewRequest(ctx, "POST", "managed-groups", opts.postMap, apiOpts...)
if err != nil {
return nil, fmt.Errorf("error creating Create request: %w", err)
}
if len(opts.queryMap) > 0 {
q := url.Values{}
for k, v := range opts.queryMap {
q.Add(k, v)
}
req.URL.RawQuery = q.Encode()
}
resp, err := c.client.Do(req)
if err != nil {
return nil, fmt.Errorf("error performing client request during Create call: %w", err)
}
target := new(ManagedGroupCreateResult)
target.Item = new(ManagedGroup)
apiErr, err := resp.Decode(target.Item)
if err != nil {
return nil, fmt.Errorf("error decoding Create response: %w", err)
}
if apiErr != nil {
return nil, apiErr
}
target.response = resp
return target, nil
}
func (c *Client) Read(ctx context.Context, managedGroupId string, opt ...Option) (*ManagedGroupReadResult, error) {
if managedGroupId == "" {
return nil, fmt.Errorf("empty managedGroupId value passed into Read request")
}
if c.client == nil {
return nil, fmt.Errorf("nil client")
}
opts, apiOpts := getOpts(opt...)
req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("managed-groups/%s", managedGroupId), nil, apiOpts...)
if err != nil {
return nil, fmt.Errorf("error creating Read request: %w", err)
}
if len(opts.queryMap) > 0 {
q := url.Values{}
for k, v := range opts.queryMap {
q.Add(k, v)
}
req.URL.RawQuery = q.Encode()
}
resp, err := c.client.Do(req, apiOpts...)
if err != nil {
return nil, fmt.Errorf("error performing client request during Read call: %w", err)
}
target := new(ManagedGroupReadResult)
target.Item = new(ManagedGroup)
apiErr, err := resp.Decode(target.Item)
if err != nil {
return nil, fmt.Errorf("error decoding Read response: %w", err)
}
if apiErr != nil {
return nil, apiErr
}
target.response = resp
return target, nil
}
func (c *Client) Update(ctx context.Context, managedGroupId string, version uint32, opt ...Option) (*ManagedGroupUpdateResult, error) {
if managedGroupId == "" {
return nil, fmt.Errorf("empty managedGroupId value passed into Update request")
}
if c.client == nil {
return nil, fmt.Errorf("nil client")
}
opts, apiOpts := getOpts(opt...)
if version == 0 {
if !opts.withAutomaticVersioning {
return nil, errors.New("zero version number passed into Update request and automatic versioning not specified")
}
existingTarget, existingErr := c.Read(ctx, managedGroupId, append([]Option{WithSkipCurlOutput(true)}, opt...)...)
if existingErr != nil {
if api.AsServerError(existingErr) != nil {
return nil, fmt.Errorf("error from controller when performing initial check-and-set read: %w", existingErr)
}
return nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr)
}
if existingTarget == nil {
return nil, errors.New("nil resource response found when performing initial check-and-set read")
}
if existingTarget.Item == nil {
return nil, errors.New("nil resource found when performing initial check-and-set read")
}
version = existingTarget.Item.Version
}
opts.postMap["version"] = version
req, err := c.client.NewRequest(ctx, "PATCH", fmt.Sprintf("managed-groups/%s", managedGroupId), opts.postMap, apiOpts...)
if err != nil {
return nil, fmt.Errorf("error creating Update request: %w", err)
}
if len(opts.queryMap) > 0 {
q := url.Values{}
for k, v := range opts.queryMap {
q.Add(k, v)
}
req.URL.RawQuery = q.Encode()
}
resp, err := c.client.Do(req)
if err != nil {
return nil, fmt.Errorf("error performing client request during Update call: %w", err)
}
target := new(ManagedGroupUpdateResult)
target.Item = new(ManagedGroup)
apiErr, err := resp.Decode(target.Item)
if err != nil {
return nil, fmt.Errorf("error decoding Update response: %w", err)
}
if apiErr != nil {
return nil, apiErr
}
target.response = resp
return target, nil
}
func (c *Client) Delete(ctx context.Context, managedGroupId string, opt ...Option) (*ManagedGroupDeleteResult, error) {
if managedGroupId == "" {
return nil, fmt.Errorf("empty managedGroupId value passed into Delete request")
}
if c.client == nil {
return nil, fmt.Errorf("nil client")
}
opts, apiOpts := getOpts(opt...)
req, err := c.client.NewRequest(ctx, "DELETE", fmt.Sprintf("managed-groups/%s", managedGroupId), nil, apiOpts...)
if err != nil {
return nil, fmt.Errorf("error creating Delete request: %w", err)
}
if len(opts.queryMap) > 0 {
q := url.Values{}
for k, v := range opts.queryMap {
q.Add(k, v)
}
req.URL.RawQuery = q.Encode()
}
resp, err := c.client.Do(req)
if err != nil {
return nil, fmt.Errorf("error performing client request during Delete call: %w", err)
}
apiErr, err := resp.Decode(nil)
if err != nil {
return nil, fmt.Errorf("error decoding Delete response: %w", err)
}
if apiErr != nil {
return nil, apiErr
}
target := &ManagedGroupDeleteResult{
response: resp,
}
return target, nil
}
func (c *Client) List(ctx context.Context, authMethodId string, opt ...Option) (*ManagedGroupListResult, error) {
if authMethodId == "" {
return nil, fmt.Errorf("empty authMethodId value passed into List request")
}
if c.client == nil {
return nil, fmt.Errorf("nil client")
}
opts, apiOpts := getOpts(opt...)
opts.queryMap["auth_method_id"] = authMethodId
req, err := c.client.NewRequest(ctx, "GET", "managed-groups", nil, apiOpts...)
if err != nil {
return nil, fmt.Errorf("error creating List request: %w", err)
}
if len(opts.queryMap) > 0 {
q := url.Values{}
for k, v := range opts.queryMap {
q.Add(k, v)
}
req.URL.RawQuery = q.Encode()
}
resp, err := c.client.Do(req)
if err != nil {
return nil, fmt.Errorf("error performing client request during List call: %w", err)
}
target := new(ManagedGroupListResult)
apiErr, err := resp.Decode(target)
if err != nil {
return nil, fmt.Errorf("error decoding List response: %w", err)
}
if apiErr != nil {
return nil, apiErr
}
target.response = resp
return target, nil
}
@@ -0,0 +1,6 @@
// Code generated by "make api"; DO NOT EDIT.
package managedgroups
type OidcManagedGroupAttributes struct {
Filter string `json:"filter,omitempty"`
}
+123
View File
@@ -0,0 +1,123 @@
package managedgroups
import (
"strings"
"github.com/hashicorp/boundary/api"
)
// Option is a func that sets optional attributes for a call. This does not need
// to be used directly, but instead option arguments are built from the
// functions in this package. WithX options set a value to that given in the
// argument; DefaultX options indicate that the value should be set to its
// default. When an API call is made options are processed in ther order they
// appear in the function call, so for a given argument X, a succession of WithX
// or DefaultX calls will result in the last call taking effect.
type Option func(*options)
type options struct {
postMap map[string]interface{}
queryMap map[string]string
withAutomaticVersioning bool
withSkipCurlOutput bool
withFilter string
}
func getDefaultOptions() options {
return options{
postMap: make(map[string]interface{}),
queryMap: make(map[string]string),
}
}
func getOpts(opt ...Option) (options, []api.Option) {
opts := getDefaultOptions()
for _, o := range opt {
if o != nil {
o(&opts)
}
}
var apiOpts []api.Option
if opts.withSkipCurlOutput {
apiOpts = append(apiOpts, api.WithSkipCurlOutput(true))
}
if opts.withFilter != "" {
opts.queryMap["filter"] = opts.withFilter
}
return opts, apiOpts
}
// If set, and if the version is zero during an update, the API will perform a
// fetch to get the current version of the resource and populate it during the
// update call. This is convenient but opens up the possibility for subtle
// order-of-modification issues, so use carefully.
func WithAutomaticVersioning(enable bool) Option {
return func(o *options) {
o.withAutomaticVersioning = enable
}
}
// WithSkipCurlOutput tells the API to not use the current call for cURL output.
// Useful for when we need to look up versions.
func WithSkipCurlOutput(skip bool) Option {
return func(o *options) {
o.withSkipCurlOutput = true
}
}
// WithFilter tells the API to filter the items returned using the provided
// filter term. The filter should be in a format supported by
// hashicorp/go-bexpr.
func WithFilter(filter string) Option {
return func(o *options) {
o.withFilter = strings.TrimSpace(filter)
}
}
func WithAttributes(inAttributes map[string]interface{}) Option {
return func(o *options) {
o.postMap["attributes"] = inAttributes
}
}
func DefaultAttributes() Option {
return func(o *options) {
o.postMap["attributes"] = nil
}
}
func WithDescription(inDescription string) Option {
return func(o *options) {
o.postMap["description"] = inDescription
}
}
func DefaultDescription() Option {
return func(o *options) {
o.postMap["description"] = nil
}
}
func WithOidcManagedGroupFilter(inFilter string) Option {
return func(o *options) {
raw, ok := o.postMap["attributes"]
if !ok {
raw = interface{}(map[string]interface{}{})
}
val := raw.(map[string]interface{})
val["filter"] = inFilter
o.postMap["attributes"] = val
}
}
func WithName(inName string) Option {
return func(o *options) {
o.postMap["name"] = inName
}
}
func DefaultName() Option {
return func(o *options) {
o.postMap["name"] = nil
}
}
+33
View File
@@ -11,6 +11,7 @@ import (
"github.com/hashicorp/boundary/internal/gen/controller/api/resources/hostcatalogs"
"github.com/hashicorp/boundary/internal/gen/controller/api/resources/hosts"
"github.com/hashicorp/boundary/internal/gen/controller/api/resources/hostsets"
"github.com/hashicorp/boundary/internal/gen/controller/api/resources/managedgroups"
"github.com/hashicorp/boundary/internal/gen/controller/api/resources/roles"
"github.com/hashicorp/boundary/internal/gen/controller/api/resources/scopes"
"github.com/hashicorp/boundary/internal/gen/controller/api/resources/sessions"
@@ -87,6 +88,10 @@ type structInfo struct {
// conveyed within the item itself
extraOptions []fieldInfo
// fieldOverrides allows overriding some field behavior without making them
// "new" fields like with extraOptions
fieldOverrides []fieldInfo
// createResponseTypes controls for which structs response types are created
createResponseTypes bool
@@ -298,6 +303,34 @@ var inputStructs = []*structInfo{
versionEnabled: true,
createResponseTypes: true,
},
// Managed Groups
{
inProto: &managedgroups.OidcManagedGroupAttributes{},
outFile: "managedgroups/oidc_managed_group_attributes.gen.go",
subtypeName: "OidcManagedGroup",
fieldOverrides: []fieldInfo{
{
Name: "Filter",
SkipDefault: true,
},
},
},
{
inProto: &managedgroups.ManagedGroup{},
outFile: "managedgroups/managedgroups.gen.go",
templates: []*template.Template{
clientTemplate,
createTemplate,
readTemplate,
updateTemplate,
deleteTemplate,
listTemplate,
},
pathArgs: []string{"managed-group"},
parentTypeName: "auth-method",
versionEnabled: true,
createResponseTypes: true,
},
// Auth Tokens
{
inProto: &authtokens.AuthToken{},
+13
View File
@@ -123,6 +123,19 @@ func fillTemplates() {
}
optionsMap[input.Package] = optionMap
}
// Override some defined options
if len(in.fieldOverrides) > 0 && optionsMap != nil {
for _, override := range in.fieldOverrides {
inOpts := optionsMap[input.Package]
if inOpts != nil {
if override.SkipDefault {
fieldInfo := inOpts[override.Name]
fieldInfo.SkipDefault = true
inOpts[override.Name] = fieldInfo
}
}
}
}
outFile, err := filepath.Abs(fmt.Sprintf("%s/%s", os.Getenv("API_GEN_BASEPATH"), in.outFile))
if err != nil {
@@ -214,3 +214,28 @@ func (r *Repository) ListManagedGroupMembershipsByMember(ctx context.Context, wi
}
return mgs, nil
}
// ListManagedGroupMembershipsByGroup lists managed group memberships via the
// group ID and supports WithLimit option.
func (r *Repository) ListManagedGroupMembershipsByGroup(ctx context.Context, withGroupId string, opt ...Option) ([]*ManagedGroupMemberAccount, error) {
const op = "oidc.(Repository).ListManagedGroupMembershipsByGroup"
if withGroupId == "" {
return nil, errors.New(errors.InvalidParameter, op, "missing managed group id")
}
opts := getOpts(opt...)
limit := r.defaultLimit
if opts.withLimit != 0 {
// non-zero signals an override of the default limit for the repo.
limit = opts.withLimit
}
reader := r.reader
if opts.withReader != nil {
reader = opts.withReader
}
var mgs []*ManagedGroupMemberAccount
err := reader.SearchWhere(ctx, &mgs, "managed_group_id = ?", []interface{}{withGroupId}, db.WithLimit(limit))
if err != nil {
return nil, errors.Wrap(err, op)
}
return mgs, nil
}
@@ -258,6 +258,18 @@ func Test_ManagedGroupMemberships(t *testing.T) {
// been in the finalMgs map, and when they are all removed there
// should be nothing left.
for _, mship := range memberships {
// Randomly check a few to ensure the MembershipsByGroup function works
members, err := repo.ListManagedGroupMembershipsByGroup(ctx, mship.ManagedGroupId)
require.NoError(err)
require.NotEmpty(members)
var found bool
for _, v := range members {
if v.MemberId == tt.account.GetPublicId() {
found = true
break
}
}
assert.True(found)
assert.Contains(finalMgs, mship.ManagedGroupId)
delete(finalMgs, mship.ManagedGroupId)
}
+49
View File
@@ -15,6 +15,7 @@ import (
"github.com/hashicorp/boundary/internal/cmd/commands/hostscmd"
"github.com/hashicorp/boundary/internal/cmd/commands/hostsetscmd"
"github.com/hashicorp/boundary/internal/cmd/commands/logout"
"github.com/hashicorp/boundary/internal/cmd/commands/managedgroupscmd"
"github.com/hashicorp/boundary/internal/cmd/commands/rolescmd"
"github.com/hashicorp/boundary/internal/cmd/commands/scopescmd"
"github.com/hashicorp/boundary/internal/cmd/commands/server"
@@ -546,6 +547,54 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
}, nil
},
"managed-groups": func() (cli.Command, error) {
return &managedgroupscmd.Command{
Command: base.NewCommand(ui),
}, nil
},
"managed-groups read": func() (cli.Command, error) {
return &managedgroupscmd.Command{
Command: base.NewCommand(ui),
Func: "read",
}, nil
},
"managed-groups delete": func() (cli.Command, error) {
return &managedgroupscmd.Command{
Command: base.NewCommand(ui),
Func: "delete",
}, nil
},
"managed-groups list": func() (cli.Command, error) {
return &managedgroupscmd.Command{
Command: base.NewCommand(ui),
Func: "list",
}, nil
},
"managed-groups create": func() (cli.Command, error) {
return &managedgroupscmd.Command{
Command: base.NewCommand(ui),
Func: "create",
}, nil
},
"managed-groups create oidc": func() (cli.Command, error) {
return &managedgroupscmd.OidcCommand{
Command: base.NewCommand(ui),
Func: "create",
}, nil
},
"managed-groups update": func() (cli.Command, error) {
return &managedgroupscmd.Command{
Command: base.NewCommand(ui),
Func: "update",
}, nil
},
"managed-groups update oidc": func() (cli.Command, error) {
return &managedgroupscmd.OidcCommand{
Command: base.NewCommand(ui),
Func: "update",
}, nil
},
"roles": func() (cli.Command, error) {
return &rolescmd.Command{
Command: base.NewCommand(ui),
@@ -103,7 +103,7 @@ func (c *Command) Flags() *base.FlagSets {
set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat)
f := set.NewFlagSet("Command Options")
common.PopulateCommonFlags(c.Command, f, "account", flagsMap[c.Func])
common.PopulateCommonFlags(c.Command, f, "account", flagsMap, c.Func)
extraFlagsFunc(c, set, f)
@@ -91,7 +91,7 @@ func (c *OidcCommand) Flags() *base.FlagSets {
set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat)
f := set.NewFlagSet("Command Options")
common.PopulateCommonFlags(c.Command, f, "oidc-type account", flagsOidcMap[c.Func])
common.PopulateCommonFlags(c.Command, f, "oidc-type account", flagsOidcMap, c.Func)
extraOidcFlagsFunc(c, set, f)
@@ -91,7 +91,7 @@ func (c *PasswordCommand) Flags() *base.FlagSets {
set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat)
f := set.NewFlagSet("Command Options")
common.PopulateCommonFlags(c.Command, f, "password-type account", flagsPasswordMap[c.Func])
common.PopulateCommonFlags(c.Command, f, "password-type account", flagsPasswordMap, c.Func)
extraPasswordFlagsFunc(c, set, f)
@@ -101,7 +101,7 @@ func (c *Command) Flags() *base.FlagSets {
set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat)
f := set.NewFlagSet("Command Options")
common.PopulateCommonFlags(c.Command, f, "auth method", flagsMap[c.Func])
common.PopulateCommonFlags(c.Command, f, "auth method", flagsMap, c.Func)
extraFlagsFunc(c, set, f)
@@ -91,7 +91,7 @@ func (c *OidcCommand) Flags() *base.FlagSets {
set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat)
f := set.NewFlagSet("Command Options")
common.PopulateCommonFlags(c.Command, f, "oidc-type auth method", flagsOidcMap[c.Func])
common.PopulateCommonFlags(c.Command, f, "oidc-type auth method", flagsOidcMap, c.Func)
extraOidcFlagsFunc(c, set, f)
@@ -91,7 +91,7 @@ func (c *PasswordCommand) Flags() *base.FlagSets {
set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat)
f := set.NewFlagSet("Command Options")
common.PopulateCommonFlags(c.Command, f, "password-type auth method", flagsPasswordMap[c.Func])
common.PopulateCommonFlags(c.Command, f, "password-type auth method", flagsPasswordMap, c.Func)
extraPasswordFlagsFunc(c, set, f)
@@ -101,7 +101,7 @@ func (c *Command) Flags() *base.FlagSets {
set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat)
f := set.NewFlagSet("Command Options")
common.PopulateCommonFlags(c.Command, f, "auth token", flagsMap[c.Func])
common.PopulateCommonFlags(c.Command, f, "auth token", flagsMap, c.Func)
extraFlagsFunc(c, set, f)
@@ -113,7 +113,7 @@ func (c *Command) Flags() *base.FlagSets {
set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat)
f := set.NewFlagSet("Command Options")
common.PopulateCommonFlags(c.Command, f, "group", flagsMap[c.Func])
common.PopulateCommonFlags(c.Command, f, "group", flagsMap, c.Func)
extraFlagsFunc(c, set, f)
@@ -101,7 +101,7 @@ func (c *Command) Flags() *base.FlagSets {
set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat)
f := set.NewFlagSet("Command Options")
common.PopulateCommonFlags(c.Command, f, "host catalog", flagsMap[c.Func])
common.PopulateCommonFlags(c.Command, f, "host catalog", flagsMap, c.Func)
extraFlagsFunc(c, set, f)
@@ -89,7 +89,7 @@ func (c *StaticCommand) Flags() *base.FlagSets {
set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat)
f := set.NewFlagSet("Command Options")
common.PopulateCommonFlags(c.Command, f, "static-type host catalog", flagsStaticMap[c.Func])
common.PopulateCommonFlags(c.Command, f, "static-type host catalog", flagsStaticMap, c.Func)
extraStaticFlagsFunc(c, set, f)
+1 -1
View File
@@ -101,7 +101,7 @@ func (c *Command) Flags() *base.FlagSets {
set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat)
f := set.NewFlagSet("Command Options")
common.PopulateCommonFlags(c.Command, f, "host", flagsMap[c.Func])
common.PopulateCommonFlags(c.Command, f, "host", flagsMap, c.Func)
extraFlagsFunc(c, set, f)
@@ -91,7 +91,7 @@ func (c *StaticCommand) Flags() *base.FlagSets {
set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat)
f := set.NewFlagSet("Command Options")
common.PopulateCommonFlags(c.Command, f, "static-type host", flagsStaticMap[c.Func])
common.PopulateCommonFlags(c.Command, f, "static-type host", flagsStaticMap, c.Func)
extraStaticFlagsFunc(c, set, f)
@@ -103,7 +103,7 @@ func (c *Command) Flags() *base.FlagSets {
set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat)
f := set.NewFlagSet("Command Options")
common.PopulateCommonFlags(c.Command, f, "host set", flagsMap[c.Func])
common.PopulateCommonFlags(c.Command, f, "host set", flagsMap, c.Func)
extraFlagsFunc(c, set, f)
@@ -89,7 +89,7 @@ func (c *StaticCommand) Flags() *base.FlagSets {
set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat)
f := set.NewFlagSet("Command Options")
common.PopulateCommonFlags(c.Command, f, "static-type host set", flagsStaticMap[c.Func])
common.PopulateCommonFlags(c.Command, f, "static-type host set", flagsStaticMap, c.Func)
extraStaticFlagsFunc(c, set, f)
@@ -0,0 +1,138 @@
package managedgroupscmd
import (
"fmt"
"time"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/managedgroups"
"github.com/hashicorp/boundary/internal/cmd/base"
)
func (c *Command) printListTable(items []*managedgroups.ManagedGroup) string {
if len(items) == 0 {
return "No managed groups found"
}
var output []string
output = []string{
"",
"Managed Group information:",
}
for i, item := range items {
if i > 0 {
output = append(output, "")
}
if item.Id != "" {
output = append(output,
fmt.Sprintf(" ID: %s", item.Id),
)
} else {
output = append(output,
fmt.Sprintf(" ID: %s", "(not available)"),
)
}
if item.Version > 0 {
output = append(output,
fmt.Sprintf(" Version: %d", item.Version),
)
}
if item.Type != "" {
output = append(output,
fmt.Sprintf(" Type: %s", item.Type),
)
}
if item.Name != "" {
output = append(output,
fmt.Sprintf(" Name: %s", item.Name),
)
}
if item.Description != "" {
output = append(output,
fmt.Sprintf(" Description: %s", item.Description),
)
}
if len(item.AuthorizedActions) > 0 {
output = append(output,
" Authorized Actions:",
base.WrapSlice(6, item.AuthorizedActions),
)
}
}
return base.WrapForHelpText(output)
}
func printItemTable(result api.GenericResult) string {
item := result.GetItem().(*managedgroups.ManagedGroup)
nonAttributeMap := map[string]interface{}{}
if item.Id != "" {
nonAttributeMap["ID"] = item.Id
}
if item.Version != 0 {
nonAttributeMap["Version"] = item.Version
}
if item.Type != "" {
nonAttributeMap["Type"] = item.Type
}
if !item.CreatedTime.IsZero() {
nonAttributeMap["Created Time"] = item.CreatedTime.Local().Format(time.RFC1123)
}
if !item.UpdatedTime.IsZero() {
nonAttributeMap["Updated Time"] = item.UpdatedTime.Local().Format(time.RFC1123)
}
if item.AuthMethodId != "" {
nonAttributeMap["Auth Method ID"] = item.AuthMethodId
}
if item.Name != "" {
nonAttributeMap["Name"] = item.Name
}
if item.Description != "" {
nonAttributeMap["Description"] = item.Description
}
maxLength := base.MaxAttributesLength(nonAttributeMap, item.Attributes, keySubstMap)
ret := []string{
"",
"Managed Group information:",
base.WrapMap(2, maxLength+2, nonAttributeMap),
}
if item.Scope != nil {
ret = append(ret,
"",
" Scope:",
base.ScopeInfoForOutput(item.Scope, maxLength),
)
}
if len(item.AuthorizedActions) > 0 {
ret = append(ret,
"",
" Authorized Actions:",
base.WrapSlice(4, item.AuthorizedActions),
)
}
if len(item.MemberIds) > 0 {
ret = append(ret,
"",
" Member IDs:",
base.WrapSlice(4, item.MemberIds),
)
}
if len(item.Attributes) > 0 {
ret = append(ret,
"",
" Attributes:",
base.WrapMap(4, maxLength, item.Attributes),
)
}
return base.WrapForHelpText(ret)
}
var keySubstMap = map[string]string{
"filter": "Filter",
}
@@ -0,0 +1,278 @@
// Code generated by "make api"; DO NOT EDIT.
package managedgroupscmd
import (
"errors"
"fmt"
"sync"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/managedgroups"
"github.com/hashicorp/boundary/internal/cmd/base"
"github.com/hashicorp/boundary/internal/cmd/common"
"github.com/hashicorp/boundary/sdk/strutil"
"github.com/mitchellh/cli"
"github.com/posener/complete"
)
func initFlags() {
flagsOnce.Do(func() {
extraFlags := extraActionsFlagsMapFunc()
for k, v := range extraFlags {
flagsMap[k] = append(flagsMap[k], v...)
}
})
}
var (
_ cli.Command = (*Command)(nil)
_ cli.CommandAutocomplete = (*Command)(nil)
)
type Command struct {
*base.Command
Func string
plural string
}
func (c *Command) AutocompleteArgs() complete.Predictor {
initFlags()
return complete.PredictAnything
}
func (c *Command) AutocompleteFlags() complete.Flags {
initFlags()
return c.Flags().Completions()
}
func (c *Command) Synopsis() string {
if extra := extraSynopsisFunc(c); extra != "" {
return extra
}
synopsisStr := "managed group"
return common.SynopsisFunc(c.Func, synopsisStr)
}
func (c *Command) Help() string {
initFlags()
var helpStr string
helpMap := common.HelpMap("managed group")
switch c.Func {
case "read":
helpStr = helpMap[c.Func]() + c.Flags().Help()
case "delete":
helpStr = helpMap[c.Func]() + c.Flags().Help()
case "list":
helpStr = helpMap[c.Func]() + c.Flags().Help()
default:
helpStr = helpMap["base"]()
}
// Keep linter from complaining if we don't actually generate code using it
_ = helpMap
return helpStr
}
var flagsMap = map[string][]string{
"read": {"id"},
"delete": {"id"},
"list": {"auth-method-id", "filter"},
}
func (c *Command) Flags() *base.FlagSets {
if len(flagsMap[c.Func]) == 0 {
return c.FlagSet(base.FlagSetNone)
}
set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat)
f := set.NewFlagSet("Command Options")
common.PopulateCommonFlags(c.Command, f, "managed group", flagsMap, c.Func)
extraFlagsFunc(c, set, f)
return set
}
func (c *Command) Run(args []string) int {
initFlags()
switch c.Func {
case "":
return cli.RunResultHelp
case "create", "update":
return cli.RunResultHelp
}
c.plural = "managed group"
switch c.Func {
case "list":
c.plural = "managed groups"
}
f := c.Flags()
if err := f.Parse(args); err != nil {
c.PrintCliError(err)
return base.CommandUserError
}
if strutil.StrListContains(flagsMap[c.Func], "id") && c.FlagId == "" {
c.PrintCliError(errors.New("ID is required but not passed in via -id"))
return base.CommandUserError
}
var opts []managedgroups.Option
if strutil.StrListContains(flagsMap[c.Func], "auth-method-id") {
switch c.Func {
case "list":
if c.FlagAuthMethodId == "" {
c.PrintCliError(errors.New("AuthMethod ID must be passed in via -auth-method-id or BOUNDARY_AUTH_METHOD_ID"))
return base.CommandUserError
}
}
}
client, err := c.Client()
if err != nil {
c.PrintCliError(fmt.Errorf("Error creating API client: %s", err.Error()))
return base.CommandCliError
}
managedgroupsClient := managedgroups.NewClient(client)
switch c.FlagName {
case "":
case "null":
opts = append(opts, managedgroups.DefaultName())
default:
opts = append(opts, managedgroups.WithName(c.FlagName))
}
switch c.FlagDescription {
case "":
case "null":
opts = append(opts, managedgroups.DefaultDescription())
default:
opts = append(opts, managedgroups.WithDescription(c.FlagDescription))
}
if c.FlagFilter != "" {
opts = append(opts, managedgroups.WithFilter(c.FlagFilter))
}
var version uint32
if ok := extraFlagsHandlingFunc(c, f, &opts); !ok {
return base.CommandUserError
}
var result api.GenericResult
var listResult api.GenericListResult
switch c.Func {
case "read":
result, err = managedgroupsClient.Read(c.Context, c.FlagId, opts...)
case "delete":
result, err = managedgroupsClient.Delete(c.Context, c.FlagId, opts...)
case "list":
listResult, err = managedgroupsClient.List(c.Context, c.FlagAuthMethodId, opts...)
}
result, err = executeExtraActions(c, result, err, managedgroupsClient, version, opts)
if err != nil {
if apiErr := api.AsServerError(err); apiErr != nil {
c.PrintApiError(apiErr, fmt.Sprintf("Error from controller when performing %s on %s", c.Func, c.plural))
return base.CommandApiError
}
c.PrintCliError(fmt.Errorf("Error trying to %s %s: %s", c.Func, c.plural, err.Error()))
return base.CommandCliError
}
output, err := printCustomActionOutput(c)
if err != nil {
c.PrintCliError(err)
return base.CommandUserError
}
if output {
return base.CommandSuccess
}
switch c.Func {
case "delete":
switch base.Format(c.UI) {
case "json":
if ok := c.PrintJsonItem(result); !ok {
return base.CommandCliError
}
case "table":
c.UI.Output("The delete operation completed successfully.")
}
return base.CommandSuccess
case "list":
switch base.Format(c.UI) {
case "json":
if ok := c.PrintJsonItems(listResult); !ok {
return base.CommandCliError
}
case "table":
listedItems := listResult.GetItems().([]*managedgroups.ManagedGroup)
c.UI.Output(c.printListTable(listedItems))
}
return base.CommandSuccess
}
switch base.Format(c.UI) {
case "table":
c.UI.Output(printItemTable(result))
case "json":
if ok := c.PrintJsonItem(result); !ok {
return base.CommandCliError
}
}
return base.CommandSuccess
}
var (
flagsOnce = new(sync.Once)
extraActionsFlagsMapFunc = func() map[string][]string { return nil }
extraSynopsisFunc = func(*Command) string { return "" }
extraFlagsFunc = func(*Command, *base.FlagSets, *base.FlagSet) {}
extraFlagsHandlingFunc = func(*Command, *base.FlagSets, *[]managedgroups.Option) bool { return true }
executeExtraActions = func(_ *Command, inResult api.GenericResult, inErr error, _ *managedgroups.Client, _ uint32, _ []managedgroups.Option) (api.GenericResult, error) {
return inResult, inErr
}
printCustomActionOutput = func(*Command) (bool, error) { return false, nil }
)
@@ -0,0 +1,95 @@
package managedgroupscmd
import (
"fmt"
"github.com/hashicorp/boundary/api/managedgroups"
"github.com/hashicorp/boundary/internal/cmd/base"
"github.com/hashicorp/go-bexpr"
)
const (
filterFlagName = "filter"
)
func init() {
extraOidcFlagsFunc = extraOidcFlagsFuncImpl
extraOidcActionsFlagsMapFunc = extraOidcActionsFlagsMapFuncImpl
extraOidcFlagsHandlingFunc = extraOidcFlagsHandlingFuncImpl
}
func extraOidcActionsFlagsMapFuncImpl() map[string][]string {
return map[string][]string{
"create": {filterFlagName},
"update": {filterFlagName},
}
}
type extraOidcCmdVars struct {
flagFilter string
}
func (c *OidcCommand) extraOidcHelpFunc(helpMap map[string]func() string) string {
var helpStr string
switch c.Func {
case "create":
helpStr = base.WrapForHelpText([]string{
"Usage: boundary managed-groups create oidc [options] [args]",
"",
" Create a oidc-type managed group. Example:",
"",
` $ boundary managed-groups create oidc -filter '"/token/email" == "doe@example.com"' -description "Oidc managed group for ProdOps"`,
"",
"",
})
case "update":
helpStr = base.WrapForHelpText([]string{
"Usage: boundary managed-groups update oidc [options] [args]",
"",
" Update a oidc-type managed group given its ID. Example:",
"",
` $ boundary managed-groups update oidc -id acctoidc_1234567890 -name "devops" -description "Oidc managed group for DevOps"`,
"",
"",
})
}
return helpStr + c.Flags().Help()
}
func extraOidcFlagsFuncImpl(c *OidcCommand, _ *base.FlagSets, f *base.FlagSet) {
for _, name := range flagsOidcMap[c.Func] {
switch name {
case "filter":
f.StringVar(&base.StringVar{
Name: filterFlagName,
Target: &c.flagFilter,
Usage: "The filter defining the criteria against which an accounts's OIDC token and/or userinfo will be evaluated to determined membership at login time.",
})
}
}
}
func extraOidcFlagsHandlingFuncImpl(c *OidcCommand, _ *base.FlagSets, opts *[]managedgroups.Option) bool {
switch c.flagFilter {
case "null":
c.UI.Error("Filter must be defined, and cannot be cleared.")
return false
case "":
if c.Func == "create" {
c.UI.Error("The -filter flag must be provided when creating a managed group.")
return false
}
default:
_, err := bexpr.CreateEvaluator(c.flagFilter)
if err != nil {
c.UI.Error(fmt.Sprintf("Error when parsing filter to check validity: %v", err))
return false
}
*opts = append(*opts, managedgroups.WithAttributes(map[string]interface{}{filterFlagName: c.flagFilter}))
}
return true
}
@@ -0,0 +1,239 @@
// Code generated by "make api"; DO NOT EDIT.
package managedgroupscmd
import (
"errors"
"fmt"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/managedgroups"
"github.com/hashicorp/boundary/internal/cmd/base"
"github.com/hashicorp/boundary/internal/cmd/common"
"github.com/hashicorp/boundary/sdk/strutil"
"github.com/mitchellh/cli"
"github.com/posener/complete"
)
func initOidcFlags() {
flagsOnce.Do(func() {
extraFlags := extraOidcActionsFlagsMapFunc()
for k, v := range extraFlags {
flagsOidcMap[k] = append(flagsOidcMap[k], v...)
}
})
}
var (
_ cli.Command = (*OidcCommand)(nil)
_ cli.CommandAutocomplete = (*OidcCommand)(nil)
)
type OidcCommand struct {
*base.Command
Func string
plural string
extraOidcCmdVars
}
func (c *OidcCommand) AutocompleteArgs() complete.Predictor {
initOidcFlags()
return complete.PredictAnything
}
func (c *OidcCommand) AutocompleteFlags() complete.Flags {
initOidcFlags()
return c.Flags().Completions()
}
func (c *OidcCommand) Synopsis() string {
if extra := extraOidcSynopsisFunc(c); extra != "" {
return extra
}
synopsisStr := "managed group"
synopsisStr = fmt.Sprintf("%s %s", "oidc-type", synopsisStr)
return common.SynopsisFunc(c.Func, synopsisStr)
}
func (c *OidcCommand) Help() string {
initOidcFlags()
var helpStr string
helpMap := common.HelpMap("managed group")
switch c.Func {
default:
helpStr = c.extraOidcHelpFunc(helpMap)
}
// Keep linter from complaining if we don't actually generate code using it
_ = helpMap
return helpStr
}
var flagsOidcMap = map[string][]string{
"create": {"auth-method-id", "name", "description"},
"update": {"id", "name", "description", "version"},
}
func (c *OidcCommand) Flags() *base.FlagSets {
if len(flagsOidcMap[c.Func]) == 0 {
return c.FlagSet(base.FlagSetNone)
}
set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat)
f := set.NewFlagSet("Command Options")
common.PopulateCommonFlags(c.Command, f, "oidc-type managed group", flagsOidcMap, c.Func)
extraOidcFlagsFunc(c, set, f)
return set
}
func (c *OidcCommand) Run(args []string) int {
initOidcFlags()
switch c.Func {
case "":
return cli.RunResultHelp
}
c.plural = "oidc-type managed group"
switch c.Func {
case "list":
c.plural = "oidc-type managed groups"
}
f := c.Flags()
if err := f.Parse(args); err != nil {
c.PrintCliError(err)
return base.CommandUserError
}
if strutil.StrListContains(flagsOidcMap[c.Func], "id") && c.FlagId == "" {
c.PrintCliError(errors.New("ID is required but not passed in via -id"))
return base.CommandUserError
}
var opts []managedgroups.Option
if strutil.StrListContains(flagsOidcMap[c.Func], "auth-method-id") {
switch c.Func {
case "create":
if c.FlagAuthMethodId == "" {
c.PrintCliError(errors.New("AuthMethod ID must be passed in via -auth-method-id or BOUNDARY_AUTH_METHOD_ID"))
return base.CommandUserError
}
}
}
client, err := c.Client()
if err != nil {
c.PrintCliError(fmt.Errorf("Error creating API client: %s", err.Error()))
return base.CommandCliError
}
managedgroupsClient := managedgroups.NewClient(client)
switch c.FlagName {
case "":
case "null":
opts = append(opts, managedgroups.DefaultName())
default:
opts = append(opts, managedgroups.WithName(c.FlagName))
}
switch c.FlagDescription {
case "":
case "null":
opts = append(opts, managedgroups.DefaultDescription())
default:
opts = append(opts, managedgroups.WithDescription(c.FlagDescription))
}
if c.FlagFilter != "" {
opts = append(opts, managedgroups.WithFilter(c.FlagFilter))
}
var version uint32
switch c.Func {
case "update":
switch c.FlagVersion {
case 0:
opts = append(opts, managedgroups.WithAutomaticVersioning(true))
default:
version = uint32(c.FlagVersion)
}
}
if ok := extraOidcFlagsHandlingFunc(c, f, &opts); !ok {
return base.CommandUserError
}
var result api.GenericResult
switch c.Func {
case "create":
result, err = managedgroupsClient.Create(c.Context, c.FlagAuthMethodId, opts...)
case "update":
result, err = managedgroupsClient.Update(c.Context, c.FlagId, version, opts...)
}
result, err = executeExtraOidcActions(c, result, err, managedgroupsClient, version, opts)
if err != nil {
if apiErr := api.AsServerError(err); apiErr != nil {
c.PrintApiError(apiErr, fmt.Sprintf("Error from controller when performing %s on %s", c.Func, c.plural))
return base.CommandApiError
}
c.PrintCliError(fmt.Errorf("Error trying to %s %s: %s", c.Func, c.plural, err.Error()))
return base.CommandCliError
}
output, err := printCustomOidcActionOutput(c)
if err != nil {
c.PrintCliError(err)
return base.CommandUserError
}
if output {
return base.CommandSuccess
}
switch c.Func {
}
switch base.Format(c.UI) {
case "table":
c.UI.Output(printItemTable(result))
case "json":
if ok := c.PrintJsonItem(result); !ok {
return base.CommandCliError
}
}
return base.CommandSuccess
}
var (
extraOidcActionsFlagsMapFunc = func() map[string][]string { return nil }
extraOidcSynopsisFunc = func(*OidcCommand) string { return "" }
extraOidcFlagsFunc = func(*OidcCommand, *base.FlagSets, *base.FlagSet) {}
extraOidcFlagsHandlingFunc = func(*OidcCommand, *base.FlagSets, *[]managedgroups.Option) bool { return true }
executeExtraOidcActions = func(_ *OidcCommand, inResult api.GenericResult, inErr error, _ *managedgroups.Client, _ uint32, _ []managedgroups.Option) (api.GenericResult, error) {
return inResult, inErr
}
printCustomOidcActionOutput = func(*OidcCommand) (bool, error) { return false, nil }
)
+1 -1
View File
@@ -113,7 +113,7 @@ func (c *Command) Flags() *base.FlagSets {
set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat)
f := set.NewFlagSet("Command Options")
common.PopulateCommonFlags(c.Command, f, "role", flagsMap[c.Func])
common.PopulateCommonFlags(c.Command, f, "role", flagsMap, c.Func)
extraFlagsFunc(c, set, f)
@@ -113,7 +113,7 @@ func (c *Command) Flags() *base.FlagSets {
set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat)
f := set.NewFlagSet("Command Options")
common.PopulateCommonFlags(c.Command, f, "scope", flagsMap[c.Func])
common.PopulateCommonFlags(c.Command, f, "scope", flagsMap, c.Func)
extraFlagsFunc(c, set, f)
@@ -96,7 +96,7 @@ func (c *Command) Flags() *base.FlagSets {
set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat)
f := set.NewFlagSet("Command Options")
common.PopulateCommonFlags(c.Command, f, "session", flagsMap[c.Func])
common.PopulateCommonFlags(c.Command, f, "session", flagsMap, c.Func)
extraFlagsFunc(c, set, f)
@@ -104,7 +104,7 @@ func (c *Command) Flags() *base.FlagSets {
set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat)
f := set.NewFlagSet("Command Options")
common.PopulateCommonFlags(c.Command, f, "target", flagsMap[c.Func])
common.PopulateCommonFlags(c.Command, f, "target", flagsMap, c.Func)
extraFlagsFunc(c, set, f)
@@ -91,7 +91,7 @@ func (c *TcpCommand) Flags() *base.FlagSets {
set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat)
f := set.NewFlagSet("Command Options")
common.PopulateCommonFlags(c.Command, f, "tcp-type target", flagsTcpMap[c.Func])
common.PopulateCommonFlags(c.Command, f, "tcp-type target", flagsTcpMap, c.Func)
extraTcpFlagsFunc(c, set, f)
+1 -1
View File
@@ -113,7 +113,7 @@ func (c *Command) Flags() *base.FlagSets {
set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat)
f := set.NewFlagSet("Command Options")
common.PopulateCommonFlags(c.Command, f, "user", flagsMap[c.Func])
common.PopulateCommonFlags(c.Command, f, "user", flagsMap, c.Func)
extraFlagsFunc(c, set, f)
+14 -8
View File
@@ -7,8 +7,8 @@ import (
"github.com/posener/complete"
)
func PopulateCommonFlags(c *base.Command, f *base.FlagSet, resourceType string, flagNames []string) {
for _, name := range flagNames {
func PopulateCommonFlags(c *base.Command, f *base.FlagSet, resourceType string, flagNames map[string][]string, command string) {
for _, name := range flagNames[command] {
switch name {
case "scope-id":
f.StringVar(&base.StringVar{
@@ -71,12 +71,18 @@ func PopulateCommonFlags(c *base.Command, f *base.FlagSet, resourceType string,
Target: &c.FlagRecursive,
Usage: "If set, the list operation will be applied recursively into child scopes, if supported by the type.",
})
case "filter":
f.StringVar(&base.StringVar{
Name: "filter",
Target: &c.FlagFilter,
Usage: "If set, the list operation will be filtered before being returned. The filter operates against each item in the list. Using single quotes is recommended as filters contain double quotes. See https://www.boundaryproject.io/docs/concepts/filtering/resource-listing for details.",
})
}
}
if command == "list" {
for _, name := range flagNames[command] {
switch name {
case "filter":
f.StringVar(&base.StringVar{
Name: "filter",
Target: &c.FlagFilter,
Usage: "If set, the list operation will be filtered before being returned. The filter operates against each item in the list. Using single quotes is recommended as filters contain double quotes. See https://www.boundaryproject.io/docs/concepts/filtering/resource-listing for details.",
})
}
}
}
}
+26
View File
@@ -251,6 +251,32 @@ var inputStructs = map[string][]*cmdInfo{
VersionedActions: []string{"update"},
},
},
"managedgroups": {
{
ResourceType: resource.ManagedGroup.String(),
Pkg: "managedgroups",
StdActions: []string{"read", "delete", "list"},
IsAbstractType: true,
Container: "AuthMethod",
HasId: true,
HasName: true,
HasDescription: true,
},
{
ResourceType: resource.ManagedGroup.String(),
Pkg: "managedgroups",
StdActions: []string{"create", "update"},
SubActionPrefix: "oidc",
HasExtraCommandVars: true,
SkipNormalHelp: true,
HasExtraHelpFunc: true,
HasId: true,
HasName: true,
Container: "AuthMethod",
HasDescription: true,
VersionedActions: []string{"update"},
},
},
"roles": {
{
ResourceType: resource.Role.String(),
+1 -1
View File
@@ -195,7 +195,7 @@ func (c *{{ camelCase .SubActionPrefix }}Command) Flags() *base.FlagSets {
set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat)
f := set.NewFlagSet("Command Options")
common.PopulateCommonFlags(c.Command, f, "{{ if .SubActionPrefix }}{{ .SubActionPrefix }}-type {{ end }}{{ lowerSpaceCase .ResourceType }}", flags{{ camelCase .SubActionPrefix }}Map[c.Func])
common.PopulateCommonFlags(c.Command, f, "{{ if .SubActionPrefix }}{{ .SubActionPrefix }}-type {{ end }}{{ lowerSpaceCase .ResourceType }}", flags{{ camelCase .SubActionPrefix }}Map, c.Func)
extra{{ camelCase .SubActionPrefix }}FlagsFunc(c, set, f)
+8
View File
@@ -3200,6 +3200,14 @@
"type": "object",
"description": "The attributes that are applicable for the specific ManagedGroup type."
},
"member_ids": {
"type": "array",
"items": {
"type": "string"
},
"description": "Output only. The IDs of the current set of members (accounts) that are associated with this ManagedGroup.",
"readOnly": true
},
"authorized_actions": {
"type": "array",
"items": {
@@ -52,6 +52,8 @@ type ManagedGroup struct {
AuthMethodId string `protobuf:"bytes,90,opt,name=auth_method_id,proto3" json:"auth_method_id,omitempty"`
// The attributes that are applicable for the specific ManagedGroup type.
Attributes *structpb.Struct `protobuf:"bytes,100,opt,name=attributes,proto3" json:"attributes,omitempty"`
// Output only. The IDs of the current set of members (accounts) that are associated with this ManagedGroup.
MemberIds []string `protobuf:"bytes,110,rep,name=member_ids,proto3" json:"member_ids,omitempty"`
// Output only. The available actions on this resource for this user.
AuthorizedActions []string `protobuf:"bytes,300,rep,name=authorized_actions,proto3" json:"authorized_actions,omitempty"`
}
@@ -158,6 +160,13 @@ func (x *ManagedGroup) GetAttributes() *structpb.Struct {
return nil
}
func (x *ManagedGroup) GetMemberIds() []string {
if x != nil {
return x.MemberIds
}
return nil
}
func (x *ManagedGroup) GetAuthorizedActions() []string {
if x != nil {
return x.AuthorizedActions
@@ -235,7 +244,7 @@ var file_controller_api_resources_managedgroups_v1_managed_group_proto_rawDesc =
0x6f, 0x70, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2a, 0x63, 0x6f, 0x6e, 0x74, 0x72,
0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6f, 0x70, 0x74,
0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd5, 0x04, 0x0a, 0x0c, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf5, 0x04, 0x0a, 0x0c, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65,
0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01,
0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x43, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18,
0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c,
@@ -269,7 +278,9 @@ var file_controller_api_resources_managedgroups_v1_managed_group_proto_rawDesc =
0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x64, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, 0x04, 0xa0, 0xda, 0x29, 0x01,
0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x12,
0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0a,
0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x6e, 0x20, 0x03, 0x28, 0x09,
0x52, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x12, 0x2f, 0x0a, 0x12,
0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f,
0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f,
0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x59, 0x0a,
@@ -43,6 +43,9 @@ message ManagedGroup {
// The attributes that are applicable for the specific ManagedGroup type.
google.protobuf.Struct attributes = 100 [(custom_options.v1.generate_sdk_option) = true];
// Output only. The IDs of the current set of members (accounts) that are associated with this ManagedGroup.
repeated string member_ids = 110 [json_name="member_ids"];
// Output only. The available actions on this resource for this user.
repeated string authorized_actions = 300 [json_name="authorized_actions"];
}
@@ -145,7 +145,7 @@ func (s Service) GetManagedGroup(ctx context.Context, req *pbs.GetManagedGroupRe
if authResults.Error != nil {
return nil, authResults.Error
}
mg, err := s.getFromRepo(ctx, req.GetId())
mg, memberIds, err := s.getFromRepo(ctx, req.GetId())
if err != nil {
return nil, err
}
@@ -163,6 +163,9 @@ func (s Service) GetManagedGroup(ctx context.Context, req *pbs.GetManagedGroupRe
if outputFields.Has(globals.AuthorizedActionsField) {
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, mg.GetPublicId(), IdActions[auth.SubtypeFromId(mg.GetPublicId())]).Strings()))
}
if outputFields.Has(globals.MemberIdsField) {
outputOpts = append(outputOpts, handlers.WithMemberIds(memberIds))
}
item, err := toProto(ctx, mg, outputOpts...)
if err != nil {
@@ -266,26 +269,37 @@ func (s Service) DeleteManagedGroup(ctx context.Context, req *pbs.DeleteManagedG
return nil, nil
}
func (s Service) getFromRepo(ctx context.Context, id string) (auth.ManagedGroup, error) {
func (s Service) getFromRepo(ctx context.Context, id string) (auth.ManagedGroup, []string, error) {
var out auth.ManagedGroup
var memberIds []string
switch auth.SubtypeFromId(id) {
case auth.OidcSubtype:
repo, err := s.oidcRepoFn()
if err != nil {
return nil, err
return nil, nil, err
}
mg, err := repo.LookupManagedGroup(ctx, id)
if err != nil {
if errors.IsNotFoundError(err) {
return nil, handlers.NotFoundErrorf("ManagedGroup %q doesn't exist.", id)
return nil, nil, handlers.NotFoundErrorf("ManagedGroup %q doesn't exist.", id)
}
return nil, nil, err
}
ids, err := repo.ListManagedGroupMembershipsByGroup(ctx, mg.GetPublicId())
if err != nil {
return nil, nil, err
}
if len(ids) > 0 {
memberIds = make([]string, len(ids))
for i, v := range ids {
memberIds[i] = v.MemberId
}
return nil, err
}
out = mg
default:
return nil, handlers.NotFoundErrorf("Unrecognized id.")
return nil, nil, handlers.NotFoundErrorf("Unrecognized id.")
}
return out, nil
return out, memberIds, nil
}
func (s Service) createOidcInRepo(ctx context.Context, am auth.AuthMethod, item *pb.ManagedGroup) (*oidc.ManagedGroup, error) {
@@ -529,6 +543,9 @@ func toProto(ctx context.Context, in auth.ManagedGroup, opt ...handlers.Option)
if outputFields.Has(globals.AuthorizedActionsField) {
out.AuthorizedActions = opts.WithAuthorizedActions
}
if outputFields.Has(globals.MemberIdsField) {
out.MemberIds = opts.WithMemberIds
}
switch i := in.(type) {
case *oidc.ManagedGroup:
if outputFields.Has(globals.TypeField) {
@@ -106,18 +106,33 @@ func TestGet(t *testing.T) {
oidc.WithSigningAlgs(oidc.RS256),
oidc.WithApiUrl(oidc.TestConvertToUrls(t, "https://www.alice.com/callback")[0]),
)
oidcA := oidc.TestAccount(t, conn, oidcAm, "test-subject")
omg := oidc.TestManagedGroup(t, conn, oidcAm, testFakeManagedGroupFilter)
// Set up managed group before getting. First get the current
// managed group to make sure we have the right version, then ensure
// the account is a member so we can test that return value.
oidcRepo, err := oidcRepoFn()
require.NoError(t, err)
currMg, err := oidcRepo.LookupManagedGroup(ctx, omg.GetPublicId())
require.NoError(t, err)
_, _, err = oidcRepo.SetManagedGroupMemberships(ctx, oidcAm, oidcA, []*oidc.ManagedGroup{currMg})
require.NoError(t, err)
// Fetch the group once more to get the updated time
currMg, err = oidcRepo.LookupManagedGroup(ctx, omg.GetPublicId())
require.NoError(t, err)
oidcWireManagedGroup := pb.ManagedGroup{
Id: omg.GetPublicId(),
AuthMethodId: omg.GetAuthMethodId(),
CreatedTime: omg.GetCreateTime().GetTimestamp(),
UpdatedTime: omg.GetUpdateTime().GetTimestamp(),
UpdatedTime: currMg.GetUpdateTime().GetTimestamp(),
Scope: &scopepb.ScopeInfo{Id: org.GetPublicId(), Type: scope.Org.String(), ParentScopeId: scope.Global.String()},
Version: 1,
Version: currMg.Version,
Type: "oidc",
Attributes: &structpb.Struct{Fields: map[string]*structpb.Value{"filter": structpb.NewStringValue(omg.GetFilter())}},
AuthorizedActions: oidcAuthorizedActions,
MemberIds: []string{oidcA.GetPublicId()},
}
cases := []struct {
@@ -72,4 +72,16 @@ func Test_GetOpts(t *testing.T) {
opts = GetOpts(WithManagedGroupIds(out))
require.Equal(out, opts.WithManagedGroupIds)
})
t.Run("WithMemberIds", func(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
opts := GetOpts()
assert.Nil(opts.WithMemberIds)
out := []string{"foobar"}
opts = GetOpts(WithMemberIds(out))
require.Equal(out, opts.WithMemberIds)
})
}
@@ -31,6 +31,7 @@ type options struct {
WithAuthorizedActions []string
WithAuthorizedCollectionActions map[string]*structpb.ListValue
WithManagedGroupIds []string
WithMemberIds []string
}
func getDefaultOptions() options {
@@ -100,3 +101,11 @@ func WithManagedGroupIds(ids []string) Option {
o.WithManagedGroupIds = ids
}
}
// WithMemberIds provides an option when creating responses to include the given
// member IDs if allowed
func WithMemberIds(ids []string) Option {
return func(o *options) {
o.WithMemberIds = ids
}
}