Skip to content

Commit

Permalink
Added user registration and revocation functionality
Browse files Browse the repository at this point in the history
Change-Id: I35fc09db4b7c79c8a521b48441647f30e4e55c96
Signed-off-by: Divyank Katira <divyank.katira@securekey.com>
  • Loading branch information
d1vyank committed Mar 24, 2017
1 parent a2ff332 commit c426926
Show file tree
Hide file tree
Showing 4 changed files with 334 additions and 42 deletions.
127 changes: 127 additions & 0 deletions fabric-ca-client/fabricca.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ limitations under the License.
package fabricca

import (
"encoding/base64"
"fmt"
"os"

"github.com/hyperledger/fabric-ca/api"
fabric_ca "github.com/hyperledger/fabric-ca/lib"
"github.com/hyperledger/fabric-sdk-go/config"
fabricclient "github.com/hyperledger/fabric-sdk-go/fabric-client"

"github.com/op/go-logging"
)
Expand All @@ -35,12 +37,47 @@ var logger = logging.MustGetLogger("fabric_sdk_go")
// Services ...
type Services interface {
Enroll(enrollmentID string, enrollmentSecret string) ([]byte, []byte, error)
Register(registrar fabricclient.User, request *RegistrationRequest) (string, error)
Revoke(registrar fabricclient.User, request *RevocationRequest) error
}

type services struct {
fabricCAClient *fabric_ca.Client
}

type RegistrationRequest struct {
// Name is the unique name of the identity
Name string
// Type of identity being registered (e.g. "peer, app, user")
Type string
// MaxEnrollments is the number of times the secret can be reused to enroll.
// if omitted, this defaults to max_enrollments configured on the server
MaxEnrollments int
// The identity's affiliation e.g. org1.department1
Affiliation string
// Optional attributes associated with this identity
Attributes []Attribute
}

type RevocationRequest struct {
// Name of the identity whose certificates should be revoked
// If this field is omitted, then Serial and AKI must be specified.
Name string
// Serial number of the certificate to be revoked
// If this is omitted, then Name must be specified
Serial string
// AKI (Authority Key Identifier) of the certificate to be revoked
AKI string
// Reason is the reason for revocation. See https://godoc.org/golang.org/x/crypto/ocsp
// for valid values. The default value is 0 (ocsp.Unspecified).
Reason int
}

type Attribute struct {
Key string
Value string
}

