2021-07-29 18:52:56 -06:00
// Copyright (c) HashiCorp, Inc.
2020-08-19 16:47:18 -04:00
// SPDX-License-Identifier: BUSL-1.1
2023-02-02 17:15:17 -08:00
2020-05-08 11:36:28 -04:00
package iam
import (
"context"
2020-08-19 16:47:18 -04:00
"crypto/rand"
2020-05-08 11:36:28 -04:00
"fmt"
2020-06-12 15:22:23 -04:00
"strings"
2020-05-11 16:21:09 -04:00
2022-10-28 15:52:06 -04:00
"github.com/hashicorp/boundary/globals"
2020-08-10 17:15:02 -04:00
"github.com/hashicorp/boundary/internal/db"
2020-11-05 16:54:03 -05:00
"github.com/hashicorp/boundary/internal/errors"
2020-08-19 16:47:18 -04:00
"github.com/hashicorp/boundary/internal/kms"
2020-08-13 17:39:48 -04:00
"github.com/hashicorp/boundary/internal/oplog"
"github.com/hashicorp/boundary/internal/types/resource"
2020-08-10 17:15:02 -04:00
"github.com/hashicorp/boundary/internal/types/scope"
2022-08-24 14:11:49 -04:00
"github.com/hashicorp/go-dbw"
2022-03-16 17:04:07 -04:00
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
2020-05-08 11:36:28 -04:00
)
2020-06-12 15:22:23 -04:00
// CreateScope will create a scope in the repository and return the written
2020-08-19 16:47:18 -04:00
// scope. Supported options include: WithPublicId and WithRandomReader.
2020-08-13 17:39:48 -04:00
func ( r * Repository ) CreateScope ( ctx context . Context , s * Scope , userId string , opt ... Option ) ( * Scope , error ) {
2020-12-23 11:00:00 -08:00
const op = "iam.(Repository).CreateScope"
2020-06-26 11:00:14 -04:00
if s == nil {
2021-07-29 18:52:56 -06:00
return nil , errors . New ( ctx , errors . InvalidParameter , op , "missing scope" )
2020-05-08 11:36:28 -04:00
}
2020-06-26 11:00:14 -04:00
if s . Scope == nil {
2021-07-29 18:52:56 -06:00
return nil , errors . New ( ctx , errors . InvalidParameter , op , "missing scope store" )
2020-06-12 15:22:23 -04:00
}
2020-06-26 11:00:14 -04:00
if s . PublicId != "" {
2021-07-29 18:52:56 -06:00
return nil , errors . New ( ctx , errors . InvalidParameter , op , "public id not empty" )
2020-06-12 15:22:23 -04:00
}
2020-08-19 16:47:18 -04:00
var parentOplogWrapper wrapping . Wrapper
var err error
2020-07-08 17:20:20 -04:00
switch s . Type {
case scope . Unknown . String ():
2021-07-29 18:52:56 -06:00
return nil , errors . New ( ctx , errors . InvalidParameter , op , "unknown type" )
2020-07-08 17:20:20 -04:00
case scope . Global . String ():
2021-07-29 18:52:56 -06:00
return nil , errors . New ( ctx , errors . InvalidParameter , op , "invalid type" )
2020-08-19 16:47:18 -04:00
default :
switch s . ParentId {
case "" :
2021-07-29 18:52:56 -06:00
return nil , errors . New ( ctx , errors . InvalidParameter , op , "missing parent id" )
2020-08-19 16:47:18 -04:00
case scope . Global . String ():
parentOplogWrapper , err = r . kms . GetWrapper ( ctx , scope . Global . String (), kms . KeyPurposeOplog )
default :
parentOplogWrapper , err = r . kms . GetWrapper ( ctx , s . ParentId , kms . KeyPurposeOplog )
}
}
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , errors . New ( ctx , errors . InvalidParameter , op , "unable to get oplog wrapper" )
2020-07-08 17:20:20 -04:00
}
2020-08-13 17:39:48 -04:00
2020-06-12 15:22:23 -04:00
opts := getOpts ( opt ... )
2020-08-13 17:39:48 -04:00
var scopePublicId string
var scopeMetadata oplog . Metadata
2022-10-25 18:29:38 -05:00
var scopeRaw any
2020-08-13 17:39:48 -04:00
{
scopeType := scope . Map [ s . Type ]
if opts . withPublicId != "" {
if ! strings . HasPrefix ( opts . withPublicId , scopeType . Prefix () + "_" ) {
2021-07-29 18:52:56 -06:00
return nil , errors . New ( ctx , errors . InvalidParameter , op , fmt . Sprintf ( "passed-in public ID %q has wrong prefix for type %q which uses prefix %q" , opts . withPublicId , scopeType . String (), scopeType . Prefix ()))
2020-08-13 17:39:48 -04:00
}
scopePublicId = opts . withPublicId
} else {
2023-06-06 17:09:08 -04:00
scopePublicId , err = newScopeId ( ctx , scopeType )
2020-08-13 17:39:48 -04:00
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , errors . Wrap ( ctx , err , op )
2020-08-13 17:39:48 -04:00
}
}
sc := s . Clone ().( * Scope )
sc . PublicId = scopePublicId
scopeRaw = sc
scopeMetadata , err = r . stdMetadata ( ctx , sc )
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , errors . Wrap ( ctx , err , op )
2020-08-13 17:39:48 -04:00
}
scopeMetadata [ "op-type" ] = [] string { oplog . OpType_OP_TYPE_CREATE . String ()}
}
2020-09-29 20:38:16 -04:00
var adminRolePublicId string
var adminRoleMetadata oplog . Metadata
var adminRole * Role
2022-10-25 18:29:38 -05:00
var adminRoleRaw any
2020-08-26 23:27:25 -04:00
switch {
case userId == "" ,
2022-10-28 15:52:06 -04:00
userId == globals . AnonymousUserId ,
userId == globals . AnyAuthenticatedUserId ,
userId == globals . RecoveryUserId ,
2020-09-29 20:38:16 -04:00
opts . withSkipAdminRoleCreation :
2020-08-13 17:39:48 -04:00
// TODO: Cause a log entry. The repo doesn't have a logger right now,
// and ideally we will be using context to pass around log info scoped
// to this request for grouped display in the server log. The only
// reason this should ever happen anyways is via the administrative
// recovery workflow so it's already a special case.
// Also, stop linter from complaining
2020-09-29 20:38:16 -04:00
_ = adminRole
2020-08-13 17:39:48 -04:00
default :
2023-06-06 17:09:08 -04:00
adminRole , err = NewRole ( ctx , scopePublicId )
2020-08-13 17:39:48 -04:00
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "error instantiating new admin role" ))
2020-06-12 15:22:23 -04:00
}
2023-06-06 17:09:08 -04:00
adminRolePublicId , err = newRoleId ( ctx )
2020-06-12 15:22:23 -04:00
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "error generating public id for new admin role" ))
2020-08-13 17:39:48 -04:00
}
2020-09-29 20:38:16 -04:00
adminRole . PublicId = adminRolePublicId
adminRole . Name = "Administration"
adminRole . Description = fmt . Sprintf ( "Role created for administration of scope %s by user %s at its creation time" , scopePublicId , userId )
adminRoleRaw = adminRole
adminRoleMetadata = oplog . Metadata {
"resource-public-id" : [] string { adminRolePublicId },
"scope-id" : [] string { scopePublicId },
"scope-type" : [] string { s . Type },
"resource-type" : [] string { resource . Role . String ()},
"op-type" : [] string { oplog . OpType_OP_TYPE_CREATE . String ()},
}
}
var defaultRolePublicId string
var defaultRoleMetadata oplog . Metadata
var defaultRole * Role
2022-10-25 18:29:38 -05:00
var defaultRoleRaw any
2021-02-11 13:14:18 -05:00
if ! opts . withSkipDefaultRoleCreation {
2023-06-06 17:09:08 -04:00
defaultRole , err = NewRole ( ctx , scopePublicId )
2020-09-29 20:38:16 -04:00
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "error instantiating new default role" ))
2020-09-29 20:38:16 -04:00
}
2023-06-06 17:09:08 -04:00
defaultRolePublicId , err = newRoleId ( ctx )
2020-09-29 20:38:16 -04:00
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "error generating public id for new default role" ))
2020-09-29 20:38:16 -04:00
}
defaultRole . PublicId = defaultRolePublicId
2021-02-11 13:14:18 -05:00
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 )
}
2020-09-29 20:38:16 -04:00
defaultRoleRaw = defaultRole
defaultRoleMetadata = oplog . Metadata {
"resource-public-id" : [] string { defaultRolePublicId },
2020-08-13 17:39:48 -04:00
"scope-id" : [] string { scopePublicId },
"scope-type" : [] string { s . Type },
"resource-type" : [] string { resource . Role . String ()},
"op-type" : [] string { oplog . OpType_OP_TYPE_CREATE . String ()},
2020-06-12 15:22:23 -04:00
}
}
2020-08-13 17:39:48 -04:00
2020-08-19 16:47:18 -04:00
reader := opts . withRandomReader
if reader == nil {
reader = rand . Reader
}
2020-08-13 17:39:48 -04:00
_ , err = r . writer . DoTx (
ctx ,
db . StdRetryCnt ,
db . ExpBackoff {},
2020-08-19 16:47:18 -04:00
func ( dbr db . Reader , w db . Writer ) error {
2020-08-13 17:39:48 -04:00
if err := w . Create (
ctx ,
scopeRaw ,
2020-08-19 16:47:18 -04:00
db . WithOplog ( parentOplogWrapper , scopeMetadata ),
2020-08-13 17:39:48 -04:00
); err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "error creating scope" ))
2020-08-13 17:39:48 -04:00
}
2020-08-19 16:47:18 -04:00
s := scopeRaw .( * Scope )
2022-05-13 13:53:22 -04:00
txnKms , err := kms . NewUsingReaderWriter ( ctx , dbr , w )
2020-08-19 16:47:18 -04:00
if err != nil {
2022-05-13 13:53:22 -04:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "error creating transaction's kms" ))
2020-08-19 16:47:18 -04:00
}
2022-05-13 13:53:22 -04:00
if err := txnKms . AddExternalWrappers ( ctx , kms . WithRootWrapper ( r . kms . GetExternalWrappers ( ctx ). Root ())); err != nil {
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "error adding external root wrapper to transaction's kms" ))
}
if err := txnKms . CreateKeys ( ctx , s . PublicId , kms . WithRandomReader ( reader ), kms . WithReaderWriter ( dbr , w )); err != nil {
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "error creating scope keys" ))
2020-08-19 16:47:18 -04:00
}
2022-05-13 13:53:22 -04:00
childOplogWrapper , err := txnKms . GetWrapper ( ctx , s . PublicId , kms . KeyPurposeOplog )
2020-08-19 16:47:18 -04:00
if err != nil {
2021-07-29 18:52:56 -06:00
return errors . New ( ctx , errors . InvalidParameter , op , "unable to get oplog wrapper" )
2020-08-19 16:47:18 -04:00
}
2020-08-13 17:39:48 -04:00
// We create a new role, then set grants and principals on it. This
// turns into a bunch of stuff sadly because the role is the
// aggregate.
2020-09-29 20:38:16 -04:00
if adminRoleRaw != nil {
2020-08-13 17:39:48 -04:00
if err := w . Create (
ctx ,
2020-09-29 20:38:16 -04:00
adminRoleRaw ,
db . WithOplog ( childOplogWrapper , adminRoleMetadata ),
2020-08-13 17:39:48 -04:00
); err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "error creating role" ))
2020-08-13 17:39:48 -04:00
}
2020-09-29 20:38:16 -04:00
adminRole = adminRoleRaw .( * Role )
2020-08-13 17:39:48 -04:00
msgs := make ([] * oplog . Message , 0 , 3 )
2022-01-05 14:38:38 -05:00
roleTicket , err := w . GetTicket ( ctx , adminRole )
2020-08-13 17:39:48 -04:00
if err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to get ticket" ))
2020-08-13 17:39:48 -04:00
}
// We need to update the role version as that's the aggregate
var roleOplogMsg oplog . Message
2020-09-29 20:38:16 -04:00
rowsUpdated , err := w . Update ( ctx , adminRole , [] string { "Version" }, nil , db . NewOplogMsg ( & roleOplogMsg ), db . WithVersion ( & adminRole . Version ))
2020-08-13 17:39:48 -04:00
if err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to update role version for adding grant" ))
2020-08-13 17:39:48 -04:00
}
if rowsUpdated != 1 {
2021-07-29 18:52:56 -06:00
return errors . New ( ctx , errors . MultipleRecords , op , fmt . Sprintf ( "updated role but %d rows updated" , rowsUpdated ))
2020-08-13 17:39:48 -04:00
}
msgs = append ( msgs , & roleOplogMsg )
2023-06-06 16:13:44 -04:00
roleGrant , err := NewRoleGrant ( ctx , adminRolePublicId , "id=*;type=*;actions=*" )
2020-08-13 17:39:48 -04:00
if err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to create in memory role grant" ))
2020-08-13 17:39:48 -04:00
}
roleGrantOplogMsgs := make ([] * oplog . Message , 0 , 1 )
2022-10-25 18:29:38 -05:00
if err := w . CreateItems ( ctx , [] any { roleGrant }, db . NewOplogMsgs ( & roleGrantOplogMsgs )); err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to add grants" ))
2020-08-13 17:39:48 -04:00
}
msgs = append ( msgs , roleGrantOplogMsgs ... )
2023-06-06 17:09:08 -04:00
rolePrincipal , err := NewUserRole ( ctx , adminRolePublicId , userId )
2020-08-13 17:39:48 -04:00
if err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to create in memory role user" ))
2020-08-13 17:39:48 -04:00
}
roleUserOplogMsgs := make ([] * oplog . Message , 0 , 1 )
2022-10-25 18:29:38 -05:00
if err := w . CreateItems ( ctx , [] any { rolePrincipal }, db . NewOplogMsgs ( & roleUserOplogMsgs )); err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to add grants" ))
2020-08-13 17:39:48 -04:00
}
msgs = append ( msgs , roleUserOplogMsgs ... )
metadata := oplog . Metadata {
"op-type" : [] string { oplog . OpType_OP_TYPE_CREATE . String ()},
"scope-id" : [] string { s . PublicId },
"scope-type" : [] string { s . Type },
2020-09-29 20:38:16 -04:00
"resource-public-id" : [] string { adminRole . PublicId },
}
if err := w . WriteOplogEntryWith ( ctx , childOplogWrapper , roleTicket , metadata , msgs ); err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to write oplog" ))
2020-09-29 20:38:16 -04:00
}
}
// We create a new role, then set grants and principals on it. This
// turns into a bunch of stuff sadly because the role is the
// aggregate.
if defaultRoleRaw != nil {
if err := w . Create (
ctx ,
defaultRoleRaw ,
db . WithOplog ( childOplogWrapper , defaultRoleMetadata ),
); err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "error creating role" ))
2020-09-29 20:38:16 -04:00
}
defaultRole = defaultRoleRaw .( * Role )
msgs := make ([] * oplog . Message , 0 , 6 )
2022-01-05 14:38:38 -05:00
roleTicket , err := w . GetTicket ( ctx , defaultRole )
2020-09-29 20:38:16 -04:00
if err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to get ticket" ))
2020-09-29 20:38:16 -04:00
}
// We need to update the role version as that's the aggregate
var roleOplogMsg oplog . Message
rowsUpdated , err := w . Update ( ctx , defaultRole , [] string { "Version" }, nil , db . NewOplogMsg ( & roleOplogMsg ), db . WithVersion ( & defaultRole . Version ))
if err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to update role version for adding grant" ))
2020-09-29 20:38:16 -04:00
}
if rowsUpdated != 1 {
2021-07-29 18:52:56 -06:00
return errors . New ( ctx , errors . MultipleRecords , op , fmt . Sprintf ( "updated role but %d rows updated" , rowsUpdated ))
2020-09-29 20:38:16 -04:00
}
msgs = append ( msgs , & roleOplogMsg )
// Grants
{
2022-10-25 18:29:38 -05:00
grants := [] any {}
2021-02-02 13:37:32 -07:00
2021-02-11 13:14:18 -05:00
switch s . Type {
case scope . Project . String ():
2023-06-06 16:13:44 -04:00
roleGrant , err := NewRoleGrant ( ctx , defaultRolePublicId , "id=*;type=session;actions=list,read:self,cancel:self" )
2021-02-11 13:14:18 -05:00
if err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to create in memory role grant" ))
2022-01-13 06:26:35 -08:00
}
grants = append ( grants , roleGrant )
2023-06-06 16:13:44 -04:00
roleGrant , err = NewRoleGrant ( ctx , defaultRolePublicId , "type=target;actions=list" )
2022-01-13 06:26:35 -08:00
if err != nil {
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to create in memory role grant" ))
2021-02-11 13:14:18 -05:00
}
grants = append ( grants , roleGrant )
default :
2023-06-06 16:13:44 -04:00
roleGrant , err := NewRoleGrant ( ctx , defaultRolePublicId , "id=*;type=scope;actions=list,no-op" )
2021-02-11 13:14:18 -05:00
if err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to create in memory role grant" ))
2021-02-11 13:14:18 -05:00
}
grants = append ( grants , roleGrant )
2023-06-06 16:13:44 -04:00
roleGrant , err = NewRoleGrant ( ctx , defaultRolePublicId , "id=*;type=auth-method;actions=authenticate,list" )
2021-02-11 13:14:18 -05:00
if err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to create in memory role grant" ))
2021-02-11 13:14:18 -05:00
}
grants = append ( grants , roleGrant )
2023-06-06 16:13:44 -04:00
roleGrant , err = NewRoleGrant ( ctx , defaultRolePublicId , "id={{.Account.Id}};actions=read,change-password" )
2021-02-11 13:14:18 -05:00
if err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to create in memory role grant" ))
2021-02-11 13:14:18 -05:00
}
grants = append ( grants , roleGrant )
2021-04-23 12:45:08 -05:00
2023-06-06 16:13:44 -04:00
roleGrant , err = NewRoleGrant ( ctx , defaultRolePublicId , "id=*;type=auth-token;actions=list,read:self,delete:self" )
2021-04-23 12:45:08 -05:00
if err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to create in memory role grant" ))
2021-04-23 12:45:08 -05:00
}
grants = append ( grants , roleGrant )
2020-09-29 20:38:16 -04:00
}
2020-10-01 10:51:54 -04:00
roleGrantOplogMsgs := make ([] * oplog . Message , 0 , 3 )
2020-09-29 20:38:16 -04:00
if err := w . CreateItems ( ctx , grants , db . NewOplogMsgs ( & roleGrantOplogMsgs )); err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to add grants" ))
2020-09-29 20:38:16 -04:00
}
msgs = append ( msgs , roleGrantOplogMsgs ... )
}
// Principals
{
2022-10-25 18:29:38 -05:00
principals := [] any {}
2022-10-28 15:52:06 -04:00
userId := globals . AnonymousUserId
2021-02-11 13:14:18 -05:00
if s . Type == scope . Project . String () {
2022-10-28 15:52:06 -04:00
userId = globals . AnyAuthenticatedUserId
2021-02-11 13:14:18 -05:00
}
2023-06-06 17:09:08 -04:00
rolePrincipal , err := NewUserRole ( ctx , defaultRolePublicId , userId )
2020-09-29 20:38:16 -04:00
if err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to create in memory role user" ))
2020-09-29 20:38:16 -04:00
}
principals = append ( principals , rolePrincipal )
roleUserOplogMsgs := make ([] * oplog . Message , 0 , 2 )
if err := w . CreateItems ( ctx , principals , db . NewOplogMsgs ( & roleUserOplogMsgs )); err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to add grants" ))
2020-09-29 20:38:16 -04:00
}
msgs = append ( msgs , roleUserOplogMsgs ... )
}
metadata := oplog . Metadata {
"op-type" : [] string { oplog . OpType_OP_TYPE_CREATE . String ()},
"scope-id" : [] string { s . PublicId },
"scope-type" : [] string { s . Type },
"resource-public-id" : [] string { defaultRole . PublicId },
2020-08-13 17:39:48 -04:00
}
2020-08-19 16:47:18 -04:00
if err := w . WriteOplogEntryWith ( ctx , childOplogWrapper , roleTicket , metadata , msgs ); err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to write oplog" ))
2020-08-13 17:39:48 -04:00
}
}
return nil
},
)
2020-05-08 11:36:28 -04:00
if err != nil {
2020-11-05 16:54:03 -05:00
if errors . IsUniqueError ( err ) {
2021-07-29 18:52:56 -06:00
return nil , errors . New ( ctx , errors . NotUnique , op , fmt . Sprintf ( "scope %s/%s already exists" , scopePublicId , s . Name ))
2020-06-12 15:22:23 -04:00
}
2021-07-29 18:52:56 -06:00
return nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( fmt . Sprintf ( "for %s" , scopePublicId )))
2020-05-08 11:36:28 -04:00
}
2020-08-13 17:39:48 -04:00
return scopeRaw .( * Scope ), nil
2020-05-08 11:36:28 -04:00
}
2020-05-31 19:55:56 -04:00
// UpdateScope will update a scope in the repository and return the written
// scope. fieldMaskPaths provides field_mask.proto paths for fields that should
// be updated. Fields will be set to NULL if the field is a zero value and
// included in fieldMask. Name and Description are the only updatable fields,
// and everything else is ignored. If no updatable fields are included in the
// fieldMaskPaths, then an error is returned.
2020-12-23 11:00:00 -08:00
func ( r * Repository ) UpdateScope ( ctx context . Context , scope * Scope , version uint32 , fieldMaskPaths [] string , _ ... Option ) ( * Scope , int , error ) {
const op = "iam.(Repository).UpdateScope"
2020-05-08 11:36:28 -04:00
if scope == nil {
2021-07-29 18:52:56 -06:00
return nil , db . NoRowsAffected , errors . New ( ctx , errors . InvalidParameter , op , "missing scope" )
2020-05-18 17:51:09 -04:00
}
if scope . PublicId == "" {
2021-07-29 18:52:56 -06:00
return nil , db . NoRowsAffected , errors . New ( ctx , errors . InvalidParameter , op , "missing public id" )
2020-05-08 11:36:28 -04:00
}
2020-05-31 19:55:56 -04:00
if contains ( fieldMaskPaths , "ParentId" ) {
2021-07-29 18:52:56 -06:00
return nil , db . NoRowsAffected , errors . New ( ctx , errors . InvalidFieldMask , op , "you cannot change a scope's parent" )
2020-05-31 19:55:56 -04:00
}
var dbMask , nullFields [] string
2022-08-24 14:11:49 -04:00
dbMask , nullFields = dbw . BuildUpdatePaths (
2022-10-25 18:29:38 -05:00
map [ string ] any {
2021-04-08 20:54:32 -04:00
"name" : scope . Name ,
"description" : scope . Description ,
2021-09-30 08:47:34 -04:00
"PrimaryAuthMethodId" : scope . PrimaryAuthMethodId , // gorm: it's important that the field start with a capital letter.
2020-05-31 19:55:56 -04:00
},
fieldMaskPaths ,
2020-09-19 10:31:49 -04:00
nil ,
2020-05-31 19:55:56 -04:00
)
// nada to update, so reload scope from db and return it
if len ( dbMask ) == 0 && len ( nullFields ) == 0 {
2021-07-29 18:52:56 -06:00
return nil , db . NoRowsAffected , errors . E ( ctx , errors . WithCode ( errors . EmptyFieldMask ), errors . WithOp ( op ))
2020-05-31 19:55:56 -04:00
}
2020-08-07 12:55:02 -04:00
resource , rowsUpdated , err := r . update ( ctx , scope , version , dbMask , nullFields )
2020-05-08 11:36:28 -04:00
if err != nil {
2020-11-05 16:54:03 -05:00
if errors . IsUniqueError ( err ) {
2021-07-29 18:52:56 -06:00
return nil , db . NoRowsAffected , errors . New ( ctx , errors . NotUnique , op , fmt . Sprintf ( "%s name %s already exists" , scope . PublicId , scope . Name ))
2020-05-31 19:55:56 -04:00
}
2021-07-29 18:52:56 -06:00
return nil , db . NoRowsAffected , errors . Wrap ( ctx , err , op , errors . WithMsg ( fmt . Sprintf ( "for public id %s" , scope . PublicId )))
2020-05-08 11:36:28 -04:00
}
2020-12-23 11:00:00 -08:00
return resource .( * Scope ), rowsUpdated , nil
2020-05-08 11:36:28 -04:00
}
2020-05-11 16:21:09 -04:00
// LookupScope will look up a scope in the repository. If the scope is not
// found, it will return nil, nil.
2020-12-23 11:00:00 -08:00
func ( r * Repository ) LookupScope ( ctx context . Context , withPublicId string , _ ... Option ) ( * Scope , error ) {
const op = "iam.(Repository).LookupScope"
2020-05-18 17:51:09 -04:00
if withPublicId == "" {
2021-07-29 18:52:56 -06:00
return nil , errors . New ( ctx , errors . InvalidParameter , op , "missing public id" )
2020-05-18 17:51:09 -04:00
}
2021-04-08 20:54:32 -04:00
scope := AllocScope ()
2020-05-18 17:51:09 -04:00
scope . PublicId = withPublicId
if err := r . reader . LookupByPublicId ( ctx , & scope ); err != nil {
2020-12-21 10:07:34 -08:00
if errors . IsNotFoundError ( err ) {
2020-05-18 17:51:09 -04:00
return nil , nil
2020-05-08 11:36:28 -04:00
}
2021-07-29 18:52:56 -06:00
return nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( fmt . Sprintf ( "failed for %s" , withPublicId )))
2020-05-08 11:36:28 -04:00
}
2020-05-18 17:51:09 -04:00
return & scope , nil
}
// DeleteScope will delete a scope from the repository
2020-12-23 11:00:00 -08:00
func ( r * Repository ) DeleteScope ( ctx context . Context , withPublicId string , _ ... Option ) ( int , error ) {
const op = "iam.(Repository).DeleteScope"
2020-05-18 17:51:09 -04:00
if withPublicId == "" {
2021-07-29 18:52:56 -06:00
return db . NoRowsAffected , errors . New ( ctx , errors . InvalidParameter , op , "missing public id" )
2020-05-18 17:51:09 -04:00
}
2020-07-08 17:20:20 -04:00
if withPublicId == scope . Global . String () {
2021-07-29 18:52:56 -06:00
return db . NoRowsAffected , errors . New ( ctx , errors . InvalidParameter , op , "invalid to delete global scope" )
2020-07-08 17:20:20 -04:00
}
2021-04-08 20:54:32 -04:00
scope := AllocScope ()
2020-05-18 17:51:09 -04:00
scope . PublicId = withPublicId
rowsDeleted , err := r . delete ( ctx , & scope )
if err != nil {
if errors . Is ( err , ErrMetadataScopeNotFound ) {
return 0 , nil
2020-05-08 11:36:28 -04:00
}
2021-07-29 18:52:56 -06:00
return db . NoRowsAffected , errors . Wrap ( ctx , err , op , errors . WithMsg ( fmt . Sprintf ( "failed for %s" , withPublicId )))
2020-05-08 11:36:28 -04:00
}
2020-05-18 17:51:09 -04:00
return rowsDeleted , nil
2020-05-08 11:36:28 -04:00
}
2020-06-18 14:53:11 -04:00
2021-01-25 19:37:40 -05:00
// ListScopes with the parent IDs, supports the WithLimit option.
func ( r * Repository ) ListScopes ( ctx context . Context , withParentIds [] string , opt ... Option ) ([] * Scope , error ) {
const op = "iam.(Repository).ListScopes"
if len ( withParentIds ) == 0 {
2021-07-29 18:52:56 -06:00
return nil , errors . New ( ctx , errors . InvalidParameter , op , "missing parent id" )
2020-06-18 14:53:11 -04:00
}
2021-01-25 19:37:40 -05:00
var items [] * Scope
2022-10-25 18:29:38 -05:00
err := r . list ( ctx , & items , "parent_id in (?)" , [] any { withParentIds }, opt ... )
2020-06-18 14:53:11 -04:00
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , errors . Wrap ( ctx , err , op )
2020-06-18 14:53:11 -04:00
}
2021-01-25 19:37:40 -05:00
return items , nil
2020-06-18 14:53:11 -04:00
}
2021-01-21 21:03:30 -05:00
2021-01-23 13:39:04 -05:00
// ListScopesRecursively allows for recursive listing of scopes based on a root scope
2021-01-21 21:03:30 -05:00
// ID. It returns the root scope ID as a part of the set.
2021-01-23 13:39:04 -05:00
func ( r * Repository ) ListScopesRecursively ( ctx context . Context , rootScopeId string , opt ... Option ) ([] * Scope , error ) {
2021-01-21 21:03:30 -05:00
const op = "iam.(Repository).ListRecursively"
2021-03-25 03:15:43 -04:00
var scopes [] * Scope
2021-01-21 21:03:30 -05:00
var where string
2022-10-25 18:29:38 -05:00
var args [] any
2021-01-21 21:03:30 -05:00
switch {
2021-11-04 09:34:01 -04:00
case rootScopeId == scope . Global . String ():
2021-01-21 21:03:30 -05:00
// Nothing -- we want all scopes
case strings . HasPrefix ( rootScopeId , "o_" ):
// The org itself and any projects that have it as parent
where = "public_id = ? or parent_id = ?"
args = append ( args , rootScopeId , rootScopeId )
case strings . HasPrefix ( rootScopeId , "p_" ):
// No scopes can (currently) live under projects, so just the project
// itself
where = "public_id = ?"
args = append ( args , rootScopeId )
default :
// We have no idea what scope type this is so bail
2021-07-29 18:52:56 -06:00
return nil , errors . New ( ctx , errors . InvalidPublicId , op + ":TypeSwitch" , "invalid scope ID" )
2021-01-21 21:03:30 -05:00
}
2021-03-25 03:15:43 -04:00
err := r . list ( ctx , & scopes , where , args , opt ... )
2021-01-21 21:03:30 -05:00
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , errors . Wrap ( ctx , err , op + ":ListQuery" )
2021-01-21 21:03:30 -05:00
}
2021-03-25 03:15:43 -04:00
return scopes , nil
2021-01-21 21:03:30 -05:00
}