Add read:self and cancel:self to sessions; add dev unpriv user (#923)

* Sessions support for read:self and cancel:self permissions. This is automatic with fallback; no API change is needed.

* Strips out values from listing if only self actions are allowed on the resource and the user ID is different. This prevents a default ACL of id=*;type=session;actions=read:self,cancel:self from causing all values to be visible when listing.

* Adds an unprivileged user to development/testing instances. This user has only default permissions plus the ability to authorize sessions in the default target.
This commit is contained in:
Jeff Mitchell authored and GitHub committed 2021-02-11 13:14:18 -05:00
1 parent d279a906ce
commit 22462cdd84
28 files changed
+626 -196

No files matched your search

+154 -63
View File
@@ -48,7 +48,7 @@ func (b *Server) CreateInitialLoginRole(ctx context.Context) (*iam.Role, error)
pr, err := iam.NewRole(scope.Global.String(),
iam.WithName("Login and Default Grants"),
iam.WithDescription(`Role created for login capability and account self-management for users of the global scope at its creation time`),
iam.WithDescription(`Role created for login capability, account self-management, and other default grants for users of the global scope at its creation time`),
)
if err != nil {
return nil, fmt.Errorf("error creating in memory role for generated grants: %w", err)
@@ -125,78 +125,110 @@ func (b *Server) CreateInitialAuthMethod(ctx context.Context) (*password.AuthMet
b.InfoKeys = append(b.InfoKeys, "generated auth method id")
b.Info["generated auth method id"] = b.DevAuthMethodId
// Create the dev user
if b.DevLoginName == "" {
b.DevLoginName, err = base62.Random(10)
if err != nil {
return nil, nil, fmt.Errorf("unable to generate login name: %w", err)
createUser := func(loginName, loginPassword, userId string, admin bool) (*iam.User, error) {
// Create the dev admin user
if loginName == "" {
b.DevLoginName, err = base62.Random(10)
if err != nil {
return nil, fmt.Errorf("unable to generate login name: %w", err)
}
loginName = strings.ToLower(b.DevLoginName)
}
b.DevLoginName = strings.ToLower(b.DevLoginName)
}
if b.DevPassword == "" {
b.DevPassword, err = base62.Random(20)
if err != nil {
return nil, nil, fmt.Errorf("unable to generate password: %w", err)
if b.DevPassword == "" {
b.DevPassword, err = base62.Random(20)
if err != nil {
return nil, fmt.Errorf("unable to generate password: %w", err)
}
loginPassword = b.DevPassword
}
}
b.InfoKeys = append(b.InfoKeys, "generated auth method password")
b.Info["generated auth method password"] = b.DevPassword
typeStr := "admin"
if !admin {
typeStr = "unprivileged"
}
b.InfoKeys = append(b.InfoKeys, fmt.Sprintf("generated %s password", typeStr))
b.Info[fmt.Sprintf("generated %s password", typeStr)] = loginPassword
acct, err := password.NewAccount(b.DevAuthMethodId, password.WithLoginName(b.DevLoginName))
if err != nil {
return nil, nil, fmt.Errorf("error creating new in memory auth account: %w", err)
}
acct, err = pwRepo.CreateAccount(cancelCtx, scope.Global.String(), acct, password.WithPassword(b.DevPassword))
if err != nil {
return nil, nil, fmt.Errorf("error saving auth account to the db: %w", err)
}
b.InfoKeys = append(b.InfoKeys, "generated auth method login name")
b.Info["generated auth method login name"] = acct.GetLoginName()
acct, err := password.NewAccount(am.PublicId, password.WithLoginName(loginName))
if err != nil {
return nil, fmt.Errorf("error creating new in memory auth account: %w", err)
}
acct, err = pwRepo.CreateAccount(cancelCtx, scope.Global.String(), acct, password.WithPassword(loginPassword))
if err != nil {
return nil, fmt.Errorf("error saving auth account to the db: %w", err)
}
b.InfoKeys = append(b.InfoKeys, fmt.Sprintf("generated %s login name", typeStr))
b.Info[fmt.Sprintf("generated %s login name", typeStr)] = acct.GetLoginName()
iamRepo, err := iam.NewRepository(rw, rw, kmsCache, iam.WithRandomReader(b.SecureRandomReader))
if err != nil {
return nil, nil, fmt.Errorf("unable to create repo for org id: %w", err)
iamRepo, err := iam.NewRepository(rw, rw, kmsCache, iam.WithRandomReader(b.SecureRandomReader))
if err != nil {
return nil, fmt.Errorf("unable to create repo for org id: %w", err)
}
// Create a new user and associate it with the account
if userId == "" {
userId, err = db.NewPublicId(iam.UserPrefix)
if err != nil {
return nil, fmt.Errorf("error generating initial user id: %w", err)
}
}
opts := []iam.Option{
iam.WithPublicId(userId),
}
if admin {
opts = append(opts,
iam.WithName("admin"),
iam.WithDescription(`Initial admin user within the "global" scope`),
)
} else {
opts = append(opts,
iam.WithName("user"),
iam.WithDescription("Initial unprivileged user"),
)
}
u, err := iam.NewUser(scope.Global.String(), opts...)
if err != nil {
return nil, fmt.Errorf("error creating in memory user: %w", err)
}
if u, err = iamRepo.CreateUser(cancelCtx, u, opts...); err != nil {
return nil, fmt.Errorf("error creating initial %s user: %w", typeStr, err)
}
if _, err = iamRepo.AddUserAccounts(cancelCtx, u.GetPublicId(), u.GetVersion(), []string{acct.GetPublicId()}); err != nil {
return nil, fmt.Errorf("error associating initial %s user with account: %w", typeStr, err)
}
if !admin {
return u, nil
}
// Create a role tying them together
pr, err := iam.NewRole(scope.Global.String(),
iam.WithName("Administration"),
iam.WithDescription(`Provides admin grants within the "global" scope to the initial user`),
)
if err != nil {
return nil, fmt.Errorf("error creating in memory role for generated grants: %w", err)
}
defPermsRole, err := iamRepo.CreateRole(cancelCtx, pr)
if err != nil {
return nil, fmt.Errorf("error creating role for default generated grants: %w", err)
}
if _, err := iamRepo.AddRoleGrants(cancelCtx, defPermsRole.PublicId, defPermsRole.Version, []string{"id=*;type=*;actions=*"}); err != nil {
return nil, fmt.Errorf("error creating grant for default generated grants: %w", err)
}
if _, err := iamRepo.AddPrincipalRoles(cancelCtx, defPermsRole.PublicId, defPermsRole.Version+1, []string{u.GetPublicId()}, nil); err != nil {
return nil, fmt.Errorf("error adding principal to role for default generated grants: %w", err)
}
return u, nil
}
// Create a new user and associate it with the account
if b.DevUserId == "" {
b.DevUserId, err = db.NewPublicId(iam.UserPrefix)
if b.DevUnprivilegedLoginName != "" {
unprivUser, err := createUser(b.DevUnprivilegedLoginName, b.DevUnprivilegedPassword, b.DevUnprivilegedUserId, false)
if err != nil {
return nil, nil, fmt.Errorf("error generating initial user id: %w", err)
return nil, nil, err
}
b.DevUnprivilegedUserId = unprivUser.GetPublicId()
}
opts := []iam.Option{
iam.WithName("admin"),
iam.WithDescription(`Initial admin user within the "global" scope`),
iam.WithPublicId(b.DevUserId),
}
u, err := iam.NewUser(scope.Global.String(), opts...)
u, err := createUser(b.DevLoginName, b.DevPassword, b.DevUserId, true)
if err != nil {
return nil, nil, fmt.Errorf("error creating in memory user: %w", err)
}
if u, err = iamRepo.CreateUser(cancelCtx, u, opts...); err != nil {
return nil, nil, fmt.Errorf("error creating initial admin user: %w", err)
}
if _, err = iamRepo.AddUserAccounts(cancelCtx, u.GetPublicId(), u.GetVersion(), []string{acct.GetPublicId()}); err != nil {
return nil, nil, fmt.Errorf("error associating initial admin user with account: %w", err)
}
// Create a role tying them together
pr, err := iam.NewRole(scope.Global.String(),
iam.WithName("Administration"),
iam.WithDescription(`Provides admin grants within the "global" scope to the initial user`),
)
if err != nil {
return nil, nil, fmt.Errorf("error creating in memory role for generated grants: %w", err)
}
defPermsRole, err := iamRepo.CreateRole(cancelCtx, pr)
if err != nil {
return nil, nil, fmt.Errorf("error creating role for default generated grants: %w", err)
}
if _, err := iamRepo.AddRoleGrants(cancelCtx, defPermsRole.PublicId, defPermsRole.Version, []string{"id=*;type=*;actions=*"}); err != nil {
return nil, nil, fmt.Errorf("error creating grant for default generated grants: %w", err)
}
if _, err := iamRepo.AddPrincipalRoles(cancelCtx, defPermsRole.PublicId, defPermsRole.Version+1, []string{u.GetPublicId()}, nil); err != nil {
return nil, nil, fmt.Errorf("error adding principal to role for default generated grants: %w", err)
return nil, nil, err
}
return am, u, nil
@@ -456,5 +488,64 @@ func (b *Server) CreateInitialTarget(ctx context.Context) (target.Target, error)
b.InfoKeys = append(b.InfoKeys, "generated target id")
b.Info["generated target id"] = b.DevTargetId
// If we have an unprivileged dev user, add user to the role that grants
// list/read:self/cancel:self, and an authorize-session role
if b.DevUnprivilegedUserId != "" {
iamRepo, err := iam.NewRepository(rw, rw, kmsCache, iam.WithRandomReader(b.SecureRandomReader))
if err != nil {
return nil, fmt.Errorf("unable to create repo for unprivileged user target connection role: %w", err)
}
roles, err := iamRepo.ListRoles(ctx, []string{b.DevProjectId})
if err != nil {
return nil, fmt.Errorf("unable to list existing roles in project: %w", err)
}
if len(roles) != 2 {
panic("")
return nil, fmt.Errorf("unexpected number of roles in default project, expected 2, got %d", len(roles))
}
var idx int = -1
for i, r := range roles {
// Hacky, I know, but saves a DB trip to look up other
// characteristics like "if any principals are currently attached".
// No matter what we pick here it's a bit heuristical.
if r.Name == "Default Grants" {
idx = i
break
}
}
if idx == -1 {
return nil, fmt.Errorf("couldn't find default grants role in default project")
}
if _, err := iamRepo.AddPrincipalRoles(ctx,
roles[idx].PublicId,
roles[idx].Version,
[]string{b.DevUnprivilegedUserId}); err != nil {
return nil, fmt.Errorf("error adding unpriv user ID to project default role: %w", err)
}
pr, err := iam.NewRole(b.DevProjectId,
iam.WithName("Unprivileged User Session Authorization"),
iam.WithDescription(`Provides grants within the dev project scope to allow the initial unprivileged user to authorize sessions against the dev target`),
)
if err != nil {
return nil, fmt.Errorf("error creating in memory role for generated grants: %w", err)
}
sessionRole, err := iamRepo.CreateRole(cancelCtx, pr)
if err != nil {
return nil, fmt.Errorf("error creating role for unprivileged user generated grants: %w", err)
}
if _, err := iamRepo.AddRoleGrants(cancelCtx,
sessionRole.PublicId,
sessionRole.Version,
[]string{fmt.Sprintf("id=%s;actions=authorize-session", b.DevTargetId)},
); err != nil {
return nil, fmt.Errorf("error creating grant for unprivileged user generated grants: %w", err)
}
if _, err := iamRepo.AddPrincipalRoles(cancelCtx, sessionRole.PublicId, sessionRole.Version+1, []string{b.DevUnprivilegedUserId}, nil); err != nil {
return nil, fmt.Errorf("error adding principal to role for unprivileged user generated grants: %w", err)
}
}
return tt, nil
}
+3
View File
@@ -71,6 +71,9 @@ type Server struct {
DevLoginName string
DevPassword string
DevUserId string
DevUnprivilegedLoginName string
DevUnprivilegedPassword string
DevUnprivilegedUserId string
DevOrgId string
DevProjectId string
DevHostCatalogId string
+22
View File
@@ -218,6 +218,17 @@ func (c *Command) Run(args []string) int {
c.UI.Error(fmt.Sprintf("An error occurred attempting to read the password. The raw error message is shown below but usually this is because you attempted to pipe a value into the command or you are executing outside of a terminal (TTY). The raw error was:\n\n%s", err.Error()))
return 2
}
fmt.Print("Please enter it one more time for confirmation: ")
confirmation, err := password.Read(os.Stdin)
fmt.Print("\n")
if err != nil {
c.UI.Error(fmt.Sprintf("An error occurred attempting to read the password. The raw error message is shown below but usually this is because you attempted to pipe a value into the command or you are executing outside of a terminal (TTY). The raw error was:\n\n%s", err.Error()))
return 2
}
if strings.TrimSpace(value) != strings.TrimSpace(confirmation) {
c.UI.Error("Entered password and confirmation value did not match.")
return 2
}
c.flagPassword = strings.TrimSpace(value)
}
@@ -240,6 +251,17 @@ func (c *Command) Run(args []string) int {
c.UI.Error(fmt.Sprintf("An error occurred attempting to read the password. The raw error message is shown below but usually this is because you attempted to pipe a value into the command or you are executing outside of a terminal (TTY). The raw error was:\n\n%s", err.Error()))
return 2
}
fmt.Print("Please enter it one more time for confirmation: ")
confirmation, err := password.Read(os.Stdin)
fmt.Print("\n")
if err != nil {
c.UI.Error(fmt.Sprintf("An error occurred attempting to read the password. The raw error message is shown below but usually this is because you attempted to pipe a value into the command or you are executing outside of a terminal (TTY). The raw error was:\n\n%s", err.Error()))
return 2
}
if strings.TrimSpace(value) != strings.TrimSpace(confirmation) {
c.UI.Error("Entered password and confirmation value did not match.")
return 2
}
c.flagNewPassword = strings.TrimSpace(value)
}
@@ -172,6 +172,17 @@ func (c *PasswordCommand) Run(args []string) int {
c.UI.Error(fmt.Sprintf("An error occurred attempting to read the password. The raw error message is shown below but usually this is because you attempted to pipe a value into the command or you are executing outside of a terminal (TTY). The raw error was:\n\n%s", err.Error()))
return 2
}
fmt.Print("Please enter it one more time for confirmation: ")
confirmation, err := password.Read(os.Stdin)
fmt.Print("\n")
if err != nil {
c.UI.Error(fmt.Sprintf("An error occurred attempting to read the password. The raw error message is shown below but usually this is because you attempted to pipe a value into the command or you are executing outside of a terminal (TTY). The raw error was:\n\n%s", err.Error()))
return 2
}
if strings.TrimSpace(value) != strings.TrimSpace(confirmation) {
c.UI.Error("Entered password and confirmation value did not match.")
return 2
}
opts = append(opts, accounts.WithPasswordAccountPassword(strings.TrimSpace(value)))
default:
opts = append(opts, accounts.WithPasswordAccountPassword(c.flagPassword))
+27 -1
View File
@@ -17,6 +17,7 @@ import (
"github.com/hashicorp/boundary/internal/servers/worker"
"github.com/hashicorp/boundary/internal/target"
"github.com/hashicorp/boundary/internal/types/scope"
"github.com/hashicorp/boundary/sdk/strutil"
"github.com/mitchellh/cli"
"github.com/posener/complete"
)
@@ -43,6 +44,8 @@ type Command struct {
flagCombineLogs bool
flagLoginName string
flagPassword string
flagUnprivilegedLoginName string
flagUnprivilegedPassword string
flagIdSuffix string
flagHostAddress string
flagTargetDefaultPort int
@@ -111,7 +114,7 @@ func (c *Command) Flags() *base.FlagSets {
Target: &c.flagPassword,
Default: "password",
EnvVar: "BOUNDARY_DEV_PASSWORD",
Usage: "Initial login password. If set to the empty string, one will be autogenerated.",
Usage: "Initial admin login password. If set to the empty string, one will be autogenerated.",
})
f.StringVar(&base.StringVar{
@@ -122,6 +125,22 @@ func (c *Command) Flags() *base.FlagSets {
Usage: "Initial admin login name. If set to the empty string, one will be autogenerated.",
})
f.StringVar(&base.StringVar{
Name: "unprivileged-password",
Target: &c.flagUnprivilegedPassword,
Default: "password",
EnvVar: "BOUNDARY_DEV_UNPRIVILEGED_PASSWORD",
Usage: "Initial unprivileged user login password. If set to the empty string, one will be autogenerated.",
})
f.StringVar(&base.StringVar{
Name: "unprivileged-login-name",
Target: &c.flagUnprivilegedLoginName,
Default: "user",
EnvVar: "BOUNDARY_DEV_UNPRIVILEGED_LOGIN_NAME",
Usage: "Initial unprivileged user name. If set to the empty string, one will be autogenerated.",
})
f.StringVar(&base.StringVar{
Name: "api-listen-address",
Target: &c.flagControllerAPIListenAddr,
@@ -261,6 +280,7 @@ func (c *Command) Run(args []string) int {
}
c.DevAuthMethodId = fmt.Sprintf("%s_%s", password.AuthMethodPrefix, c.flagIdSuffix)
c.DevUserId = fmt.Sprintf("%s_%s", iam.UserPrefix, c.flagIdSuffix)
c.DevUnprivilegedUserId = "u_" + strutil.Reverse(strings.TrimPrefix(c.DevUserId, "u_"))
c.DevOrgId = fmt.Sprintf("%s_%s", scope.Org.Prefix(), c.flagIdSuffix)
c.DevProjectId = fmt.Sprintf("%s_%s", scope.Project.Prefix(), c.flagIdSuffix)
c.DevHostCatalogId = fmt.Sprintf("%s_%s", static.HostCatalogPrefix, c.flagIdSuffix)
@@ -274,6 +294,12 @@ func (c *Command) Run(args []string) int {
if c.flagPassword != "" {
c.DevPassword = c.flagPassword
}
if c.flagUnprivilegedLoginName != "" {
c.DevUnprivilegedLoginName = c.flagUnprivilegedLoginName
}
if c.flagUnprivilegedPassword != "" {
c.DevUnprivilegedPassword = c.flagUnprivilegedPassword
}
c.DevTargetDefaultPort = c.flagTargetDefaultPort
host, port, err := net.SplitHostPort(c.flagHostAddress)
if err != nil {
+39 -18
View File
@@ -126,7 +126,7 @@ func (r *Repository) CreateScope(ctx context.Context, s *Scope, userId string, o
var defaultRoleMetadata oplog.Metadata
var defaultRole *Role
var defaultRoleRaw interface{}
if !opts.withSkipDefaultRoleCreation && s.Type == scope.Org.String() {
if !opts.withSkipDefaultRoleCreation {
defaultRole, err = NewRole(scopePublicId)
if err != nil {
return nil, errors.Wrap(err, op, errors.WithMsg("error instantiating new default role"))
@@ -136,8 +136,14 @@ func (r *Repository) CreateScope(ctx context.Context, s *Scope, userId string, o
return nil, errors.Wrap(err, op, errors.WithMsg("error generating public id for new default role"))
}
defaultRole.PublicId = defaultRolePublicId
defaultRole.Name = "Login and Default Grants"
defaultRole.Description = fmt.Sprintf("Role created for login capability and account self-management for users of scope %s at its creation time", scopePublicId)
switch s.Type {
case scope.Project.String():
defaultRole.Name = "Default Grants"
defaultRole.Description = fmt.Sprintf("Role created to provide default grants to users of scope %s at its creation time", scopePublicId)
default:
defaultRole.Name = "Login and Default Grants"
defaultRole.Description = fmt.Sprintf("Role created for login capability, account self-management, and other default grants for users of scope %s at its creation time", scopePublicId)
}
defaultRoleRaw = defaultRole
defaultRoleMetadata = oplog.Metadata{
"resource-public-id": []string{defaultRolePublicId},
@@ -280,23 +286,34 @@ func (r *Repository) CreateScope(ctx context.Context, s *Scope, userId string, o
// Grants
{
grants := []interface{}{}
roleGrant, err := NewRoleGrant(defaultRolePublicId, "id=*;type=scope;actions=list,read")
if err != nil {
return errors.Wrap(err, op, errors.WithMsg("unable to create in memory role grant"))
}
grants = append(grants, roleGrant)
roleGrant, err = NewRoleGrant(defaultRolePublicId, "id=*;type=auth-method;actions=authenticate,list")
if err != nil {
return errors.Wrap(err, op, errors.WithMsg("unable to create in memory role grant"))
}
grants = append(grants, roleGrant)
switch s.Type {
case scope.Project.String():
roleGrant, err := NewRoleGrant(defaultRolePublicId, "id=*;type=session;actions=list,read:self,cancel:self")
if err != nil {
return errors.Wrap(err, op, errors.WithMsg("unable to create in memory role grant"))
}
grants = append(grants, roleGrant)
roleGrant, err = NewRoleGrant(defaultRolePublicId, "id={{account.id}};actions=read,change-password")
if err != nil {
return errors.Wrap(err, op, errors.WithMsg("unable to create in memory role grant"))
default:
roleGrant, err := NewRoleGrant(defaultRolePublicId, "id=*;type=scope;actions=list,read")
if err != nil {
return errors.Wrap(err, op, errors.WithMsg("unable to create in memory role grant"))
}
grants = append(grants, roleGrant)
roleGrant, err = NewRoleGrant(defaultRolePublicId, "id=*;type=auth-method;actions=authenticate,list")
if err != nil {
return errors.Wrap(err, op, errors.WithMsg("unable to create in memory role grant"))
}
grants = append(grants, roleGrant)
roleGrant, err = NewRoleGrant(defaultRolePublicId, "id={{account.id}};actions=read,change-password")
if err != nil {
return errors.Wrap(err, op, errors.WithMsg("unable to create in memory role grant"))
}
grants = append(grants, roleGrant)
}
grants = append(grants, roleGrant)
roleGrantOplogMsgs := make([]*oplog.Message, 0, 3)
if err := w.CreateItems(ctx, grants, db.NewOplogMsgs(&roleGrantOplogMsgs)); err != nil {
@@ -308,7 +325,11 @@ func (r *Repository) CreateScope(ctx context.Context, s *Scope, userId string, o
// Principals
{
principals := []interface{}{}
rolePrincipal, err := NewUserRole(defaultRolePublicId, "u_anon")
userId := "u_anon"
if s.Type == scope.Project.String() {
userId = "u_auth"
}
rolePrincipal, err := NewUserRole(defaultRolePublicId, userId)
if err != nil {
return errors.Wrap(err, op, errors.WithMsg("unable to create in memory role user"))
}
@@ -717,7 +717,7 @@ func TestCreate(t *testing.T) {
require.NoError(err)
switch tc.scopeId {
case defaultOrg.PublicId:
require.Len(roles, 1)
require.Len(roles, 2)
case "global":
require.Len(roles, 2)
}
@@ -726,7 +726,9 @@ func TestCreate(t *testing.T) {
case "Administration":
assert.Equal(fmt.Sprintf("Role created for administration of scope %s by user %s at its creation time", got.GetItem().GetId(), userId), role.GetDescription())
case "Login and Default Grants":
assert.Equal(fmt.Sprintf("Role created for login capability and account self-management for users of scope %s at its creation time", got.GetItem().GetId()), role.GetDescription())
assert.Equal(fmt.Sprintf("Role created for login capability, account self-management, and other default grants for users of scope %s at its creation time", got.GetItem().GetId()), role.GetDescription())
case "Default Grants":
assert.Equal(fmt.Sprintf("Role created to provide default grants to users of scope %s at its creation time", got.GetItem().GetId()), role.GetDescription())
default:
t.Fatal("unexpected role name", role.GetName())
}
@@ -25,7 +25,9 @@ var (
// individual resources
IdActions = action.ActionSet{
action.Read,
action.ReadSelf,
action.Cancel,
action.CancelSelf,
}
// CollectionActions contains the set of actions that can be performed on
@@ -61,7 +63,7 @@ func (s Service) GetSession(ctx context.Context, req *pbs.GetSessionRequest) (*p
if err := validateGetRequest(req); err != nil {
return nil, err
}
authResults := s.authResult(ctx, req.GetId(), action.Read)
authResults := s.authResult(ctx, req.GetId(), action.ReadSelf)
if authResults.Error != nil {
return nil, authResults.Error
}
@@ -69,8 +71,24 @@ func (s Service) GetSession(ctx context.Context, req *pbs.GetSessionRequest) (*p
if err != nil {
return nil, err
}
authzdActions := authResults.FetchActionSetForId(ctx, ses.Id, IdActions)
// Check to see if we need to verify Read vs. just ReadSelf
if ses.GetUserId() != authResults.UserId {
var found bool
for _, v := range authzdActions {
if v == action.Read {
found = true
break
}
}
if !found {
return nil, handlers.ForbiddenError()
}
}
ses.Scope = authResults.Scope
ses.AuthorizedActions = authResults.FetchActionSetForId(ctx, ses.Id, IdActions).Strings()
ses.AuthorizedActions = authzdActions.Strings()
return &pbs.GetSessionResponse{Item: ses}, nil
}
@@ -101,9 +119,19 @@ func (s Service) ListSessions(ctx context.Context, req *pbs.ListSessionsRequest)
for _, item := range seslist {
item.Scope = scopeInfoMap[item.GetScopeId()]
res.ScopeId = item.Scope.Id
item.AuthorizedActions = authResults.FetchActionSetForId(ctx, item.Id, IdActions, auth.WithResource(res)).Strings()
if len(item.AuthorizedActions) > 0 {
finalItems = append(finalItems, item)
authorizedActions := authResults.FetchActionSetForId(ctx, item.Id, IdActions, auth.WithResource(res))
if len(authorizedActions) > 0 {
onlySelf := true
for _, v := range authorizedActions {
if v != action.ReadSelf && v != action.CancelSelf {
onlySelf = false
break
}
}
if !onlySelf || item.GetUserId() == authResults.UserId {
item.AuthorizedActions = authorizedActions.Strings()
finalItems = append(finalItems, item)
}
}
}
@@ -115,16 +143,36 @@ func (s Service) CancelSession(ctx context.Context, req *pbs.CancelSessionReques
if err := validateCancelRequest(req); err != nil {
return nil, err
}
authResults := s.authResult(ctx, req.GetId(), action.Cancel)
authResults := s.authResult(ctx, req.GetId(), action.CancelSelf)
if authResults.Error != nil {
return nil, authResults.Error
}
ses, err := s.cancelInRepo(ctx, req.GetId(), req.GetVersion())
ses, err := s.getFromRepo(ctx, req.GetId())
if err != nil {
return nil, err
}
authzdActions := authResults.FetchActionSetForId(ctx, ses.Id, IdActions)
// Check to see if we need to verify Read vs. just ReadSelf
if ses.GetUserId() != authResults.UserId {
var found bool
for _, v := range authzdActions {
if v == action.Cancel {
found = true
break
}
}
if !found {
return nil, handlers.ForbiddenError()
}
}
ses, err = s.cancelInRepo(ctx, req.GetId(), req.GetVersion())
if err != nil {
return nil, err
}
ses.Scope = authResults.Scope
ses.AuthorizedActions = authResults.FetchActionSetForId(ctx, ses.Id, IdActions).Strings()
ses.AuthorizedActions = authzdActions.Strings()
return &pbs.CancelSessionResponse{Item: ses}, nil
}
@@ -196,7 +244,7 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
res.Error = handlers.NotFoundError()
return res
}
case action.Read, action.Cancel:
case action.Read, action.ReadSelf, action.Cancel, action.CancelSelf:
repo, err := s.repoFn()
if err != nil {
res.Error = err
@@ -81,7 +81,7 @@ func TestGetSession(t *testing.T) {
States: []*pb.SessionState{{Status: session.StatusPending.String(), StartTime: sess.CreateTime.GetTimestamp()}},
Certificate: sess.Certificate,
Type: target.TcpSubType.String(),
AuthorizedActions: []string{"read", "cancel"},
AuthorizedActions: []string{"read", "read:self", "cancel", "cancel:self"},
}
cases := []struct {
@@ -210,7 +210,7 @@ func TestList(t *testing.T) {
States: states,
Certificate: sess.Certificate,
Type: target.TcpSubType.String(),
AuthorizedActions: []string{"read", "cancel"},
AuthorizedActions: []string{"read", "read:self", "cancel", "cancel:self"},
})
totalSession = append(totalSession, wantSession[i])
@@ -245,7 +245,7 @@ func TestList(t *testing.T) {
States: states,
Certificate: sess.Certificate,
Type: target.TcpSubType.String(),
AuthorizedActions: []string{"read", "cancel"},
AuthorizedActions: []string{"read", "read:self", "cancel", "cancel:self"},
})
}
@@ -362,7 +362,7 @@ func TestCancel(t *testing.T) {
Status: session.StatusCanceling.String(),
Certificate: sess.Certificate,
Type: target.TcpSubType.String(),
AuthorizedActions: []string{"read", "cancel"},
AuthorizedActions: []string{"read", "read:self", "cancel", "cancel:self"},
}
version := wireSess.GetVersion()
+3 -9
View File
@@ -15,18 +15,12 @@ import (
func TestAuthenticationMulti(t *testing.T) {
assert, require := assert.New(t), require.New(t)
amId := "ampw_1234567890"
user := "user"
password := "passpass"
logger := hclog.New(&hclog.LoggerOptions{
Level: hclog.Trace,
})
c1 := controller.NewTestController(t, &controller.TestControllerOpts{
DefaultAuthMethodId: amId,
DefaultLoginName: user,
DefaultPassword: password,
Logger: logger.Named("c1"),
Logger: logger.Named("c1"),
})
defer c1.Shutdown()
@@ -36,14 +30,14 @@ func TestAuthenticationMulti(t *testing.T) {
defer c2.Shutdown()
auth := authmethods.NewClient(c1.Client())
token1Result, err := auth.Authenticate(c1.Context(), amId, map[string]interface{}{"login_name": user, "password": password})
token1Result, err := auth.Authenticate(c1.Context(), c1.Server().DevAuthMethodId, map[string]interface{}{"login_name": c1.Server().DevLoginName, "password": c1.Server().DevPassword})
require.Nil(err)
token1 := token1Result.Item
require.NotNil(token1)
time.Sleep(5 * time.Second)
auth = authmethods.NewClient(c2.Client())
token2Result, err := auth.Authenticate(c2.Context(), amId, map[string]interface{}{"login_name": user, "password": password})
token2Result, err := auth.Authenticate(c2.Context(), c2.Server().DevAuthMethodId, map[string]interface{}{"login_name": c2.Server().DevLoginName, "password": c2.Server().DevPassword})
require.Nil(err)
token2 := token2Result.Item
require.NotNil(token2)
+49 -5
View File
@@ -18,6 +18,7 @@ import (
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/servers"
"github.com/hashicorp/boundary/sdk/strutil"
"github.com/hashicorp/go-hclog"
wrapping "github.com/hashicorp/go-kms-wrapping"
"github.com/hashicorp/vault/sdk/helper/base62"
@@ -25,9 +26,11 @@ import (
)
const (
DefaultTestAuthMethodId = "ampw_1234567890"
DefaultTestLoginName = "user"
DefaultTestPassword = "passpass"
DefaultTestAuthMethodId = "ampw_1234567890"
DefaultTestLoginName = "admin"
DefaultTestUnprivilegedLoginName = "user"
DefaultTestPassword = "passpass"
DefaultTestUserId = "u_1234567890"
)
// TestController wraps a base.Server and Controller to provide a
@@ -46,6 +49,11 @@ type TestController struct {
opts *TestControllerOpts
}
// Server returns the underlying base server
func (tc *TestController) Server() *base.Server {
return tc.b
}
// Controller returns the underlying controller
func (tc *TestController) Controller() *Controller {
return tc.c
@@ -135,6 +143,26 @@ func (tc *TestController) Token() *authtokens.AuthToken {
return token.Item
}
func (tc *TestController) UnprivilegedToken() *authtokens.AuthToken {
if tc.opts.DisableAuthMethodCreation {
tc.t.Error("no default auth method ID configured")
return nil
}
token, err := authmethods.NewClient(tc.Client()).Authenticate(
tc.Context(),
tc.b.DevAuthMethodId,
map[string]interface{}{
"login_name": tc.b.DevUnprivilegedLoginName,
"password": tc.b.DevUnprivilegedPassword,
},
)
if err != nil {
tc.t.Error(fmt.Errorf("error logging in: %w", err))
return nil
}
return token.Item
}
func (tc *TestController) addrs(purpose string) []string {
var prefix string
switch purpose {
@@ -242,10 +270,13 @@ type TestControllerOpts struct {
// DefaultAuthMethodId is the default auth method ID to use, if set.
DefaultAuthMethodId string
// DefaultLoginName is the login name used when creating the default account.
// DefaultLoginName is the login name used when creating the default admin account.
DefaultLoginName string
// DefaultPassword is the password used when creating the default account.
// DefaultUnprivilegedLoginName is the login name used when creating the default unprivileged account.
DefaultUnprivilegedLoginName string
// DefaultPassword is the password used when creating the default accounts.
DefaultPassword string
// DisableInitialLoginRoleCreation can be set true to disable creating the
@@ -360,10 +391,17 @@ func NewTestController(t *testing.T, opts *TestControllerOpts) *TestController {
} else {
tc.b.DevLoginName = DefaultTestLoginName
}
if opts.DefaultUnprivilegedLoginName != "" {
tc.b.DevUnprivilegedLoginName = opts.DefaultUnprivilegedLoginName
} else {
tc.b.DevUnprivilegedLoginName = DefaultTestUnprivilegedLoginName
}
if opts.DefaultPassword != "" {
tc.b.DevPassword = opts.DefaultPassword
tc.b.DevUnprivilegedPassword = opts.DefaultPassword
} else {
tc.b.DevPassword = DefaultTestPassword
tc.b.DevUnprivilegedPassword = DefaultTestPassword
}
// Start a logger
@@ -396,7 +434,11 @@ func NewTestController(t *testing.T, opts *TestControllerOpts) *TestController {
tc.b.DevProjectId = "p_" + suffix
tc.b.DevTargetId = "ttcp_" + suffix
tc.b.DevUserId = "u_" + suffix
tc.b.DevUnprivilegedUserId = "u_" + strutil.Reverse(strings.TrimPrefix(tc.b.DevUserId, "u_"))
} else {
tc.b.DevUserId = DefaultTestUserId
}
tc.b.DevUnprivilegedUserId = "u_" + strutil.Reverse(strings.TrimPrefix(tc.b.DevUserId, "u_"))
// Set up KMSes
switch {
@@ -509,6 +551,8 @@ func (tc *TestController) AddClusterControllerMember(t *testing.T, opts *TestCon
RecoveryKms: tc.c.conf.RecoveryKms,
Name: opts.Name,
Logger: tc.c.conf.Logger,
DefaultLoginName: tc.b.DevLoginName,
DefaultPassword: tc.b.DevPassword,
DisableKmsKeyCreation: true,
DisableAuthMethodCreation: true,
}
+22 -11
View File
@@ -3,7 +3,6 @@ package accounts_test
import (
"fmt"
"net/http"
"os"
"testing"
"github.com/hashicorp/boundary/api"
@@ -17,8 +16,6 @@ import (
)
func TestList(t *testing.T) {
os.Setenv("BOUNDARY_LOG_URLS", "1")
os.Setenv("BOUNDARY_DEV_SKIP_AUTHZ", "1")
assert, require := assert.New(t), require.New(t)
tc := controller.NewTestController(t, nil)
defer tc.Shutdown()
@@ -126,23 +123,37 @@ func TestCustomMethods(t *testing.T) {
client.SetToken(token.Token)
amId := token.AuthMethodId
accountClient := accounts.NewClient(client)
adminAccountClient := accounts.NewClient(client)
client = client.Clone()
client.SetToken(tc.UnprivilegedToken().Token)
userAccountClient := accounts.NewClient(client)
al, err := accountClient.List(tc.Context(), amId)
al, err := adminAccountClient.List(tc.Context(), amId)
require.NoError(err)
require.Len(al.Items, 1)
require.Len(al.Items, 2)
acct := al.Items[0]
userAcct := al.Items[0]
if userAcct.Name == "admin" {
userAcct = al.Items[1]
}
setAcct, err := accountClient.SetPassword(tc.Context(), acct.Id, "setpassword", acct.Version)
_, err = userAccountClient.SetPassword(tc.Context(), userAcct.Id, "setpassword", userAcct.Version)
require.Error(err)
setAcct, err := adminAccountClient.SetPassword(tc.Context(), userAcct.Id, "setpassword", userAcct.Version)
require.NoError(err)
require.NotNil(setAcct)
assert.Equal(acct.Version+1, setAcct.Item.Version)
assert.Equal(userAcct.Version+1, setAcct.Item.Version)
changeAcct, err := accountClient.ChangePassword(tc.Context(), acct.Id, "setpassword", "changepassword", setAcct.Item.Version)
changeAcct, err := adminAccountClient.ChangePassword(tc.Context(), userAcct.Id, "setpassword", "changepassword", setAcct.Item.Version)
require.NoError(err)
require.NotNil(changeAcct)
assert.Equal(setAcct.Item.Version+1, changeAcct.Item.Version)
assert.Equal(userAcct.Version+2, changeAcct.Item.Version)
changeAcct, err = userAccountClient.ChangePassword(tc.Context(), userAcct.Id, "changepassword", "password2", changeAcct.Item.Version)
require.NoError(err)
require.NotNil(changeAcct)
assert.Equal(userAcct.Version+3, changeAcct.Item.Version)
}
func TestErrors(t *testing.T) {
@@ -13,22 +13,17 @@ import (
func TestAuthenticate(t *testing.T) {
assert, require := assert.New(t), require.New(t)
amId := "ampw_1234567890"
tc := controller.NewTestController(t, &controller.TestControllerOpts{
DefaultAuthMethodId: amId,
DefaultLoginName: "user",
DefaultPassword: "passpass",
})
tc := controller.NewTestController(t, nil)
defer tc.Shutdown()
client := tc.Client()
methods := authmethods.NewClient(client)
tok, err := methods.Authenticate(tc.Context(), amId, map[string]interface{}{"login_name": "user", "password": "passpass"})
tok, err := methods.Authenticate(tc.Context(), tc.Server().DevAuthMethodId, map[string]interface{}{"login_name": "user", "password": "passpass"})
require.NoError(err)
assert.NotNil(tok)
_, err = methods.Authenticate(tc.Context(), amId, map[string]interface{}{"login_name": "user", "password": "wrong"})
_, err = methods.Authenticate(tc.Context(), tc.Server().DevAuthMethodId, map[string]interface{}{"login_name": "user", "password": "wrong"})
require.Error(err)
apiErr := api.AsServerError(err)
require.NotNil(apiErr)
@@ -19,12 +19,7 @@ import (
func TestList(t *testing.T) {
assert, require := assert.New(t), require.New(t)
amId := "ampw_1234567890"
tc := controller.NewTestController(t, &controller.TestControllerOpts{
DefaultAuthMethodId: amId,
DefaultLoginName: "user",
DefaultPassword: "passpass",
})
tc := controller.NewTestController(t, nil)
defer tc.Shutdown()
token := tc.Token()
@@ -36,7 +31,7 @@ func TestList(t *testing.T) {
amResult, err := amClient.Create(tc.Context(), "password", org.GetPublicId())
require.NoError(err)
require.NotNil(amResult)
amId = amResult.Item.Id
amId := amResult.Item.Id
rolesClient := roles.NewClient(client)
role, err := rolesClient.Create(tc.Context(), org.GetPublicId())
@@ -106,12 +101,7 @@ func comparableSlice(in []*authtokens.AuthToken) []authtokens.AuthToken {
func TestCrud(t *testing.T) {
assert, require := assert.New(t), require.New(t)
amId := "ampw_1234567890"
tc := controller.NewTestController(t, &controller.TestControllerOpts{
DefaultAuthMethodId: amId,
DefaultLoginName: "user",
DefaultPassword: "passpass",
})
tc := controller.NewTestController(t, nil)
defer tc.Shutdown()
client := tc.Client()
@@ -120,7 +110,7 @@ func TestCrud(t *testing.T) {
tokens := authtokens.NewClient(client)
methods := authmethods.NewClient(client)
want, err := methods.Authenticate(tc.Context(), amId, map[string]interface{}{"login_name": "user", "password": "passpass"})
want, err := methods.Authenticate(tc.Context(), tc.Server().DevAuthMethodId, map[string]interface{}{"login_name": "user", "password": "passpass"})
require.NoError(err)
at, err := tokens.Read(tc.Context(), want.Item.Id)
+1 -2
View File
@@ -107,7 +107,6 @@ func TestCustom(t *testing.T) {
require.NoError(err)
assert.EqualValues(updatedRole.Item.Version, version)
assert.Empty(updatedRole.Item.Grants)
version++
})
}
}
@@ -148,7 +147,7 @@ func TestList(t *testing.T) {
case "org":
numBuiltIn = 2
case "proj":
numBuiltIn = 1
numBuiltIn = 2
}
require.Len(p1.Items, numBuiltIn)
expected = append(expected, p1.Items[0:numBuiltIn]...)
@@ -1,6 +1,7 @@
export BOUNDARY_ADDR="${BOUNDARY_ADDR:-http://127.0.0.1:9200}"
export DEFAULT_PASSWORD="${DEFAULT_PASSWORD:-password}"
export DEFAULT_LOGIN="${DEFAULT_LOGIN:-admin}"
export DEFAULT_UNPRIVILEGED_LOGIN="${DEFAULT_UNPRIVILEGED_LOGIN:-user}"
export DEFAULT_AMPW="${DEFAULT_AMPW:-ampw_1234567890}"
export DEFAULT_P_ID='p_1234567890'
export DEFAULT_O_ID='o_1234567890'
@@ -10,6 +11,7 @@ export DEFAULT_HOST_SET='hsst_1234567890'
export DEFAULT_HOST_CATALOG='hcst_1234567890'
export DEFAULT_HOST='hst_1234567890'
export DEFAULT_USER='u_1234567890'
export DEFAULT_UNPRIVILEGED_USER='u_0987654321'
export DEFAULT_GLOBAL='global'
function strip() {
@@ -0,0 +1,17 @@
load _authorized_actions
function list_sessions() {
boundary sessions list -scope-id $1 -format json
}
function count_sessions() {
list_sessions $1 | jq '. | length'
}
function cancel_session() {
boundary sessions cancel -id $1
}
function read_session() {
boundary sessions read -id $1
}
+1 -1
View File
@@ -31,7 +31,7 @@ function assoc_host_sets() {
boundary targets add-host-sets -id $id -host-set $hst
}
function target_id() {
function target_id_from_name() {
local sid=$1
local name=$2
strip $(list_targets $sid | jq -c ".[] | select(.name | contains(\"$name\")) | .[\"id\"]")
+104
View File
@@ -0,0 +1,104 @@
#!/usr/bin/env bats
load _auth
load _connect
load _sessions
load _helpers
@test "boundary/session: admin user can connect to default target" {
run login $DEFAULT_LOGIN
[ "$status" -eq 0 ]
run connect_nc $DEFAULT_TARGET
echo "$output"
[ "$status" -eq 0 ]
# Run twice so we have two values for later testing
run connect_nc $DEFAULT_TARGET
echo "$output"
[ "$status" -eq 0 ]
}
@test "boundary/session/connect: unpriv user can connect to default target" {
run login $DEFAULT_UNPRIVILEGED_LOGIN
[ "$status" -eq 0 ]
run connect_nc $DEFAULT_TARGET
echo "$output"
[ "$status" -eq 0 ]
# Run twice so we have two values for later testing
run connect_nc $DEFAULT_TARGET
echo "$output"
[ "$status" -eq 0 ]
}
# Note: there seems to be an issue with jq's length calculation -- an increase
# in the returned array by 2 shows up as a 4 or more increase in jq's length count.
# So for now, verify they're not the same.
@test "boundary/session: verify admin and unpriv user see different counts" {
run login $DEFAULT_UNPRIVILEGED_LOGIN
[ "$status" -eq 0 ]
run count_sessions $DEFAULT_P_ID
[ "$status" -eq 0 ]
unpriv_sessions="$output"
run login $DEFAULT_LOGIN
[ "$status" -eq 0 ]
run count_sessions $DEFAULT_P_ID
[ "$status" -eq 0 ]
admin_sessions="$output"
[ "$unpriv_sessions" -lt "$admin_sessions" ]
}
@test "boundary/session: verify read and cancelation permissions on admin session" {
# Find an admin session
run login $DEFAULT_LOGIN
[ "$status" -eq 0 ]
run list_sessions $DEFAULT_P_ID
[ "$status" -eq 0 ]
id=$(echo "$output" | jq -r '[.[]|select(.user_id == "u_1234567890")][0].id')
# Check unpriv cannot read or cancel an admin's session
run login $DEFAULT_UNPRIVILEGED_LOGIN
[ "$status" -eq 0 ]
run read_session $id
[ "$status" -eq 1 ]
run cancel_session $id
[ "$status" -eq 1 ]
# Check that admin _can_
run login $DEFAULT_LOGIN
[ "$status" -eq 0 ]
run read_session $id
[ "$status" -eq 0 ]
run cancel_session $id
[ "$status" -eq 0 ]
}
@test "boundary/session: verify read and cancelation permissions on unpriv session" {
# Find an unpriv session
run login $DEFAULT_UNPRIVILEGED_LOGIN
[ "$status" -eq 0 ]
run list_sessions $DEFAULT_P_ID
[ "$status" -eq 0 ]
id=$(echo "$output" | jq -r '[.[]|select(.user_id == "u_0987654321")][0].id')
# Check unpriv can read and cancel their own session
run login $DEFAULT_UNPRIVILEGED_LOGIN
[ "$status" -eq 0 ]
run read_session $id
[ "$status" -eq 0 ]
run cancel_session $id
[ "$status" -eq 0 ]
# Check that admin can too
run login $DEFAULT_LOGIN
[ "$status" -eq 0 ]
run read_session $id
[ "$status" -eq 0 ]
run cancel_session $id
[ "$status" -eq 0 ]
}
+39 -13
View File
@@ -6,35 +6,61 @@ load _targets
load _helpers
@test "boundary/login: can login as default user" {
@test "boundary/login: can login as admin user" {
run login $DEFAULT_LOGIN
[ "$status" -eq 0 ]
}
@test "boundary/target/connect: default user can connect to default target" {
@test "boundary/target/connect: admin user can connect to default target" {
run connect_nc $DEFAULT_TARGET
echo "$output"
[ "$status" -eq 0 ]
}
@test "boundary/target: default user can create target" {
@test "boundary/target: admin user can read default target" {
run read_target $DEFAULT_TARGET
[ "$status" -eq 0 ]
}
@test "boundary/login: can login as unpriv user" {
run login $DEFAULT_UNPRIVILEGED_LOGIN
[ "$status" -eq 0 ]
}
@test "boundary/target/connect: unpriv user can connect to default target" {
run connect_nc $DEFAULT_TARGET
echo "$output"
[ "$status" -eq 0 ]
}
@test "boundary/target: unpriv user can not read default target" {
run read_target $DEFAULT_TARGET
[ "$status" -eq 1 ]
}
@test "boundary/login: login back in as admin user" {
run login $DEFAULT_LOGIN
[ "$status" -eq 0 ]
}
@test "boundary/target: admin user can create target" {
run create_tcp_target $DEFAULT_P_ID 22 $TGT_NAME
[ "$status" -eq 0 ]
}
@test "boundary/target: default user can not create already created target" {
@test "boundary/target: admin user can not create already created target" {
run create_tcp_target $DEFAULT_P_ID 22 $TGT_NAME
[ "$status" -eq 1 ]
}
@test "boundary/target: default user can read created target" {
local id=$(target_id $DEFAULT_P_ID $TGT_NAME)
@test "boundary/target: admin user can read created target" {
local id=$(target_id_from_name $DEFAULT_P_ID $TGT_NAME)
run read_target $id
[ "$status" -eq 0 ]
}
@test "boundary/target: the $TGT_NAME target contains default authorized-actions" {
local id=$(target_id $DEFAULT_P_ID $TGT_NAME)
local id=$(target_id_from_name $DEFAULT_P_ID $TGT_NAME)
local out=$(read_target $id)
run has_default_target_actions "$out"
@@ -42,35 +68,35 @@ load _helpers
[ "$status" -eq 0 ]
}
@test "boundary/target: default user can add default host set to created target" {
local id=$(target_id $DEFAULT_P_ID $TGT_NAME)
@test "boundary/target: admin user can add default host set to created target" {
local id=$(target_id_from_name $DEFAULT_P_ID $TGT_NAME)
run assoc_host_sets $id $DEFAULT_HOST_SET
echo "$output"
[ "$status" -eq 0 ]
}
@test "boundary/target: created target has default host set" {
local id=$(target_id $DEFAULT_P_ID $TGT_NAME)
local id=$(target_id_from_name $DEFAULT_P_ID $TGT_NAME)
run target_has_host_set_id $id $DEFAULT_HOST_SET
echo "$output"
[ "$status" -eq 0 ]
}
@test "boundary/target/connect: default user can connect to created target" {
local id=$(target_id $DEFAULT_P_ID $TGT_NAME)
local id=$(target_id_from_name $DEFAULT_P_ID $TGT_NAME)
run connect_nc $id
echo "connecting to $id: $output"
[ "$status" -eq 0 ]
}
@test "boundary/target: default user can delete target" {
local id=$(target_id $DEFAULT_P_ID $TGT_NAME)
local id=$(target_id_from_name $DEFAULT_P_ID $TGT_NAME)
run delete_target $id
[ "$status" -eq 0 ]
}
@test "boundary/target: default user can not read deleted target" {
local id=$(target_id $DEFAULT_P_ID $TGT_NAME)
local id=$(target_id_from_name $DEFAULT_P_ID $TGT_NAME)
run read_target $id
[ "$status" -eq 1 ]
}
+2 -8
View File
@@ -17,9 +17,6 @@ import (
func TestIPv6Listener(t *testing.T) {
assert, require := assert.New(t), require.New(t)
amId := "ampw_1234567890"
user := "user"
password := "passpass"
logger := hclog.New(&hclog.LoggerOptions{
Level: hclog.Trace,
})
@@ -38,11 +35,8 @@ func TestIPv6Listener(t *testing.T) {
}
c1 := controller.NewTestController(t, &controller.TestControllerOpts{
Config: conf,
DefaultAuthMethodId: amId,
DefaultLoginName: user,
DefaultPassword: password,
Logger: logger.Named("c1"),
Config: conf,
Logger: logger.Named("c1"),
})
defer c1.Shutdown()
@@ -13,9 +13,6 @@ import (
func TestMultiControllerMultiWorkerConnections(t *testing.T) {
require := require.New(t)
amId := "ampw_1234567890"
user := "user"
password := "passpass"
logger := hclog.New(&hclog.LoggerOptions{
Level: hclog.Trace,
})
@@ -24,11 +21,8 @@ func TestMultiControllerMultiWorkerConnections(t *testing.T) {
require.NoError(err)
c1 := controller.NewTestController(t, &controller.TestControllerOpts{
Config: conf,
DefaultAuthMethodId: amId,
DefaultLoginName: user,
DefaultPassword: password,
Logger: logger.Named("c1"),
Config: conf,
Logger: logger.Named("c1"),
})
defer c1.Shutdown()
+2 -8
View File
@@ -20,9 +20,6 @@ import (
func TestUnixListener(t *testing.T) {
assert, require := assert.New(t), require.New(t)
amId := "ampw_1234567890"
user := "user"
password := "passpass"
logger := hclog.New(&hclog.LoggerOptions{
Level: hclog.Trace,
})
@@ -50,11 +47,8 @@ func TestUnixListener(t *testing.T) {
}
c1 := controller.NewTestController(t, &controller.TestControllerOpts{
Config: conf,
DefaultAuthMethodId: amId,
DefaultLoginName: user,
DefaultPassword: password,
Logger: logger.Named("c1"),
Config: conf,
Logger: logger.Named("c1"),
})
defer c1.Shutdown()
@@ -17,9 +17,6 @@ import (
)
func TestWorkerTagging(t *testing.T) {
amId := "ampw_1234567890"
user := "user"
password := "passpass"
logger := hclog.New(&hclog.LoggerOptions{
Level: hclog.Trace,
})
@@ -29,9 +26,6 @@ func TestWorkerTagging(t *testing.T) {
c1 := controller.NewTestController(t, &controller.TestControllerOpts{
Config: conf,
DefaultAuthMethodId: amId,
DefaultLoginName: user,
DefaultPassword: password,
InitialResourcesSuffix: "1234567890",
Logger: logger.Named("c1"),
})
+2 -2
View File
@@ -689,14 +689,14 @@ var session = &Resource{
},
{
Name: "read:self",
Description: "Read a session, which must belong to the calling user",
Description: "Read a session, which must be associated with the calling user",
Examples: []string{
"id=*;type=session;actions=read:self",
},
},
{
Name: "cancel:self",
Description: "Cancel a session, which must belong to the calling user",
Description: "Cancel a session, which must be associated with the calling user",
Examples: []string{
"id=*;type=session;actions=cancel:self",
},
+10
View File
@@ -464,3 +464,13 @@ func StringListToInterfaceList(in []string) []interface{} {
}
return ret
}
// Reverse reverses the input string
func Reverse(in string) string {
l := len(in)
out := make([]byte, l)
for i := 0; i <= l/2; i++ {
out[i], out[l-1-i] = in[l-1-i], in[i]
}
return string(out)
}
+22
View File
@@ -633,3 +633,25 @@ func TestPrintable(t *testing.T) {
})
}
}
func TestReverse(t *testing.T) {
cases := []struct {
in string
out string
}{
{
in: "abc",
out: "cba",
},
{
in: "abcd",
out: "dcba",
},
}
for i, tc := range cases {
t.Run(fmt.Sprint(i), func(t *testing.T) {
require.Equal(t, Reverse(tc.in), tc.out)
})
}
}
@@ -28,6 +28,22 @@ Roles are composable; a user's final set of grants will be composed of various
roles that each contribute grants to a set of principals that include that user
or groups of which that user is a member.
## Subactions
Starting in Boundary 0.1.6, some subactions are supported. These actions have a
format `top_level_action:subaction`, such as `read:self`. Being granted the top
level action infers being granted all subactions. Thus, if a grant conveys
`read`, it also matches the API actions `read` and `read:self`. However, if a
grant conveys `read:self`, it will match the API action `read:self` but will not
match `read`.
Note that not all API subactions are available for grants. As an example, auth
methods require one or more steps to perform authentication. These are separated
into separate API actions based on the required workflow for that authentication
mechanism, e.g. `authenticate:login`. However, the grant system does not
currently expose subactions for the `authenticate` top level action, as it
generally does not make sense to allow only one step out of many.
## Applicable Resource Types
Boundary's [domain model](/docs/concepts/domain-model) is based on resource
@@ -1251,8 +1267,8 @@ wildcard or templated grant strings.
</li>
</ul>
<li>
<code>read:self</code>: Read a session, which must belong to the
calling user
<code>read:self</code>: Read a session, which must be associated
with the calling user
</li>
<ul>
<li>
@@ -1260,8 +1276,8 @@ wildcard or templated grant strings.
</li>
</ul>
<li>
<code>cancel:self</code>: Cancel a session, which must belong to the
calling user
<code>cancel:self</code>: Cancel a session, which must be associated
with the calling user
</li>
<ul>
<li>