// NewFabricCAClient ...
/**
* @param {string} clientConfigFile for fabric-ca services"
Expand Down Expand Up @@ -89,3 +126,93 @@ func (fabricCAServices *services) Enroll(enrollmentID string, enrollmentSecret s
}
return id.GetECert().Key(), id.GetECert().Cert(), nil
}

// Register a User with the Fabric CA
// @param {User} registrar The User that is initiating the registration
// @param {RegistrationRequest} request Registration Request
// @returns {string} Enrolment Secret
// @returns {error} Error
func (fabricCAServices *services) Register(registrar fabricclient.User,
request *RegistrationRequest) (string, error) {
// Validate registration request
if request == nil {
return "", fmt.Errorf("Registration request cannot be nil")
}
// Create request signing identity
identity, err := fabricCAServices.createSigningIdentity(registrar)
if err != nil {
return "", fmt.Errorf("Error creating signing identity: %s", err.Error())
}
// Contruct request for Fabric CA client
var attributes []api.Attribute
for i, _ := range request.Attributes {
attributes = append(attributes, api.Attribute{Name: request.
Attributes[i].Key, Value: request.Attributes[i].Value})
}
var req = api.RegistrationRequest{
Name: request.Name,
Type: request.Type,
MaxEnrollments: request.MaxEnrollments,
Affiliation: request.Affiliation,
Attributes: attributes}
// Make registration request
response, err := identity.Register(&req)
if err != nil {
return "", fmt.Errorf("Error Registering User: %s", err.Error())
}
// Decode enrolment secret
secret, err := base64.StdEncoding.DecodeString(response.Secret)
if err != nil {
return "", fmt.Errorf("Error decoding enrolment secret: %s", err.Error())
}

return string(secret), nil
}

// Revoke a User with the Fabric CA
// @param {User} registrar The User that is initiating the revocation
// @param {RevocationRequest} request Revocation Request
// @returns {error} Error
func (fabricCAServices *services) Revoke(registrar fabricclient.User,
request *RevocationRequest) error {
// Validate revocation request
if request == nil {
return fmt.Errorf("Revocation request cannot be nil")
}
// Create request signing identity
identity, err := fabricCAServices.createSigningIdentity(registrar)
if err != nil {
return fmt.Errorf("Error creating signing identity: %s", err.Error())
}
// Create revocation request
var req = api.RevocationRequest{
Name: request.Name,
Serial: request.Serial,
AKI: request.AKI,
Reason: request.Reason}
return identity.Revoke(&req)
}

// createSigningIdentity creates an identity to sign Fabric CA requests with
func (fabricCAServices *services) createSigningIdentity(user fabricclient.
User) (*fabric_ca.Identity, error) {
// Validate user
if user == nil {
return nil, fmt.Errorf("Valid user required to create signing identity")
}
// Validate enrolment information
cert := user.GetEnrollmentCertificate()
key := user.GetPrivateKey()
if key == nil || cert == nil {
return nil, fmt.Errorf(
"Unable to read user enrolment information to create signing identity")
}
// TODO: Right now this reads the key from a default BCCSP implementation using the SKI
// this method signature will change to accepting a BCCSP key soon.
// Track changes here: https://gerrit.hyperledger.org/r/#/c/6727/
ski := key.SKI()
if ski == nil {
return nil, fmt.Errorf("Unable to read private key SKI")
}
return fabricCAServices.fabricCAClient.NewIdentity(ski, cert)
}
83 changes: 83 additions & 0 deletions fabric-ca-client/fabricca_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ limitations under the License.
package fabricca

import (
"io/ioutil"
"testing"

"github.com/hyperledger/fabric-sdk-go/fabric-ca-client/mocks"
"github.com/hyperledger/fabric-sdk-go/fabric-client"
)

func TestEnrollWithMissingParameters(t *testing.T) {
Expand All @@ -43,3 +47,82 @@ func TestEnrollWithMissingParameters(t *testing.T) {
t.Fatalf("Enroll didn't return right error")
}
}

func TestRegister(t *testing.T) {
fabricCAClient, err := NewFabricCAClient()
if err != nil {
t.Fatalf("NewFabricCAClient returned error: %v", err)
}
mockKey := &mocks.MockKey{}
user := fabricclient.NewUser("test")
// Register with nil request
_, err = fabricCAClient.Register(user, nil)
if err == nil {
t.Fatalf("Expected error with nil request")
}
//Register with nil user
_, err = fabricCAClient.Register(nil, &RegistrationRequest{})
if err == nil {
t.Fatalf("Expected error with nil user")
}
// Register with nil user cert and key
_, err = fabricCAClient.Register(user, &RegistrationRequest{})
if err == nil {
t.Fatalf("Expected error without user enrolment information")
}
user.SetEnrollmentCertificate(readCert(t))
user.SetPrivateKey(mockKey)
// Register without registration name paramter
_, err = fabricCAClient.Register(user, &RegistrationRequest{})
if err.Error() != "Error Registering User: Register was called without a Name set" {
t.Fatalf("Expected error without registration information. Got: %s", err.Error())
}
// Register without registration affiliation paramter
_, err = fabricCAClient.Register(user, &RegistrationRequest{Name: "test"})
if err.Error() != "Error Registering User: Registration request does not have an affiliation" {
t.Fatalf("Expected error without registration information. Got: %s", err.Error())
}
// Register with valid request
var attributes []Attribute
attributes = append(attributes, Attribute{Key: "test1", Value: "test2"})
attributes = append(attributes, Attribute{Key: "test2", Value: "test3"})
_, err = fabricCAClient.Register(user, &RegistrationRequest{Name: "test",
Affiliation: "test", Attributes: attributes})
if err == nil {
t.Fatalf("Expected PEM decoding error with test cert")
}
}

func TestRevoke(t *testing.T) {
fabricCAClient, err := NewFabricCAClient()
if err != nil {
t.Fatalf("NewFabricCAClient returned error: %v", err)
}
mockKey := &mocks.MockKey{}
user := fabricclient.NewUser("test")
// Revoke with nil request
err = fabricCAClient.Revoke(user, nil)
if err == nil {
t.Fatalf("Expected error with nil request")
}
//Revoke with nil user
err = fabricCAClient.Revoke(nil, &RevocationRequest{})
if err == nil {
t.Fatalf("Expected error with nil user")
}
user.SetEnrollmentCertificate(readCert(t))
user.SetPrivateKey(mockKey)
err = fabricCAClient.Revoke(user, &RevocationRequest{})
if err == nil {
t.Fatalf("Expected decoding error with test cert")
}
}

// Reads a random cert for testing
func readCert(t *testing.T) []byte {
cert, err := ioutil.ReadFile("../test/fixtures/root.pem")
if err != nil {
t.Fatalf("Error reading cert: %s", err.Error())
}
return cert
}
46 changes: 46 additions & 0 deletions fabric-ca-client/mocks/mockkey.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
Copyright SecureKey Technologies Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package mocks

import "github.com/hyperledger/fabric/bccsp"

// MockKey mocks BCCSP key
type MockKey struct {
}

func (m *MockKey) Bytes() ([]byte, error) {
return []byte("Not implemented"), nil
}

func (m *MockKey) SKI() []byte {
return []byte("Not implemented")
}

func (m *MockKey) Symmetric() bool {
return false
}

func (m *MockKey) Private() bool {
return true
}

func (m *MockKey) PublicKey() (bccsp.Key, error) {
return m, nil
}
Loading

0 comments on commit c426926

Please sign in to comment.