Skip to content

Commit

Permalink
Merge "[FAB-7882] Need wildcard for bootstrap user"
Browse files Browse the repository at this point in the history
  • Loading branch information
hacera-jonathan authored and Gerrit Code Review committed May 11, 2018
2 parents 5ca7782 + 2b5ed40 commit 534af8c
Show file tree
Hide file tree
Showing 12 changed files with 194 additions and 58 deletions.
4 changes: 2 additions & 2 deletions cmd/fabric-ca-server/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,8 @@ registry:
type: client
affiliation: ""
attrs:
hf.Registrar.Roles: "peer,orderer,client,user"
hf.Registrar.DelegateRoles: "peer,orderer,client,user"
hf.Registrar.Roles: "*"
hf.Registrar.DelegateRoles: "*"
hf.Revoker: true
hf.IntermediateCA: true
hf.GenCRL: true
Expand Down
4 changes: 2 additions & 2 deletions docs/source/serverconfig.rst
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@ Fabric-CA Server's Configuration File
type: client
affiliation: ""
attrs:
hf.Registrar.Roles: "peer,orderer,client,user"
hf.Registrar.DelegateRoles: "peer,orderer,client,user"
hf.Registrar.Roles: "*"
hf.Registrar.DelegateRoles: "*"
hf.Revoker: true
hf.IntermediateCA: true
hf.GenCRL: true
Expand Down
18 changes: 16 additions & 2 deletions lib/attr/attribute.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,9 +238,9 @@ func (ac *attributeControl) validateListAttribute(requestedAttr *api.Attribute,
return nil
}
// Make sure the values requested for attribute is equal to or a subset of the registrar's attribute
err := util.IsSubsetOf(requestedAttrValue, callersAttrValue)
err := ac.IsSubsetOf(requestedAttrValue, callersAttrValue)
if err != nil {
return errors.WithMessage(err, fmt.Sprintf("The requested values for attribute '%s' is a superset of the caller's attribute value", ac.getName()))
return err
}
// If requested attribute is 'hf.Registrar.DeletegateRoles', make sure it is equal or a subset of the user's hf.Registrar.Roles attribute
if ac.getName() == DelegateRoles {
Expand All @@ -252,6 +252,17 @@ func (ac *attributeControl) validateListAttribute(requestedAttr *api.Attribute,
return nil
}

func (ac *attributeControl) IsSubsetOf(requestedAttrValue, callersAttrValue string) error {
if (ac.getName() == Roles || ac.getName() == DelegateRoles) && util.ListContains(callersAttrValue, "*") {
return nil
}
err := util.IsSubsetOf(requestedAttrValue, callersAttrValue)
if err != nil {
return errors.WithMessage(err, fmt.Sprintf("The requested values for attribute '%s' is a superset of the caller's attribute value", ac.getName()))
}
return nil
}

// Check if registrar has the proper authority to register the values for 'hf.Registrar.Attributes'.
// Registering 'hf.Registrar.Attributes' with a value that has a 'hf.' prefix requires that the user
// being registered to possess that hf. attribute. For example, if attribute is 'hf.Registrar.Attributes=hf.Revoker'
Expand Down Expand Up @@ -313,6 +324,9 @@ func checkDelegateRoleValues(reqAttrs []api.Attribute, user AttributeControl) er
}
}
}
if util.ListContains(roles, "*") {
return nil
}
delegateRoles := GetAttrValue(reqAttrs, DelegateRoles)
err := util.IsSubsetOf(delegateRoles, roles)
if err != nil {
Expand Down
20 changes: 20 additions & 0 deletions lib/dbaccessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"strings"

"github.com/hyperledger/fabric-ca/lib/attr"
"github.com/hyperledger/fabric-ca/util"

"github.com/pkg/errors"

Expand Down Expand Up @@ -626,7 +627,17 @@ func (d *Accessor) GetFilteredUsers(affiliation, types string) (*sqlx.Rows, erro
typesArray[i] = strings.TrimSpace(typesArray[i])
}

// If root affiliation, allowed to get back users of all affiliations
if affiliation == "" {
if util.ListContains(types, "*") { // If type is '*', allowed to get back of all types
query := "SELECT * FROM users"
rows, err := d.db.Queryx(d.db.Rebind(query))
if err != nil {
return nil, errors.Wrapf(err, "Failed to execute query '%s' for affiliation '%s' and types '%s'", query, affiliation, types)
}
return rows, nil
}

query := "SELECT * FROM users WHERE (type IN (?))"
query, args, err := sqlx.In(query, typesArray)
if err != nil {
Expand All @@ -640,6 +651,15 @@ func (d *Accessor) GetFilteredUsers(affiliation, types string) (*sqlx.Rows, erro
}

subAffiliation := affiliation + ".%"
if util.ListContains(types, "*") { // If type is '*', allowed to get back of all types for requested affiliation
query := "SELECT * FROM users WHERE ((affiliation = ?) OR (affiliation LIKE ?))"
rows, err := d.db.Queryx(d.db.Rebind(query))
if err != nil {
return nil, errors.Wrapf(err, "Failed to execute query '%s' for affiliation '%s' and types '%s'", query, affiliation, types)
}
return rows, nil
}

query := "SELECT * FROM users WHERE ((affiliation = ?) OR (affiliation LIKE ?)) AND (type IN (?))"
inQuery, args, err := sqlx.In(query, affiliation, subAffiliation, typesArray)
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions lib/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,8 @@ func (s *Server) RegisterBootstrapUser(user, pass, affiliation string) error {
Affiliation: affiliation,
MaxEnrollments: 0, // 0 means to use the server's max enrollment setting
Attrs: map[string]string{
attr.Roles: allRoles,
attr.DelegateRoles: allRoles,
attr.Roles: "*",
attr.DelegateRoles: "*",
attr.Revoker: "true",
attr.IntermediateCA: "true",
attr.GenCRL: "true",
Expand Down
2 changes: 1 addition & 1 deletion lib/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ func TestSRVRootServer(t *testing.T) {
Secret: "adminpw",
})
if err != nil {
t.Fatalf("Failed to enroll admin/adminpw: %s", err)
t.Fatalf("Failed to enroll admin2/admin2pw: %s", err)
}
admin = eresp.Identity
// test registration permissions wrt roles and affiliation
Expand Down
108 changes: 108 additions & 0 deletions lib/serveridentities_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,114 @@ func TestDynamicWithMultCA(t *testing.T) {

}

func TestBootstrapUserAddingRoles(t *testing.T) {
os.RemoveAll(rootDir)
defer os.RemoveAll(rootDir)
os.RemoveAll("../testdata/msp")
defer os.RemoveAll("../testdata/msp")

var err error

srv := TestGetRootServer(t)
err = srv.Start()
util.FatalError(t, err, "Failed to start server")
defer srv.Stop()

client := getTestClient(7075)
resp, err := client.Enroll(&api.EnrollmentRequest{
Name: "admin",
Secret: "adminpw",
})
util.FatalError(t, err, "Failed to enroll user 'admin'")

admin := resp.Identity

// Bootstrap user with '*' for hf.Registrar.Roles should be able to register any type
_, err = admin.AddIdentity(&api.AddIdentityRequest{
ID: "testuser",
Type: "newType",
})
assert.NoError(t, err, "Bootstrap user with '*' for hf.Registrar.Roles should be able to register any type")

// Bootstrap user with '*' for hf.Registrar.Roles should be able to register any value for hf.Registrar.Roles
_, err = admin.AddIdentity(&api.AddIdentityRequest{
ID: "testuser2",
Type: "client",
Attributes: []api.Attribute{
api.Attribute{
Name: "hf.Registrar.Roles",
Value: "peer,client,user,orderer,newType",
},
},
})
assert.NoError(t, err, "Bootstrap user with '*' for hf.Registrar.Roles should be able to register any value for hf.Registrar.Roles")

// Bootstrap user should be able to register any value for hf.Registrar.Roles, but not be able to specify '*' for
// hf.Registrar.DelegateRoles if hf.Registrar.Roles is not a '*'
_, err = admin.AddIdentity(&api.AddIdentityRequest{
ID: "testuser2",
Type: "client",
Attributes: []api.Attribute{
api.Attribute{
Name: "hf.Registrar.Roles",
Value: "peer,client,user,orderer,newType",
},
api.Attribute{
Name: "hf.Registrar.DelegateRoles",
Value: "*",
},
},
})
assert.Error(t, err, "Bootstrap user should be able to register any value for hf.Registrar.Roles, but not be able to specify '*' for hf.Registrar.DelegateRoles if hf.Registrar.Roles is not a '*'")

// Bootstrap should fail to register hf.Registrar.DelegateRoles without giving hf.Registrar.Roles attribute to the identity being registered
_, err = admin.AddIdentity(&api.AddIdentityRequest{
ID: "testuser3",
Type: "client",
Attributes: []api.Attribute{
api.Attribute{
Name: "hf.Registrar.DelegateRoles",
Value: "peer,client,user,orderer,newType",
},
},
})
assert.Error(t, err, "Should fail to register hf.Registrar.DelegateRoles without having hf.Registrar.Roles")

// Bootstrap should be able to register '*' for hf.Registrar.Roles and provide any value for hf.Registrar.DelegateRoles
_, err = admin.AddIdentity(&api.AddIdentityRequest{
ID: "testuser3",
Type: "client",
Attributes: []api.Attribute{
api.Attribute{
Name: "hf.Registrar.Roles",
Value: "*",
},
api.Attribute{
Name: "hf.Registrar.DelegateRoles",
Value: "peer,client,user,orderer,newType",
},
},
})
assert.NoError(t, err, "Bootstrap should be able to register star for hf.Registrar.Roles and provide any value for hf.Registrar.DelegateRoles")

// Bootstrap user should be able to register '*' for hf.Registrar.Roles and hf.Registrar.DelegateRoles
_, err = admin.AddIdentity(&api.AddIdentityRequest{
ID: "testuser4",
Type: "client",
Attributes: []api.Attribute{
api.Attribute{
Name: "hf.Registrar.Roles",
Value: "*",
},
api.Attribute{
Name: "hf.Registrar.DelegateRoles",
Value: "*",
},
},
})
assert.NoError(t, err, "Bootstrap user should be able to register '*' for hf.Registrar.Roles and hf.Registrar.DelegateRoles")
}

func cleanMultiCADir(t *testing.T) {
var err error
caFolder := "../testdata/ca"
Expand Down
60 changes: 13 additions & 47 deletions lib/serverregister.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package lib
import (
"fmt"
"net/url"
"strings"

"github.com/pkg/errors"

Expand Down Expand Up @@ -116,20 +115,24 @@ func normalizeRegistrationRequest(req *api.RegistrationRequest, registrar spi.Us
}

func validateAffiliation(req *api.RegistrationRequest, ctx *serverRequestContext) error {
log.Debug("Validate Affiliation")
err := ctx.ContainsAffiliation(req.Affiliation)
affiliation := req.Affiliation
log.Debugf("Validating affiliation: %s", affiliation)

err := ctx.ContainsAffiliation(affiliation)
if err != nil {
return err
}
return nil
}

func validateID(req *api.RegistrationRequest, ca *CA) error {
log.Debug("Validate ID")
err := isValidAffiliation(req.Affiliation, ca)
// If requested affiliation is for root then don't need to do lookup in affiliation's table
if affiliation == "" {
return nil
}

_, err = ctx.ca.registry.GetAffiliation(affiliation)
if err != nil {
return err
return errors.WithMessage(err, fmt.Sprintf("Failed getting affiliation '%s'", affiliation))
}

return nil
}

Expand Down Expand Up @@ -178,56 +181,19 @@ func registerUserID(req *api.RegistrationRequest, ca *CA) (string, error) {
return req.Secret, nil
}

func isValidAffiliation(affiliation string, ca *CA) error {
log.Debugf("Validating affiliation: %s", affiliation)

// If requested affiliation is for root then don't need to do lookup in affiliation's table
if affiliation == "" {
return nil
}

_, err := ca.registry.GetAffiliation(affiliation)
if err != nil {
return errors.WithMessage(err, fmt.Sprintf("Failed getting affiliation '%s'", affiliation))
}

return nil
}

func canRegister(registrar spi.User, req *api.RegistrationRequest, ctx *serverRequestContext) error {
log.Debugf("canRegister - Check to see if user '%s' can register", registrar.GetName())

var roles []string
rolesStr, isRegistrar, err := ctx.isRegistrar()
err := ctx.CanActOnType(req.Type)
if err != nil {
return err
}
if !isRegistrar {
return errors.Errorf("'%s' does not have authority to register identities", registrar)
}
if rolesStr != "" {
roles = strings.Split(rolesStr, ",")
} else {
roles = make([]string, 0)
}
if req.Type == "" {
req.Type = "client"
}
if !util.StrContained(req.Type, roles) {
return fmt.Errorf("Identity '%s' may not register type '%s'", registrar, req.Type)
}

// Check that the affiliation requested is of the appropriate level
err = validateAffiliation(req, ctx)
if err != nil {
return fmt.Errorf("Registration of '%s' failed in affiliation validation: %s", req.Name, err)
}

err = validateID(req, ctx.ca)
if err != nil {
return errors.WithMessage(err, fmt.Sprintf("Registration of '%s' to validate", req.Name))
}

err = attr.CanRegisterRequestedAttributes(req.Attributes, nil, registrar)
if err != nil {
return newAuthErr(ErrRegAttrAuth, "Failed to register attribute: %s", err)
Expand Down
8 changes: 7 additions & 1 deletion lib/serverrequestcontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -538,13 +538,19 @@ func (ctx *serverRequestContext) canActOnType(requestedType string) (bool, error
return false, newAuthErr(ErrRegAttrAuth, "'%s' is not allowed to manage users", caller.GetName())
}

if util.ListContains(typesStr, "*") {
return true, nil
}

var types []string
if typesStr != "" {
types = strings.Split(typesStr, ",")
} else {
types = make([]string, 0)
}

if requestedType == "" {
requestedType = "client"
}
if !util.StrContained(requestedType, types) {
log.Debugf("Caller with types '%s' is not authorized to act on '%s'", types, requestedType)
return false, nil
Expand Down
2 changes: 1 addition & 1 deletion scripts/fvt/ident_modify_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ function testRoleAuthorization() {
# the type of the identity being added must be in the user's hf.Registrar.Roles list
$FABRIC_CA_CLIENTEXEC identity add userType1 $URI -H $TESTDIR/admin \
--type account --affiliation ${defaultValues[Affiliation]} 2>&1 |
grepPrint "Identity.*admin.*may not register type 'account'" ||
grepPrint "Registrar does not have authority to act on type 'account'" ||
ErrorMsg "admin should not be able to add user of type 'account'"
$FABRIC_CA_CLIENTEXEC identity modify admin $URI -H $TESTDIR/admin/ -d \
--attrs '"hf.Registrar.Roles=client,user,peer,validator,auditor,ca,app,role1,role2,role3,role4,role5,role6,role7,role8,apple,orange"'
Expand Down
12 changes: 12 additions & 0 deletions util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -825,3 +825,15 @@ func ErrorContains(t *testing.T, err error, contains, msg string, args ...interf
func GetSliceFromList(split string, delim string) []string {
return strings.Split(strings.Replace(split, " ", "", -1), delim)
}

// ListContains looks through a comma separated list to see if a string exists
func ListContains(list, find string) bool {
items := strings.Split(list, ",")
for _, item := range items {
item = strings.TrimPrefix(item, " ")
if item == find {
return true
}
}
return false
}
10 changes: 10 additions & 0 deletions util/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -774,3 +774,13 @@ func TestValidateAndReturnAbsConf(t *testing.T) {
t.Error("Failed to get correct path for configuration file")
}
}

func TestListContains(t *testing.T) {
list := "peer, client,orderer, *"
found := ListContains(list, "*")
assert.Equal(t, found, true)

list = "peer, client,orderer"
found = ListContains(list, "*")
assert.Equal(t, found, false)
}

0 comments on commit 534af8c

Please sign in to comment.