Skip to content

Commit

Permalink
[FAB-6647] 2. Maintain backwards compatibility
Browse files Browse the repository at this point in the history
Need to maintain backwards compatibility for any
functionality that is added that affect functionality
of current users.

Version checks are made on the server configuration file,
server executable, and database version. Based on the versions
it is determined if a migration is needed of existing identities
to work with the latest release of fabric-ca-server.

Adds logic that will update existing registrar's in the database
to have the 'hf.Registrar.Attributes' attribute with a value of '*'.

Change-Id: I2f8bd7a610900fc4487966f9eeea7b2b1fc68d4c
Signed-off-by: Saad Karim <[email protected]>
  • Loading branch information
Saad Karim committed Dec 1, 2017
1 parent 6b6b294 commit f187f3d
Show file tree
Hide file tree
Showing 10 changed files with 291 additions and 17 deletions.
46 changes: 43 additions & 3 deletions lib/ca.go
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,7 @@ func (ca *CA) initUserRegistry() error {
return err
}

// Use the DB for the user registry and load users
// Use the DB for the user registry
dbAccessor := new(Accessor)
dbAccessor.SetDB(ca.db)
ca.registry = dbAccessor
Expand Down Expand Up @@ -1097,7 +1097,21 @@ func (ca *CA) loadCNFromEnrollmentInfo(certFile string) (string, error) {
func (ca *CA) performMigration() error {
log.Debug("Checking and performing migration, if needed")

// TODO: Migration logic will go here
users, err := ca.registry.GetUserLessThanLevel(metadata.IdentityLevel)
if err != nil {
return err
}

for _, user := range users {
currentLevel := user.GetLevel()
if currentLevel < 1 {
err := ca.migrateUserToLevel1(user)
if err != nil {
return err
}
currentLevel++
}
}

sl, err := metadata.GetLevels(metadata.GetVersion())
if err != nil {
Expand All @@ -1111,10 +1125,12 @@ func (ca *CA) performMigration() error {
return nil
}

// This function returns an error if the version specified in the configuration file is greater than the server version
func (ca *CA) checkConfigLevels() error {
var err error
serverVersion := metadata.GetVersion()
configVersion := ca.Config.Version
log.Debugf("Checking configuration file verion '%+v' against server version: '%+v'", configVersion, serverVersion)
log.Debugf("Checking configuration file version '%+v' against server version: '%+v'", configVersion, serverVersion)
// Check configuration file version against server version to make sure that newer configuration file is not being used with server
cmp, err := metadata.CmpVersion(configVersion, serverVersion)
if err != nil {
Expand Down Expand Up @@ -1151,6 +1167,30 @@ func (ca *CA) checkDBLevels() error {
return nil
}

func (ca *CA) migrateUserToLevel1(user spi.User) error {
log.Debugf("Migrating user '%s' to level 1", user.GetName())

// Update identity to level 1
_, err := user.GetAttribute("hf.Registrar.Roles") // Check if user a registrar
if err == nil {
_, err := user.GetAttribute("hf.Registrar.Attributes") // Check if user already has "hf.Registrar.Attributes" attribute
if err != nil {
addAttr := []api.Attribute{api.Attribute{Name: "hf.Registrar.Attributes", Value: "*"}}
err := user.ModifyAttributes(addAttr)
if err != nil {
return errors.WithMessage(err, "Failed to set attribute")
}
}
}

err = user.SetLevel(1)
if err != nil {
return errors.WithMessage(err, "Failed to update level of user")
}

return nil
}

func writeFile(file string, buf []byte, perm os.FileMode) error {
err := os.MkdirAll(filepath.Dir(file), 0755)
if err != nil {
Expand Down
44 changes: 44 additions & 0 deletions lib/ca_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (

"github.com/cloudflare/cfssl/csr"
"github.com/hyperledger/fabric-ca/api"
"github.com/hyperledger/fabric-ca/lib/dbutil"
"github.com/hyperledger/fabric-ca/util"
"github.com/hyperledger/fabric/bccsp/factory"
"github.com/hyperledger/fabric/bccsp/pkcs11"
Expand Down Expand Up @@ -763,6 +764,49 @@ func TestCAVerifyCertificate(t *testing.T) {
CAclean(ca, t)
}

// Loads a registrar user and a non-registrar user into database. Server is started using an existing database
// with users. This test verifies that the registrar is given the new attribute "hf.Registrar.Attribute" but
// the non-registrar user is not.
func TestServerMigration(t *testing.T) {
dir := "migrationTest"
os.RemoveAll(dir)
defer os.RemoveAll(dir)
os.Mkdir(dir, 0777)
db, err := dbutil.NewUserRegistrySQLLite3(filepath.Join(dir, "fabric-ca-server.db"))
util.FatalError(t, err, "Failed to create db")
_, err = db.Exec("INSERT INTO users (id, token, type, affiliation, attributes, state, max_enrollments, level) VALUES ('registrar', '', 'user', 'org2', '[{\"name\":\"hf.Registrar.Roles\",\"value\":\"user,peer,client\"}]', '0', '-1', '0')")
assert.NoError(t, err, "Failed to insert user 'registrar' into database")
_, err = db.Exec("INSERT INTO users (id, token, type, affiliation, attributes, state, max_enrollments, level) VALUES ('notregistrar', '', 'user', 'org2', '[{\"name\":\"hf.Revoker\",\"value\":\"true\"}]', '0', '-1', '0')")
assert.NoError(t, err, "Failed to insert user 'notregistrar' into database")

server := TestGetServer2(false, rootPort, dir, "", -1, t)
if server == nil {
return
}
err = server.Start()
util.FatalError(t, err, "Server start failed")
defer func() {
err = server.Stop()
if err != nil {
t.Errorf("Failed to stop server: %s", err)
}
}()

registrar, err := server.CA.registry.GetUser("registrar", nil)
assert.NoError(t, err, "Failed to get user")
registrarAttr, err := registrar.GetAttribute("hf.Registrar.Attributes")
assert.NoError(t, err, "Failed to get attribute")
t.Logf("registrarAttr: '%+v'", registrarAttr)
if registrarAttr.Value == "" {
t.Error("Failed to correctly migrate user 'registrar'")
}

notregistrar, err := server.CA.registry.GetUser("notregistrar", nil)
assert.NoError(t, err, "Failed to get user")
_, err = notregistrar.GetAttribute("hf.Registrar.Attributes")
assert.Error(t, err, "Non-registrar user should not have this attribute, failed to correctly migrate user")
}

func getCertFromFile(f string) (*x509.Certificate, error) {
p, err := ioutil.ReadFile(f)
if err != nil {
Expand Down
2 changes: 2 additions & 0 deletions lib/client_whitebox_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -519,13 +519,15 @@ func TestCWBCAConfig(t *testing.T) {
t.Errorf("initConfig failed: %s", err)
}
ca.Config = new(CAConfig)
ca.server = &Server{}
ca.Config.CA.Certfile = "../testdata/ec_cert.pem"
ca.Config.CA.Keyfile = "../testdata/ec_key.pem"
err = ca.initConfig()
if err != nil {
t.Errorf("initConfig failed: %s", err)
}
s := &Server{}
s.CA.Config = &CAConfig{}
err = s.initConfig()
if err != nil {
t.Errorf("server.initConfig default failed: %s", err)
Expand Down
10 changes: 10 additions & 0 deletions lib/ldap/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,11 @@ func (lc *Client) GetProperties(name []string) (map[string]string, error) {
return nil, errNotSupported
}

// GetUserLessThanLevel returns all identities that are less than the level specified
func (lc *Client) GetUserLessThanLevel(version int) ([]spi.User, error) {
return nil, errNotSupported
}

// Connect to the LDAP server and bind as user as admin user as specified in LDAP URL
func (lc *Client) newConnection() (conn *ldap.Conn, err error) {
address := fmt.Sprintf("%s:%d", lc.Host, lc.Port)
Expand Down Expand Up @@ -318,6 +323,11 @@ func (u *User) GetLevel() int {
return 0
}

// SetLevel sets the level of the user
func (u *User) SetLevel(level int) error {
return errNotSupported
}

// Login logs a user in using password
func (u *User) Login(password string, caMaxEnrollment int) error {

Expand Down
9 changes: 8 additions & 1 deletion lib/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,14 @@ func (s *Server) Init(renew bool) (err error) {

// init initializses the server leaving the DB open
func (s *Server) init(renew bool) (err error) {
log.Debugf("Server Version: %s", metadata.GetVersion())
serverVersion := metadata.GetVersion()
log.Infof("Server Version: %s", serverVersion)
s.levels, err = metadata.GetLevels(serverVersion)
if err != nil {
return err
}
log.Infof("Server Levels: %+v", s.levels)

// Initialize the config
err = s.initConfig()
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions lib/serverregister.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ func registerUserID(req *api.RegistrationRequestNet, ca *CA) (string, error) {
Affiliation: req.Affiliation,
Attributes: req.Attributes,
MaxEnrollments: req.MaxEnrollments,
Level: ca.server.levels.Identity,
}

registry := ca.registry
Expand Down
5 changes: 4 additions & 1 deletion lib/spi/userregistry.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,16 @@ type User interface {
GetAttribute(name string) (*api.Attribute, error)
// GetAttributes returns the requested attributes
GetAttributes(attrNames []string) ([]api.Attribute, error)
// ModifyAttributes adds a new attribute or modifies existing attribute
// ModifyAttributes adds, removes, or deletes attribute
ModifyAttributes(attrs []api.Attribute) error
// LoginComplete completes the login process by incrementing the state of the user
LoginComplete() error
// Revoke will revoke the user, setting the state of the user to be -1
Revoke() error
// GetLevel returns the level of the user, level is used to verify if the user needs migration
GetLevel() int
// SetLevel sets the level of the user
SetLevel(level int) error
}

// UserRegistry is the API for retreiving users and groups
Expand All @@ -73,4 +75,5 @@ type UserRegistry interface {
DeleteAffiliation(name string) error
// GetProperties returns the properties by name from the database
GetProperties(name []string) (map[string]string, error)
GetUserLessThanLevel(version int) ([]User, error)
}
1 change: 1 addition & 0 deletions lib/test-util.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ func TestGetServer2(deleteHome bool, port int, home, parentURL string, maxEnroll
Profiles: profiles,
Default: defaultProfile,
},
Version: "1.1.0", // The default test server/ca should use the latest version
},
},
HomeDir: home,
Expand Down
Loading

0 comments on commit f187f3d

Please sign in to comment.