Skip to content

Commit

Permalink
[FAB-5726] 4. Dynamic Cfg - identities: Add/Remove
Browse files Browse the repository at this point in the history
This change set implements the functionality to add
a new identity and to remove an existing identity.

Next change set will implement modifying an identity
functionality.

Change-Id: Ieef7849eeaa220625dc1b91342854e426e378304
Signed-off-by: Saad Karim <[email protected]>
  • Loading branch information
Saad Karim committed Jan 4, 2018
1 parent d63bf9e commit 68c8eec
Show file tree
Hide file tree
Showing 12 changed files with 490 additions and 178 deletions.
1 change: 1 addition & 0 deletions api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ type ModifyIdentityRequest struct {
// fabric-ca-server
type RemoveIdentityRequest struct {
ID string `json:"id" skip:"true"`
Force bool `json:"force"`
CAName string `json:"caname,omitempty" skip:"true"`
}

Expand Down
163 changes: 56 additions & 107 deletions cmd/fabric-ca-client/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ type identityArgs struct {
func (c *ClientCmd) newIdentityCommand() *cobra.Command {
identityCmd := &cobra.Command{
Use: "identity",
Short: "Update an identity",
Long: "Dynamically update an identity on Fabric CA server",
Short: "Manage identities",
Long: "Manage identities",
}
identityCmd.AddCommand(c.newListIdentityCommand())
identityCmd.AddCommand(c.newAddIdentityCommand())
Expand All @@ -51,8 +51,8 @@ func (c *ClientCmd) newIdentityCommand() *cobra.Command {
func (c *ClientCmd) newListIdentityCommand() *cobra.Command {
identityListCmd := &cobra.Command{
Use: "list",
Short: "List information an identity or identities",
Long: "List information an identity or identities from the Fabric CA server",
Short: "List identities",
Long: "List identities visible to caller",
PreRunE: func(cmd *cobra.Command, args []string) error {
log.Level = log.LevelWarning
err := c.configInit()
Expand All @@ -64,14 +64,7 @@ func (c *ClientCmd) newListIdentityCommand() *cobra.Command {

return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
err := c.runListIdentity()
if err != nil {
return err
}

return nil
},
RunE: c.runListIdentity,
}
flags := identityListCmd.Flags()
flags.StringVarP(
Expand All @@ -81,37 +74,12 @@ func (c *ClientCmd) newListIdentityCommand() *cobra.Command {

func (c *ClientCmd) newAddIdentityCommand() *cobra.Command {
identityAddCmd := &cobra.Command{
Use: "add",
Use: "add <id>",
Short: "Add identity",
Long: "Add an identity on Fabric CA server",
Example: "fabric-ca-client identity add <id> [flags]\nfabric-ca-client identity add user1 --type peer",
PreRunE: func(cmd *cobra.Command, args []string) error {
err := argsCheck(args)
if err != nil {
return err
}

err = c.configInit()
if err != nil {
return err
}

log.Debugf("Client configuration settings: %+v", c.clientCfg)

return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
if c.dynamicIdentity.json != "" && checkOtherFlags(cmd) {
return errors.Errorf("Can't use 'json' flag in conjunction with other flags")
}

err := c.runAddIdentity(args)
if err != nil {
return err
}

return nil
},
Long: "Add an identity",
Example: "fabric-ca-client identity add user1 --type peer",
PreRunE: c.identityPreRunE,
RunE: c.runAddIdentity,
}
flags := identityAddCmd.Flags()
util.RegisterFlags(c.myViper, flags, &c.dynamicIdentity.add.IdentityInfo, nil)
Expand All @@ -126,37 +94,12 @@ func (c *ClientCmd) newAddIdentityCommand() *cobra.Command {

func (c *ClientCmd) newModifyIdentityCommand() *cobra.Command {
identityModifyCmd := &cobra.Command{
Use: "modify",
Use: "modify <id>",
Short: "Modify identity",
Long: "Modify an existing identity on Fabric CA server",
Example: "fabric-ca-client identity modify <id> [flags]\nfabric-ca-client identity modify user1 --type peer",
PreRunE: func(cmd *cobra.Command, args []string) error {
err := argsCheck(args)
if err != nil {
return err
}

err = c.configInit()
if err != nil {
return err
}

log.Debugf("Client configuration settings: %+v", c.clientCfg)

return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
if c.dynamicIdentity.json != "" && checkOtherFlags(cmd) {
return errors.Errorf("Can't use 'json' flag in conjunction with other flags")
}

err := c.runModifyIdentity(args)
if err != nil {
return err
}

return nil
},
Long: "Modify an existing identity",
Example: "fabric-ca-client identity modify user1 --type peer",
PreRunE: c.identityPreRunE,
RunE: c.runModifyIdentity,
}
flags := identityModifyCmd.Flags()
tags := map[string]string{
Expand All @@ -174,39 +117,21 @@ func (c *ClientCmd) newModifyIdentityCommand() *cobra.Command {

func (c *ClientCmd) newRemoveIdentityCommand() *cobra.Command {
identityRemoveCmd := &cobra.Command{
Use: "remove",
Use: "remove <id>",
Short: "Remove identity",
Long: "Remove an identity from Fabric CA server",
Example: "fabric-ca-client identity remove <id> [flags]\nfabric-ca-client identity remove user1",
PreRunE: func(cmd *cobra.Command, args []string) error {
err := argsCheck(args)
if err != nil {
return err
}

err = c.configInit()
if err != nil {
return err
}

log.Debugf("Client configuration settings: %+v", c.clientCfg)

return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
err := c.runRemoveIdentity(args)
if err != nil {
return err
}

return nil
},
Long: "Remove an identity",
Example: "fabric-ca-client identity remove user1",
PreRunE: c.identityPreRunE,
RunE: c.runRemoveIdentity,
}
flags := identityRemoveCmd.Flags()
flags.BoolVarP(
&c.dynamicIdentity.remove.Force, "force", "", false, "Forces removing your own identity")
return identityRemoveCmd
}

// The client side logic for executing list identity command
func (c *ClientCmd) runListIdentity() error {
func (c *ClientCmd) runListIdentity(cmd *cobra.Command, args []string) error {
log.Debug("Entered runListIdentity")

id, err := c.loadMyIdentity()
Expand All @@ -233,8 +158,11 @@ func (c *ClientCmd) runListIdentity() error {
}

// The client side logic for adding an identity
func (c *ClientCmd) runAddIdentity(args []string) error {
func (c *ClientCmd) runAddIdentity(cmd *cobra.Command, args []string) error {
log.Debugf("Entered runAddIdentity: %+v", c.dynamicIdentity)
if c.dynamicIdentity.json != "" && checkOtherFlags(cmd) {
return errors.Errorf("Can't use 'json' flag in conjunction with other flags")
}

id, err := c.loadMyIdentity()
if err != nil {
Expand All @@ -257,19 +185,24 @@ func (c *ClientCmd) runAddIdentity(args []string) error {

req.ID = args[0]
req.CAName = c.clientCfg.CAName
req.Secret = c.dynamicIdentity.add.Secret
resp, err := id.AddIdentity(req)
if err != nil {
return err
}

fmt.Printf("Successfully added identity: %+v\n", resp)
fmt.Printf("Successfully added identity: %+v\n", resp.IdentityInfo)
fmt.Printf("Secret: %s\n", resp.Secret)

return nil
}

// The client side logic for modifying an identity
func (c *ClientCmd) runModifyIdentity(args []string) error {
func (c *ClientCmd) runModifyIdentity(cmd *cobra.Command, args []string) error {
log.Debugf("Entered runModifyIdentity: %+v", c.dynamicIdentity)
if c.dynamicIdentity.json != "" && checkOtherFlags(cmd) {
return errors.Errorf("Can't use 'json' flag in conjunction with other flags")
}

req := &api.ModifyIdentityRequest{}

Expand Down Expand Up @@ -303,7 +236,7 @@ func (c *ClientCmd) runModifyIdentity(args []string) error {
}

// The client side logic for removing an identity
func (c *ClientCmd) runRemoveIdentity(args []string) error {
func (c *ClientCmd) runRemoveIdentity(cmd *cobra.Command, args []string) error {
log.Debugf("Entered runRemoveIdentity: %+v", c.dynamicIdentity)

id, err := c.loadMyIdentity()
Expand All @@ -319,12 +252,28 @@ func (c *ClientCmd) runRemoveIdentity(args []string) error {
return err
}

fmt.Printf("Successfully removed identity: %+v\n", resp)
fmt.Printf("Successfully removed identity: %+v\n", resp.IdentityInfo)

return nil
}

func (c *ClientCmd) identityPreRunE(cmd *cobra.Command, args []string) error {
err := argsCheck(args, "Identity")
if err != nil {
return err
}

err = c.configInit()
if err != nil {
return err
}

log.Debugf("Client configuration settings: %+v", c.clientCfg)

return nil
}

// checkOtherFlags returs true if other flags besides '--json' are set
// checkOtherFlags returns true if other flags besides '--json' are set
// Viper.IsSet does not work correctly if there are defaults defined for
// flags. This is a workaround until this bug is addressed in Viper.
// Viper Bug: https://github.com/spf13/viper/issues/276
Expand All @@ -343,9 +292,9 @@ func checkOtherFlags(cmd *cobra.Command) bool {
return false
}

func argsCheck(args []string) error {
func argsCheck(args []string, field string) error {
if len(args) == 0 {
return errors.New("Identity name is required")
return errors.Errorf("%s name is required", field)
}
if len(args) > 1 {
return errors.Errorf("Unknown argument '%s', only the identity name should be passed in as non-flag argument", args[1])
Expand Down
53 changes: 40 additions & 13 deletions cmd/fabric-ca-client/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,6 @@ func TestIdentityCmd(t *testing.T) {
err = RunMain([]string{cmdName, "register", "--id.name", "test user"})
util.FatalError(t, err, "Failed to register user")

// Negative test cases
err = RunMain([]string{
cmdName, "identity", "list"})
assert.NoError(t, err, "Failed to get all ids")
Expand Down Expand Up @@ -527,18 +526,6 @@ func TestIdentityCmd(t *testing.T) {
assert.Contains(t, err.Error(), "Unknown argument")
}

err = RunMain([]string{
cmdName, "identity", "add", "testuser", "--json", `{"type": "peer"}`})
assert.Error(t, err, "Should have failed, not yet implemented")

err = RunMain([]string{
cmdName, "identity", "modify", "testuser", "--type", "client"})
assert.Error(t, err, "Should have failed, not yet implemented")

err = RunMain([]string{
cmdName, "identity", "remove", "testuser"})
assert.Error(t, err, "Should have failed, not yet implemented")

err = RunMain([]string{
cmdName, "identity", "add", "testuser", "--json", `{"type": "peer"}`, "--type", "peer"})
if assert.Error(t, err, "Should have failed") {
Expand All @@ -562,6 +549,46 @@ func TestIdentityCmd(t *testing.T) {
if assert.Error(t, err, "Should have failed") {
assert.Contains(t, err.Error(), "Can't use 'json' flag in conjunction with other flags")
}

// Add user using JSON
err = RunMain([]string{
cmdName, "identity", "add", "testuser1", "--json", `{"secret": "user1pw", "type": "user", "affiliation": "org1", "max_enrollments": 1, "attrs": [{"name:": "hf.Revoker", "value": "true"}]}`})
assert.NoError(t, err, "Failed to add user 'testuser1'")

err = RunMain([]string{
cmdName, "identity", "add", "testuser1", "--json", `{"secret": "user1pw", "type": "user", "affiliation": "org1", "max_enrollments": 1, "attrs": [{"name:": "hf.Revoker", "value": "true"}]}`})
assert.Error(t, err, "Should have failed to add same user twice")

// Add user using flags
err = RunMain([]string{
cmdName, "identity", "add", "testuser2", "--secret", "user2pw", "--type", "user", "--affiliation", ".", "--maxenrollments", "1", "--attrs", "hf.Revoker=true"})
assert.NoError(t, err, "Failed to add user 'testuser2'")

// Check that the secret got correctly configured
err = RunMain([]string{
cmdName, "enroll", "-u", "http://testuser2:user2pw@localhost:7090", "-d"})
assert.NoError(t, err, "Failed to enroll user 'testuser2'")

// Enroll admin back to use it credentials for next commands
err = RunMain([]string{cmdName, "enroll", "-u", enrollURL})
util.FatalError(t, err, "Failed to enroll user")

server.CA.Config.Cfg.Identities.AllowRemove = true

registry := server.CA.DBAccessor()
_, err = registry.GetUser("testuser1", nil)
assert.NoError(t, err, "Failed to get user 'testuser1'")

_, err = registry.GetUser("testuser2", nil)
assert.NoError(t, err, "Failed to get user 'testuser2'")

err = RunMain([]string{
cmdName, "identity", "remove", "testuser1"})
assert.NoError(t, err, "Failed to remove user")

err = RunMain([]string{
cmdName, "identity", "modify", "testuser", "--type", "peer"})
assert.Error(t, err, "Should have failed, not yet implemented")
}

// Verify the certificate has attribute 'name' with a value of 'val'
Expand Down
11 changes: 11 additions & 0 deletions lib/caconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ csr:
// "skip" - to skip the field.
type CAConfig struct {
Version string `skip:"true"`
Cfg cfgOptions
CA CAInfo
Signing *config.Signing
CSR api.CSRInfo
Expand All @@ -104,6 +105,16 @@ type CAConfig struct {
CRL CRLConfig
}

// cfgOptions is a CA configuration that allows for setting different options
type cfgOptions struct {
Identities identitiesOptions
}

// identitiesOptions are options that are related to identities
type identitiesOptions struct {
AllowRemove bool `help:"Enables removing of identities dynamically"`
}

// CAInfo is the CA information on a fabric-ca-server
type CAInfo struct {
Name string `opt:"n" help:"Certificate Authority name"`
Expand Down
4 changes: 4 additions & 0 deletions lib/certdbaccessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ SELECT %s FROM certificates
selectRevokedSQL1 = `
SELECT %s FROM certificates
WHERE (expiry > ? AND expiry < ? AND status='revoked' AND revoked_at > ? AND revoked_at < ?);`

deleteCertificatebyID = `
DELETE FROM certificates
WHERE (ID = ?);`
)

// CertRecord extends CFSSL CertificateRecord by adding an enrollment ID to the record
Expand Down
Loading

0 comments on commit 68c8eec

Please sign in to comment.