From 81097b92bb5d68d7293bc5f2e56da396c56a0852 Mon Sep 17 00:00:00 2001 From: Keith Smith Date: Mon, 9 Jan 2017 16:34:07 -0500 Subject: [PATCH] COP API simplification See https://jira.hyperledger.org/browse/FAB-1462 for a description of what was done in this change set. This draws clean lines around the client library API defined in api/client.go verses the REST APIs of what goes over the network in api/net.go, which defines the body of POST and responses. This should help in generating swagger doc for the REST APIs. Note that the tcert library at lib/tcert has it's own API in lib/tcert/api.go also. Change-Id: Idb4500b3a5dbc04dfeb65d5b79f8b803411d975f Signed-off-by: Keith Smith --- api.go | 104 -------------- api/api.go | 204 -------------------------- api/api_test.go | 17 --- api/client.go | 141 ++++++++++++++++++ api/error.go | 95 ------------ api/error_test.go | 59 -------- api/net.go | 80 +++++++++++ cli/client/enroll.go | 4 +- cli/client/reenroll.go | 6 +- cli/client/register.go | 11 +- cli/client/revoke.go | 4 +- cli/server/certdbaccessor.go | 11 +- cli/server/config.go | 4 +- cli/server/dasqlite_test.go | 10 +- cli/server/dbaccessor.go | 22 ++- cli/server/dbutil/dbutil.go | 15 +- cli/server/enroll.go | 6 +- cli/server/errors.go | 14 +- cli/server/register.go | 26 ++-- cli/server/register_test.go | 30 ++-- cli/server/revoke.go | 10 +- cli/server/server_test.go | 58 ++++---- cli/server/spi/userregistry.go | 4 +- cli/server/tcert.go | 10 +- idp/idp.go | 255 --------------------------------- lib/client.go | 202 ++++++-------------------- lib/client_test.go | 75 +++++----- lib/identity.go | 148 +++++++++++-------- lib/identity_test.go | 22 +-- lib/signer.go | 54 +++---- lib/signer_test.go | 75 ---------- lib/tcert/api.go | 10 +- lib/tcert/tcert.go | 2 +- lib/temporalsigner.go | 38 ----- lib/temporalsigner_test.go | 38 ----- lib/tls/tls.go | 4 +- lib/util.go | 48 +++++++ lib/verifier.go | 93 ------------ lib/verifier_test.go | 94 ------------ scripts/bash_profile | 2 +- scripts/run_tests | 2 +- testdata/testconfig.json | 16 +++ util/util.go | 32 ++--- 43 files changed, 616 insertions(+), 1539 deletions(-) delete mode 100644 api.go delete mode 100644 api/api.go delete mode 100644 api/api_test.go create mode 100644 api/client.go delete mode 100644 api/error.go delete mode 100644 api/error_test.go create mode 100644 api/net.go delete mode 100644 idp/idp.go delete mode 100644 lib/signer_test.go delete mode 100644 lib/temporalsigner.go delete mode 100644 lib/temporalsigner_test.go create mode 100644 lib/util.go delete mode 100644 lib/verifier.go delete mode 100644 lib/verifier_test.go diff --git a/api.go b/api.go deleted file mode 100644 index 2711c22d0..000000000 --- a/api.go +++ /dev/null @@ -1,104 +0,0 @@ -/* -Copyright IBM Corp. 2016 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. -*/ - -/* - * This file is simply a mirror of the interfaces in interfaces/interfaces.go. - * This was done in order to prevent an import cycle. - */ - -package cop - -import real "github.com/hyperledger/fabric-cop/api" - -// Mgr is the main interface to COP functionality -type Mgr interface { - real.Mgr -} - -// CertMgr is a COP certificate manager -type CertMgr interface { - real.CertMgr -} - -// JoinRequest is the state of a request to join the blockchain network -type JoinRequest struct { - real.JoinRequest -} - -// JoinRequestListener is a listener for join requests -type JoinRequestListener real.JoinRequestListener - -// JoinRequestStatus is the status of a join request -type JoinRequestStatus real.JoinRequestStatus - -// Values denoting the possible values of the JoinRequestStatus -const ( - JRSWaiting = real.JRSWaiting - JRSApproved = real.JRSApproved - JRSDenied = real.JRSDenied -) - -// JoinResponseType are the types of responses which can be provided to a JoinRequest -type JoinResponseType real.JoinResponseType - -// Values denoting the possible values of the JoinResponseType -const ( - JRTApprove = real.JRTApprove - JRTDeny = real.JRTDeny - JRTAbstain = real.JRTAbstain - JRTCount = real.JRTCount -) - -// CertHandler provides functions related to a certificate -type CertHandler interface { - real.CertHandler -} - -// KeyHandler provides functions related to a key -type KeyHandler interface { - real.KeyHandler -} - -// RegisterRequest information -type RegisterRequest struct { - real.RegisterRequest -} - -// EnrollRequest is an enroll request -type EnrollRequest struct { - real.EnrollRequest -} - -// Identity is any type of identity which is opaque for now -type Identity real.Identity - -// The following are all the error codes returned by COP. -// The values begin with "100000" to avoid overlap with CFSSL errors. -// Add all new errors to the end of the current list. -const ( - // NotImplemented means not yet implemented but plans to support - NotImplemented = real.NotImplemented - // NotSupported means no current plans to support - NotSupported = real.NotSupported - InvalidProviderName = real.InvalidProviderName - TooManyArgs = real.TooManyArgs - NotInitialized = real.NotInitialized -) - -// Error is an interface with a Code method -type Error interface { - real.Error -} diff --git a/api/api.go b/api/api.go deleted file mode 100644 index b419ee739..000000000 --- a/api/api.go +++ /dev/null @@ -1,204 +0,0 @@ -/* -Copyright IBM Corp. 2016 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. -*/ - -/* - * This file contains interfaces for the COP library. - * COP provides police-like security functions for Hyperledger Fabric. - */ - -package api - -import "github.com/hyperledger/fabric-cop/idp" - -// Mgr is the main interface to COP functionality -type Mgr interface { - - // NewCertMgr creates a COP certificate manager - NewCertMgr() CertMgr -} - -// Client is a COP client -type Client interface { - //GetTcertBatch gets a batch of tcerts - GetTcertBatch(jsonString string, signatureJSON string) (string, error) - // GetHomeDir returns the home directory - GetHomeDir() string - - // SetHomeDir sets the home directory - SetHomeDir(dir string) - - // GetServerAddr returns the server address - GetServerAddr() string - - // SetServerAddr sets the server address - SetServerAddr(dir string) - - // Register a new identity - Register(registration *RegisterRequest) ([]byte, Error) - - // Enroll a registered identity - // Enroll(user, pass string) (Identity, Error) - Enroll(enroll *EnrollRequest, csrJSON string) ([]byte, Error) - - // RegisterAndEnroll registers and enrolls a new identity - RegisterAndEnroll(registration *RegisterRequest) (Identity, Error) - - /* - // SubmitJoinRequest submits a join request, implicitly approving by the caller - // Returns the join request ID - SubmitJoinRequest(participantFilePath string) (JoinRequest, Error) - - // ApproveJoinRequest approves the join request - ApproveJoinRequest(joinRequestID string) Error - - // DenyJoinRequest denies the join request - DenyJoinRequest(joinRequestID string) Error - - // ListJoinRequests lists the currently outstanding join requests for the blockchain network - ListJoinRequests() ([]JoinRequest, Error) - - // ListParticipants lists the current participants in the blockchain network - ListParticipants() ([]string, Error) - - // Set the listener to be called when a JoinRequestEvent is emitted - SetJoinRequestListener(listener JoinRequestListener) - */ -} - -// JoinRequest is the state of a request to join the blockchain network -type JoinRequest struct { - ID string // Unique ID of join request - Info string // The original JSON request from the participant - Status JoinRequestStatus // waiting, approved, or denied - Responses [JRTCount][]string // participant names of approvers -} - -// JoinRequestListener is a listener for join requests -type JoinRequestListener func(JoinRequest) - -// JoinRequestStatus is the status of a join request -type JoinRequestStatus int - -// Values denoting the possible values of the JoinRequestStatus -const ( - JRSWaiting JoinRequestStatus = iota - JRSApproved - JRSDenied -) - -// JoinResponseType are the types of responses which can be provided to a JoinRequest -type JoinResponseType int - -// Values denoting the possible values of the JoinResponseType -const ( - JRTApprove JoinResponseType = iota - JRTDeny - JRTAbstain - JRTCount -) - -// CertMgr is the interface for all certificate-based management -type CertMgr interface { - - // GenCert generates a certificate - GenCert(csr string, prefix string, participantFile string) Error - - // InitSelfSign generates self-signed certs and updates the participant file - InitSelfSign(domain string, path string) Error - - // InitLego gets certificates from Let's Encrypt and updates the participant file - InitLego(host string) Error - - // SetECAKey sets the ECA key - SetECAKey(key []byte) Error - - // SetTCAKey sets the TCA key - SetTCAKey(key []byte) Error - - // Set the path for the participant file - SetParticipantFilePath(path string) Error - - // UpdateParticipantFile - UpdateParticipantFile() Error - - // LoadFromString - //LoadFromString(str string) Error - - // StoreToString - //StoreToString() string - - // NewCertHandler creates a COP certificate handler - NewCertHandler(cert []byte) (CertHandler, Error) - - // NewKeyHandler creates a COP key handler - NewKeyHandler(key []byte) (KeyHandler, Error) -} - -// CertHandler provides functions related to a certificate -type CertHandler interface { - // GetId returns the ID of the owner of this cert - GetID() string - // GetPartipantId returns the participant ID associated with this cert - GetParticipantID() string - // Determine if the caller has a specific role (e.g. 'orderer', 'peer', etc) - IsType(role string) bool - // Verify a signature against this certificate - Verify(buf []byte, signature []byte) (bool, Error) -} - -// KeyHandler provides functions related to a key -type KeyHandler interface { - CertHandler - // Create a signature using this key - Sign(buf []byte) ([]byte, Error) -} - -// RegisterRequest information -type RegisterRequest struct { - User string `json:"user"` - Group string `json:"group"` - Type string `json:"type"` // Type of identity being registered (e.g. "peer, app, user") - Attributes []idp.Attribute `json:"attrs,omitempty"` - CallerID string `json:"callerID"` -} - -// EnrollRequest - information need to process enrollment request to server -type EnrollRequest struct { - User string `json:"user"` - Token []byte `json:"token"` - CSR []byte `json:"csr"` -} - -// Enrollment - information need to process enrollment request to client -type Enrollment struct { - ID string - EnrollSecret []byte -} - -// Identity is any type of identity which is opaque for now -type Identity interface{} - -var mgr Mgr - -// SetMgr sets the COP manager -func SetMgr(m Mgr) { - mgr = m -} - -// NewCertMgr creates a COP certificate manager -func NewCertMgr() CertMgr { - return mgr.NewCertMgr() -} diff --git a/api/api_test.go b/api/api_test.go deleted file mode 100644 index 445b32826..000000000 --- a/api/api_test.go +++ /dev/null @@ -1,17 +0,0 @@ -/* -Copyright IBM Corp. 2016 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 api diff --git a/api/client.go b/api/client.go new file mode 100644 index 000000000..6c0365176 --- /dev/null +++ b/api/client.go @@ -0,0 +1,141 @@ +/* +Copyright IBM Corp. 2016 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 api + +import ( + "time" + + "github.com/cloudflare/cfssl/csr" + "github.com/hyperledger/fabric-cop/lib/tcert" +) + +// RegistrationRequest for a new identity +type RegistrationRequest struct { + // Name is the unique name of the identity + Name string `json:"id"` + // Type of identity being registered (e.g. "peer, app, user") + Type string `json:"type"` + // Secret is an optional password. If not specified, + // a random secret is generated. In both cases, the secret + // is returned in the RegistrationResponse. + Secret string `json:"secret,omitempty"` + // MaxEnrollments is the maximum number of times the secret can + // be reused to enroll. + MaxEnrollments int `json:"max_enrollments,omitempty"` + // is returned in the response. + // Group name associated with the identity + Group string `json:"group"` + // Attributes associated with this identity + Attributes []Attribute `json:"attrs,omitempty"` +} + +// RegistrationResponse is a registration response +type RegistrationResponse struct { + // The secret returned from a successful registration response + Secret string `json:"credential,omitempty"` +} + +// EnrollmentRequest is a request to enroll an identity +type EnrollmentRequest struct { + // The identity name to enroll + Name string `json:"name"` + // The secret returned via Register + Secret string `json:"secret,omitempty"` + // Hosts is a comma-separated host list in the CSR + Hosts string `json:"hosts,omitempty"` + // Profile is the name of the signing profile to use in issuing the certificate + Profile string `json:"profile,omitempty"` + // Label is the label to use in HSM operations + Label string `json:"label,omitempty"` + // CSR is Certificate Signing Request info + CSR *CSRInfo `json:"csr,omitempty"` +} + +// ReenrollmentRequest is a request to reenroll an identity. +// This is useful to renew a certificate before it has expired. +type ReenrollmentRequest struct { + // Hosts is a comma-separated host list in the CSR + Hosts string `json:"hosts,omitempty"` + // Profile is the name of the signing profile to use in issuing the certificate + Profile string `json:"profile,omitempty"` + // Label is the label to use in HSM operations + Label string `json:"label,omitempty"` + // CSR is Certificate Signing Request info + CSR *CSRInfo `json:"csr,omitempty"` +} + +// RevocationRequest is a revocation request for a single certificate or all certificates +// associated with an identity. +// To revoke a single certificate, both the Serial and AKI fields must be set; +// otherwise, to revoke all certificates and the identity associated with an enrollment ID, +// the Name field must be set to an existing enrollment ID. +// A RevocationRequest can only be performed by a user with the "hf.Revoker" 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 `json:"id,omitempty"` + // Serial number of the certificate to be revoked + // If this is omitted, then Name must be specified + Serial string `json:"serial,omitempty"` + // AKI (Authority Key Identifier) of the certificate to be revoked + AKI string `json:"aki,omitempty"` + // 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 `json:"reason,omitempty"` +} + +// GetTCertBatchRequest is input provided to identity.GetTCertBatch +type GetTCertBatchRequest struct { + // Number of TCerts in the batch. + Count int `json:"count"` + // The attribute names whose names and values are to be sealed in the issued TCerts. + AttrNames []string `json:"attr_names,omitempty"` + // EncryptAttrs denotes whether to encrypt attribute values or not. + // When set to true, each issued TCert in the batch will contain encrypted attribute values. + EncryptAttrs bool `json:"encrypt_attrs,omitempty"` + // Certificate Validity Period. If specified, the value used + // is the minimum of this value and the configured validity period + // of the TCert manager. + ValidityPeriod time.Duration `json:"validity_period,omitempty"` + // The pre-key to be used for key derivation. + PreKey string `json:"prekey"` + // DisableKeyDerivation if true disables key derivation so that a TCert is not + // cryptographically related to an ECert. This may be necessary when using an + // HSM which does not support the TCert's key derivation function. + DisableKeyDerivation bool `json:"disable_kdf,omitempty"` +} + +// GetTCertBatchResponse is the return value of identity.GetTCertBatch +type GetTCertBatchResponse struct { + tcert.GetBatchResponse +} + +// CSRInfo is Certificate Signing Request information +type CSRInfo struct { + CN string `json:"CN"` + Names []csr.Name `json:"names,omitempty"` + Hosts []string `json:"hosts,omitempty"` + KeyRequest *csr.BasicKeyRequest `json:"key,omitempty"` + CA *csr.CAConfig `json:"ca,omitempty"` + SerialNumber string `json:"serial_number,omitempty"` +} + +// Attribute is a name and value pair +type Attribute struct { + Name string `json:"name"` + Value string `json:"value"` +} diff --git a/api/error.go b/api/error.go deleted file mode 100644 index fa8a3e3fd..000000000 --- a/api/error.go +++ /dev/null @@ -1,95 +0,0 @@ -/* -Copyright IBM Corp. 2016 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 api - -import ( - "fmt" - - cfssl "github.com/cloudflare/cfssl/errors" -) - -// The following are all the error codes returned by COP. -// COP's errors start from 100,000 to avoid an overlap with CFSSL Toolkit's errors. -// Append new errors to the end of the below list for backward compatibility. -const ( - // NotImplemented means not yet implemented but plans to support - NotImplemented int = 100000 + iota - CFSSL - NotSupported - TooManyArgs - InvalidProviderName - ReadFileError - WriteFileError - MarshallError - UnmarshallError - InvalidConfig - NotInitialized - AlreadyEnrolled - ServerAddrNotSet - AuthorizationFailure - IOError - Input - Output - UserStoreError - EnrollingUserError - RegisteringUserError - DatabaseError - TLSError -) - -// Error is an interface with a Code method -type Error interface { - error - // Code returns the specific error code - Code() int -} - -// ErrorImpl is the implementation of the Error interface -type ErrorImpl struct { - ErrorCode int `json:"code"` - Message string `json:"message"` -} - -// Error implements the error interface -func (e *ErrorImpl) Error() string { - return fmt.Sprintf("%d: %s", e.ErrorCode, e.Message) -} - -// Code implements the error interface -func (e *ErrorImpl) Code() int { - return e.ErrorCode -} - -// NewError constructor for COP errors -func NewError(code int, format string, args ...interface{}) *ErrorImpl { - msg := fmt.Sprintf(format, args...) - return &ErrorImpl{ErrorCode: code, Message: msg} -} - -// WrapError another COP error -func WrapError(err error, code int, format string, args ...interface{}) *ErrorImpl { - msg := fmt.Sprintf(format, args) - msg = fmt.Sprintf("%s [%s]", msg, err.Error()) - return &ErrorImpl{ErrorCode: code, Message: msg} -} - -// WrapCFSSLError wraps a CFSSL error -func WrapCFSSLError(error *cfssl.Error, code int, format string, args ...interface{}) *ErrorImpl { - msg := fmt.Sprintf(format, args) - msg = fmt.Sprintf("%s [CFSSL %d: %s]", msg, error.ErrorCode, error.Message) - return &ErrorImpl{ErrorCode: code, Message: msg} -} diff --git a/api/error_test.go b/api/error_test.go deleted file mode 100644 index 55606f8fc..000000000 --- a/api/error_test.go +++ /dev/null @@ -1,59 +0,0 @@ -/* -Copyright IBM Corp. 2016 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 api - -import ( - "testing" - - cfsslErr "github.com/cloudflare/cfssl/errors" -) - -func TestNewError(t *testing.T) { - err := NewError(InvalidProviderName, "invalid factory name: %s", "foo") - if err == nil { - t.Error("Error creation failed.") - } - if err.Error() == "" { - t.Errorf("returned empty error") - } - if err.Code() != 100004 { - t.Errorf("invalid error code; expecting 100003 but found %d", err.ErrorCode) - } - t.Logf("TestNew: %v", err) -} - -func TestWrapError(t *testing.T) { - err := NewError(InvalidProviderName, "invalid factory name: %s", "foo") - if err == nil { - t.Error("Error creation failed.") - } - err = WrapError(err, NotImplemented, "feature 'foo' has not implemented") - if err == nil { - t.Error("Wrap creation failed.") - } -} - -func TestCfsslWrapError(t *testing.T) { - err := cfsslErr.New(cfsslErr.CertificateError, cfsslErr.Unknown) - if err == nil { - t.Fatal("CFSSL Error creation failed.") - } - err2 := WrapCFSSLError(err, 1, "wrapped error") - if err2 == nil { - t.Fatal("COP Error creation failed.") - } -} diff --git a/api/net.go b/api/net.go new file mode 100644 index 000000000..53b09840b --- /dev/null +++ b/api/net.go @@ -0,0 +1,80 @@ +/* +Copyright IBM Corp. 2016 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 api + +import "github.com/hyperledger/fabric-cop/lib/tcert" + +/* + * This file contains the structure definitions for the request + * and responses which flow over the network between a COP client + * and the COP server. + */ + +// RegistrationRequestNet is the registration request for a new identity +type RegistrationRequestNet struct { + RegistrationRequest +} + +// RegistrationResponseNet is a registration response +type RegistrationResponseNet struct { + RegistrationResponse +} + +// EnrollmentRequestNet is a request to enroll an identity +type EnrollmentRequestNet struct { + EnrollmentRequest +} + +// ReenrollmentRequestNet is a request to reenroll an identity. +// This is useful to renew a certificate before it has expired. +type ReenrollmentRequestNet struct { + ReenrollmentRequest +} + +// RevocationRequestNet is a revocation request which flows over the network +// to the COP server. +// To revoke a single certificate, both the Serial and AKI fields must be set; +// otherwise, to revoke all certificates and the identity associated with an enrollment ID, +// the Name field must be set to an existing enrollment ID. +// A RevocationRequest can only be performed by a user with the "hf.Revoker" attribute. +type RevocationRequestNet struct { + RevocationRequest +} + +// GetTCertBatchRequestNet is a network request for a batch of transaction certificates +type GetTCertBatchRequestNet struct { + GetTCertBatchRequest + // KeySigs is an optional array of public keys and corresponding signatures. + // If not set, the server generates it's own keys based on a key derivation function + // which cryptographically relates the TCert to an ECert. + KeySigs []KeySig `json:"key_sigs,omitempty"` +} + +// GetTCertBatchResponseNet is the network response for a batch of transaction certificates +type GetTCertBatchResponseNet struct { + tcert.GetBatchResponse +} + +// KeySig is a public key, signature, and signature algorithm tuple +type KeySig struct { + // Key is a public key + Key []byte `json:"key"` + // Sig is a signature over the PublicKey + Sig []byte `json:"sig"` + // Alg is the signature algorithm + Alg string `json:"alg"` +} diff --git a/cli/client/enroll.go b/cli/client/enroll.go index 5482a12dd..2311c10ce 100644 --- a/cli/client/enroll.go +++ b/cli/client/enroll.go @@ -22,7 +22,7 @@ import ( "github.com/cloudflare/cfssl/cli" "github.com/cloudflare/cfssl/log" - "github.com/hyperledger/fabric-cop/idp" + "github.com/hyperledger/fabric-cop/api" ) var enrollUsageText = `cop client enroll -- Enroll with COP server @@ -60,7 +60,7 @@ func enrollMain(args []string, c cli.Config) error { return err } - req := &idp.EnrollmentRequest{ + req := &api.EnrollmentRequest{ Name: id, Secret: secret, } diff --git a/cli/client/reenroll.go b/cli/client/reenroll.go index 25da7c7f3..4142d87f9 100644 --- a/cli/client/reenroll.go +++ b/cli/client/reenroll.go @@ -22,7 +22,7 @@ import ( "github.com/cloudflare/cfssl/cli" "github.com/cloudflare/cfssl/log" - "github.com/hyperledger/fabric-cop/idp" + "github.com/hyperledger/fabric-cop/api" ) var reenrollUsageText = `cop client reenroll -- Reenroll with COP server @@ -57,7 +57,7 @@ func reenrollMain(args []string, c cli.Config) error { return fmt.Errorf("Client is not yet enrolled: %s", err) } - req := &idp.ReenrollmentRequest{ID: id} + req := &api.ReenrollmentRequest{} if len(args) > 0 { path, _, err2 := cli.PopFirstArgument(args) @@ -70,7 +70,7 @@ func reenrollMain(args []string, c cli.Config) error { } } - newID, err := client.Reenroll(req) + newID, err := id.Reenroll(req) if err != nil { return fmt.Errorf("failed to store enrollment information: %s", err) } diff --git a/cli/client/register.go b/cli/client/register.go index 8c0780788..3b99e6287 100644 --- a/cli/client/register.go +++ b/cli/client/register.go @@ -22,9 +22,7 @@ import ( "fmt" "github.com/cloudflare/cfssl/cli" - cop "github.com/hyperledger/fabric-cop/api" - "github.com/hyperledger/fabric-cop/idp" - + "github.com/hyperledger/fabric-cop/api" "github.com/hyperledger/fabric-cop/util" ) @@ -54,7 +52,7 @@ func registerMain(args []string, c cli.Config) error { return err } - regReq := new(idp.RegistrationRequest) + regReq := new(api.RegistrationRequest) err = json.Unmarshal(buf, regReq) if err != nil { return err @@ -75,15 +73,14 @@ func registerMain(args []string, c cli.Config) error { return err } - regReq.Registrar = id - resp, err := client.Register(regReq) + resp, err := id.Register(regReq) if err != nil { return err } secretBytes, err := base64.StdEncoding.DecodeString(resp.Secret) if err != nil { - cop.WrapError(err, cop.EnrollingUserError, "Failed to decode string to bytes") + return fmt.Errorf("Failed decoding response: %s", err) } fmt.Printf("One time password: %s\n", string(secretBytes)) diff --git a/cli/client/revoke.go b/cli/client/revoke.go index 32c30df17..5c2183a3f 100644 --- a/cli/client/revoke.go +++ b/cli/client/revoke.go @@ -20,7 +20,7 @@ import ( "fmt" "github.com/cloudflare/cfssl/cli" - "github.com/hyperledger/fabric-cop/idp" + "github.com/hyperledger/fabric-cop/api" ) var revokeUsageTxt = `cop client revoke -- revokes one or more certificates @@ -69,7 +69,7 @@ func revokeMain(args []string, c cli.Config) error { } return id.Revoke( - &idp.RevocationRequest{ + &api.RevocationRequest{ Name: enrollmentID, Serial: c.Serial, AKI: c.AKI, diff --git a/cli/server/certdbaccessor.go b/cli/server/certdbaccessor.go index b906ea028..ec609be65 100644 --- a/cli/server/certdbaccessor.go +++ b/cli/server/certdbaccessor.go @@ -24,7 +24,6 @@ import ( "github.com/cloudflare/cfssl/certdb" certsql "github.com/cloudflare/cfssl/certdb/sql" "github.com/cloudflare/cfssl/log" - cop "github.com/hyperledger/fabric-cop/api" "github.com/hyperledger/fabric-cop/util" "github.com/kisielk/sqlstruct" @@ -106,20 +105,18 @@ func (d *CertDBAccessor) InsertCertificate(cr certdb.CertificateRecord) error { res, err := d.db.NamedExec(insertSQL, record) if err != nil { - log.Errorf("Error occured during insertion of record [error: %s]", err) - return err + return fmt.Errorf("Failed to insert record into database: %s", err) } numRowsAffected, err := res.RowsAffected() if numRowsAffected == 0 { - // log.Errorf("Failed to insert the certificate record") - return cop.NewError(cop.DatabaseError, "Failed to insert the certificate record") + return fmt.Errorf("Failed to insert the certificate record") } if numRowsAffected != 1 { - // log.Errorf("%d rows are affected, should be 1 row", numRowsAffected) - return cop.NewError(cop.DatabaseError, "%d rows are affected, should be 1 row", numRowsAffected) + return fmt.Errorf("Expected to affect 1 entry in certificate database but affected %d", + numRowsAffected) } return err diff --git a/cli/server/config.go b/cli/server/config.go index 880c3b6e7..666b1f6bc 100644 --- a/cli/server/config.go +++ b/cli/server/config.go @@ -25,8 +25,8 @@ import ( "github.com/cloudflare/cfssl/cli" "github.com/cloudflare/cfssl/log" + "github.com/hyperledger/fabric-cop/api" "github.com/hyperledger/fabric-cop/cli/server/ldap" - "github.com/hyperledger/fabric-cop/idp" "github.com/hyperledger/fabric-cop/lib/tls" "github.com/hyperledger/fabric-cop/util" @@ -66,7 +66,7 @@ type User struct { Pass string `json:"pass"` // enrollment secret Type string `json:"type"` Group string `json:"group"` - Attributes []idp.Attribute `json:"attrs,omitempty"` + Attributes []api.Attribute `json:"attrs,omitempty"` } // Constructor for COP config diff --git a/cli/server/dasqlite_test.go b/cli/server/dasqlite_test.go index fc0a7051b..d03160e4f 100644 --- a/cli/server/dasqlite_test.go +++ b/cli/server/dasqlite_test.go @@ -21,9 +21,9 @@ import ( "strings" "testing" + "github.com/hyperledger/fabric-cop/api" "github.com/hyperledger/fabric-cop/cli/server/dbutil" "github.com/hyperledger/fabric-cop/cli/server/spi" - "github.com/hyperledger/fabric-cop/idp" "github.com/jmoiron/sqlx" _ "github.com/mattn/go-sqlite3" ) @@ -110,7 +110,7 @@ func testInsertAndGetUser(ta TestAccessor, t *testing.T) { Name: "testId", Pass: "123456", Type: "client", - Attributes: []idp.Attribute{}, + Attributes: []api.Attribute{}, } err := ta.Accessor.InsertUser(insert) @@ -136,7 +136,7 @@ func testDeleteUser(ta TestAccessor, t *testing.T) { Name: "testId", Pass: "123456", Type: "client", - Attributes: []idp.Attribute{}, + Attributes: []api.Attribute{}, } err := ta.Accessor.InsertUser(insert) @@ -163,7 +163,7 @@ func testUpdateUser(ta TestAccessor, t *testing.T) { Name: "testId", Pass: "123456", Type: "client", - Attributes: []idp.Attribute{}, + Attributes: []api.Attribute{}, } err := ta.Accessor.InsertUser(insert) @@ -235,7 +235,7 @@ func testUpdateAndGetField(ta TestAccessor, t *testing.T) { Name: "testId", Pass: "123456", Type: "client", - Attributes: []idp.Attribute{}, + Attributes: []api.Attribute{}, } err := ta.Accessor.InsertUser(insert) diff --git a/cli/server/dbaccessor.go b/cli/server/dbaccessor.go index ca1574634..2c252f888 100644 --- a/cli/server/dbaccessor.go +++ b/cli/server/dbaccessor.go @@ -23,9 +23,8 @@ import ( "strings" "github.com/cloudflare/cfssl/log" - cop "github.com/hyperledger/fabric-cop/api" + "github.com/hyperledger/fabric-cop/api" "github.com/hyperledger/fabric-cop/cli/server/spi" - "github.com/hyperledger/fabric-cop/idp" "github.com/jmoiron/sqlx" "github.com/kisielk/sqlstruct" @@ -152,11 +151,11 @@ func (d *Accessor) InsertUser(user spi.UserInfo) error { } if numRowsAffected == 0 { - return cop.NewError(cop.UserStoreError, "Failed to insert the user record") + return fmt.Errorf("Failed to insert the user record") } if numRowsAffected != 1 { - return cop.NewError(cop.UserStoreError, "%d rows are affected, should be 1 row", numRowsAffected) + return fmt.Errorf("Expected one user record to be inserted, but %d records were inserted", numRowsAffected) } log.Debugf("User %s inserted into database successfully", user.Name) @@ -210,11 +209,11 @@ func (d *Accessor) UpdateUser(user spi.UserInfo) error { numRowsAffected, err := res.RowsAffected() if numRowsAffected == 0 { - return cop.NewError(cop.UserStoreError, "Failed to update the user record") + return fmt.Errorf("Failed to update the user record") } if numRowsAffected != 1 { - return cop.NewError(cop.UserStoreError, "%d rows are affected, should be 1 row", numRowsAffected) + return fmt.Errorf("Expected one user record to be updated, but %d records were updated", numRowsAffected) } return err @@ -244,7 +243,7 @@ func (d *Accessor) UpdateField(id string, field int, value interface{}) error { return err } default: - return cop.NewError(cop.DatabaseError, "DB: Specified field does not exist or cannot be updated") + return fmt.Errorf("Specified field does not exist and cannot be updated: %d", field) } return err @@ -267,8 +266,8 @@ func (d *Accessor) GetField(id string, field int) (interface{}, error) { } return groupRec.Prekey, nil default: - log.Error("DB: Specified field does not exist or cannot be retrieved") - return nil, cop.NewError(cop.DatabaseError, "DB: Specified field does not exist or cannot be retrieved") + err = fmt.Errorf("Specified field does not exist and cannot be updated: %d", field) + return nil, err } } @@ -368,7 +367,7 @@ func (d *Accessor) newDBUser(userRec *UserRecord) *DBUser { user.state = userRec.State user.maxEnrollments = userRec.MaxEnrollments user.affiliationPath = strings.Split(userRec.Group, "/") - var attrs []idp.Attribute + var attrs []api.Attribute json.Unmarshal([]byte(userRec.Attributes), &attrs) user.attrs = make(map[string]string) for _, attr := range attrs { @@ -400,8 +399,7 @@ func (u *DBUser) Login(pass string) error { // Check the password if u.pass != pass { - log.Errorf("Incorrect password for %s", u.name) - return cop.NewError(cop.AuthorizationFailure, "Incorrect username/password provided)") + return errors.New("Incorrect username/password provided") } // If the maxEnrollments is set (i.e. >= 0), make sure we haven't exceeded this number of logins. diff --git a/cli/server/dbutil/dbutil.go b/cli/server/dbutil/dbutil.go index 356892fe7..fa1a10695 100644 --- a/cli/server/dbutil/dbutil.go +++ b/cli/server/dbutil/dbutil.go @@ -26,7 +26,6 @@ import ( "github.com/cloudflare/cfssl/log" "github.com/go-sql-driver/mysql" - cop "github.com/hyperledger/fabric-cop/api" "github.com/hyperledger/fabric-cop/lib/tls" "github.com/jmoiron/sqlx" ) @@ -70,7 +69,7 @@ func createSQLiteDBTables(datasource string) error { log.Debug("Database location: ", datasource) db, err := sqlx.Open("sqlite3", datasource) if err != nil { - return cop.WrapError(err, cop.DatabaseError, "Failed to connect to database") + return fmt.Errorf("Failed to open database: %s", err) } log.Debug("Creating tables...") @@ -117,7 +116,7 @@ func NewUserRegistryPostgres(datasource string, clientTLSConfig *tls.ClientTLSCo db, err := sqlx.Open("postgres", connStr) if err != nil { - return nil, false, cop.WrapError(err, cop.DatabaseError, "Failed to open database") + return nil, false, fmt.Errorf("Failed to open database: %s", err) } err = db.Ping() @@ -129,9 +128,7 @@ func NewUserRegistryPostgres(datasource string, clientTLSConfig *tls.ClientTLSCo // Check if database exists r, err2 := db.Exec("SELECT * FROM pg_catalog.pg_database where datname=$1", dbName) if err2 != nil { - msg := "Failed to query 'pg_database' table" - log.Error(msg+" error: ", err2) - return nil, false, cop.WrapError(err, cop.DatabaseError, msg) + return nil, false, fmt.Errorf("Failed to query 'pg_database' table: %s", err2) } found, _ := r.RowsAffected() @@ -162,7 +159,7 @@ func createPostgresDBTables(datasource string, dbName string, db *sqlx.DB) error query := "CREATE DATABASE " + dbName _, err := db.Exec(query) if err != nil { - return cop.WrapError(err, cop.DatabaseError, "Failed to create Postgres database") + return fmt.Errorf("Failed to create Postgres database: %s", err) } database, err := sqlx.Open("postgres", datasource) @@ -209,7 +206,7 @@ func NewUserRegistryMySQL(datasource string, clientTLSConfig *tls.ClientTLSConfi log.Debug("Connection String: ", connStr) db, err := sqlx.Open("mysql", connStr) if err != nil { - return nil, false, cop.WrapError(err, cop.DatabaseError, "Failed to open database") + return nil, false, fmt.Errorf("Failed to open database: %s", err) } err = db.Ping() @@ -225,7 +222,7 @@ func NewUserRegistryMySQL(datasource string, clientTLSConfig *tls.ClientTLSConfi log.Debugf("Database (%s) does not exist", dbName) exists = false } else { - return nil, false, cop.WrapError(err, cop.DatabaseError, "Failed to query 'INFORMATION_SCHEMA.SCHEMATA table") + return nil, false, fmt.Errorf("Failed to query 'INFORMATION_SCHEMA.SCHEMATA table: %s", err) } } diff --git a/cli/server/enroll.go b/cli/server/enroll.go index 6c1ed64d0..f4a99f7fe 100644 --- a/cli/server/enroll.go +++ b/cli/server/enroll.go @@ -21,7 +21,7 @@ import ( "io/ioutil" "net/http" - "github.com/cloudflare/cfssl/api" + cfsslapi "github.com/cloudflare/cfssl/api" "github.com/cloudflare/cfssl/log" "github.com/cloudflare/cfssl/signer" "github.com/hyperledger/fabric-cop/util" @@ -46,7 +46,7 @@ type signHandler struct { // newEnrollHandler is the constructor for an enroll or reenroll handler func newSignHandler(endpoint string) (h http.Handler, err error) { // NewHandler is constructor for register handler - return &api.HTTPHandler{ + return &cfsslapi.HTTPHandler{ Handler: &signHandler{endpoint: endpoint}, Methods: []string{"POST"}, }, nil @@ -80,5 +80,5 @@ func (sh *signHandler) Handle(w http.ResponseWriter, r *http.Request) error { return err } - return api.SendResponse(w, cert) + return cfsslapi.SendResponse(w, cert) } diff --git a/cli/server/errors.go b/cli/server/errors.go index 130296882..00ddca4b5 100644 --- a/cli/server/errors.go +++ b/cli/server/errors.go @@ -21,10 +21,8 @@ import ( "errors" "net/http" - "github.com/cloudflare/cfssl/api" - cerr "github.com/cloudflare/cfssl/errors" + cfsslapi "github.com/cloudflare/cfssl/api" "github.com/cloudflare/cfssl/log" - cop "github.com/hyperledger/fabric-cop/api" ) var ( @@ -38,23 +36,23 @@ var ( ) func badRequest(w http.ResponseWriter, err error) error { - return httpError(w, 400, cop.IOError, err.Error()) + return httpError(w, 400, 10001, err.Error()) } func authErr(w http.ResponseWriter, err error) error { - return httpError(w, 401, cop.AuthorizationFailure, err.Error()) + return httpError(w, 401, 10002, err.Error()) } func notFound(w http.ResponseWriter, err error) error { - return httpError(w, 404, cerr.RecordNotFound, err.Error()) + return httpError(w, 404, 10003, err.Error()) } func dbErr(w http.ResponseWriter, err error) error { - return httpError(w, 500, cop.IOError, err.Error()) + return httpError(w, 500, 10004, err.Error()) } func httpError(w http.ResponseWriter, scode, code int, msg string) error { - response := api.NewErrorResponse(msg, code) + response := cfsslapi.NewErrorResponse(msg, code) jsonMessage, err := json.Marshal(response) if err != nil { log.Errorf("Failed to marshal JSON: %v", err) diff --git a/cli/server/register.go b/cli/server/register.go index 9e4ca9bb8..b0ff4b6dc 100644 --- a/cli/server/register.go +++ b/cli/server/register.go @@ -24,12 +24,11 @@ import ( "net/http" "strings" - "github.com/cloudflare/cfssl/api" + cfsslapi "github.com/cloudflare/cfssl/api" "github.com/cloudflare/cfssl/log" - cop "github.com/hyperledger/fabric-cop/api" + "github.com/hyperledger/fabric-cop/api" "github.com/hyperledger/fabric-cop/cli/server/spi" - "github.com/hyperledger/fabric-cop/idp" "github.com/hyperledger/fabric-cop/util" ) @@ -40,7 +39,7 @@ type registerHandler struct { // NewRegisterHandler is constructor for register handler func NewRegisterHandler() (h http.Handler, err error) { // NewHandler is constructor for register handler - return &api.HTTPHandler{ + return &cfsslapi.HTTPHandler{ Handler: ®isterHandler{}, Methods: []string{"POST"}, }, nil @@ -58,21 +57,21 @@ func (h *registerHandler) Handle(w http.ResponseWriter, r *http.Request) error { r.Body.Close() // Parse request body - var req cop.RegisterRequest + var req api.RegistrationRequestNet err = json.Unmarshal(body, &req) if err != nil { return err } // Register User - tok, err := reg.RegisterUser(req.User, req.Type, req.Group, req.Attributes, req.CallerID) + callerID := r.Header.Get(enrollmentIDHdrName) + tok, err := reg.RegisterUser(req.Name, req.Type, req.Group, req.Attributes, callerID) if err != nil { - log.Error("Error occured during register of user, error: ", err) return err } log.Debug("Registration completed - Sending response to clients") - return api.SendResponse(w, []byte(tok)) + return cfsslapi.SendResponse(w, []byte(tok)) } // Register for registering a user @@ -95,7 +94,7 @@ func NewRegisterUser() *Register { } // RegisterUser will register a user -func (r *Register) RegisterUser(id string, userType string, group string, attributes []idp.Attribute, registrar string, opt ...string) (string, error) { +func (r *Register) RegisterUser(id string, userType string, group string, attributes []api.Attribute, registrar string, opt ...string) (string, error) { log.Debugf("Received request to register user with id: %s, group: %s, attributes: %s, registrar: %s\n", id, group, attributes, registrar) @@ -124,7 +123,7 @@ func (r *Register) RegisterUser(id string, userType string, group string, attrib return tok, nil } -// func (r *Register) validateAndGenerateEnrollID(id, group string, attr []idp.Attribute) (string, error) { +// func (r *Register) validateAndGenerateEnrollID(id, group string, attr []api.Attribute) (string, error) { func (r *Register) validateID(id string, userType string, group string) error { log.Debug("Validate ID") // Check whether the group is required for the current user. @@ -147,7 +146,7 @@ func (r *Register) validateID(id string, userType string, group string) error { } // registerUserID registers a new user and its enrollmentID, role and state -func (r *Register) registerUserID(id string, userType string, group string, attributes []idp.Attribute, opt ...string) (string, error) { +func (r *Register) registerUserID(id string, userType string, group string, attributes []api.Attribute, opt ...string) (string, error) { log.Debugf("Registering user id: %s\n", id) var tok string @@ -167,8 +166,7 @@ func (r *Register) registerUserID(id string, userType string, group string, attr _, err := userRegistry.GetUser(id, nil) if err == nil { - log.Error("User is already registered") - return "", cop.NewError(cop.RegisteringUserError, "User is already registered") + return "", fmt.Errorf("User '%s' is already registered", id) } err = userRegistry.InsertUser(insert) @@ -224,7 +222,7 @@ func (r *Register) canRegister(registrar string, userType string) error { roles = make([]string, 0) } if !util.StrContained(userType, roles) { - return cop.NewError(cop.RegisteringUserError, "user %s may not register type %s", registrar, userType) + return fmt.Errorf("User '%s' may not register type '%s'", registrar, userType) } return nil diff --git a/cli/server/register_test.go b/cli/server/register_test.go index 15eb5035d..ed9729271 100644 --- a/cli/server/register_test.go +++ b/cli/server/register_test.go @@ -23,26 +23,25 @@ import ( "testing" "github.com/cloudflare/cfssl/cli" - cop "github.com/hyperledger/fabric-cop/api" - "github.com/hyperledger/fabric-cop/idp" + "github.com/hyperledger/fabric-cop/api" ) type Admin struct { - User string + Name string Pass []byte Type string Group string - Attributes []idp.Attribute + Attributes []api.Attribute } var ( - NotRegistrar = Admin{User: "testUser2", Pass: []byte("pass"), Type: "User", Group: "bank_b", Attributes: []idp.Attribute{idp.Attribute{Name: "role", Value: "client"}}} - Registrar = Admin{User: "admin", Pass: []byte("adminpw"), Type: "User", Group: "bank_a", Attributes: []idp.Attribute{idp.Attribute{Name: "hf.Registrar.DelegateRoles", Value: "client,user,auditor"}}} - testUser = cop.RegisterRequest{User: "testUser1", Type: "user", Group: "bank_a", Attributes: []idp.Attribute{idp.Attribute{Name: "test", Value: "testValue"}}} - testAuditor = cop.RegisterRequest{User: "testAuditor", Type: "Auditor", Attributes: []idp.Attribute{idp.Attribute{Name: "role", Value: "auditor"}}} - testClient1 = cop.RegisterRequest{User: "testClient1", Type: "Client", Group: "bank_a", Attributes: []idp.Attribute{idp.Attribute{Name: "test", Value: "testValue"}}} - testBogus = cop.RegisterRequest{User: "testBogus", Type: "Bogus", Group: "bank_b", Attributes: []idp.Attribute{idp.Attribute{Name: "test", Value: "testValue"}}} - testEnroll = cop.RegisterRequest{User: "testEnroll", Type: "User", Group: "bank_a", Attributes: []idp.Attribute{idp.Attribute{Name: "test", Value: "testValue"}}} + NotRegistrar = Admin{Name: "testUser2", Pass: []byte("pass"), Type: "User", Group: "bank_b", Attributes: []api.Attribute{api.Attribute{Name: "role", Value: "client"}}} + Registrar = Admin{Name: "admin", Pass: []byte("adminpw"), Type: "User", Group: "bank_a", Attributes: []api.Attribute{api.Attribute{Name: "hf.Registrar.DelegateRoles", Value: "client,user,auditor"}}} + testUser = api.RegistrationRequest{Name: "testUser1", Type: "user", Group: "bank_a", Attributes: []api.Attribute{api.Attribute{Name: "test", Value: "testValue"}}} + testAuditor = api.RegistrationRequest{Name: "testAuditor", Type: "Auditor", Attributes: []api.Attribute{api.Attribute{Name: "role", Value: "auditor"}}} + testClient1 = api.RegistrationRequest{Name: "testClient1", Type: "Client", Group: "bank_a", Attributes: []api.Attribute{api.Attribute{Name: "test", Value: "testValue"}}} + testBogus = api.RegistrationRequest{Name: "testBogus", Type: "Bogus", Group: "bank_b", Attributes: []api.Attribute{api.Attribute{Name: "test", Value: "testValue"}}} + testEnroll = api.RegistrationRequest{Name: "testEnroll", Type: "User", Group: "bank_a", Attributes: []api.Attribute{api.Attribute{Name: "test", Value: "testValue"}}} ) const ( @@ -82,18 +81,17 @@ func bootstrapUsers() error { if r == nil { return errors.New("Failed to get register object") } - r.RegisterUser(Registrar.User, Registrar.Type, Registrar.Group, Registrar.Attributes, "", string(Registrar.Pass)) + r.RegisterUser(Registrar.Name, Registrar.Type, Registrar.Group, Registrar.Attributes, "", string(Registrar.Pass)) - r.RegisterUser(NotRegistrar.User, NotRegistrar.Type, NotRegistrar.Group, NotRegistrar.Attributes, "", string(NotRegistrar.Pass)) + r.RegisterUser(NotRegistrar.Name, NotRegistrar.Type, NotRegistrar.Group, NotRegistrar.Attributes, "", string(NotRegistrar.Pass)) return nil } -func registerUser(registrar Admin, user *cop.RegisterRequest) (string, error) { +func registerUser(registrar Admin, user *api.RegistrationRequest) (string, error) { r := NewRegisterUser() - user.CallerID = registrar.User - tok, err := r.RegisterUser(user.User, user.Type, user.Group, user.Attributes, user.CallerID) + tok, err := r.RegisterUser(user.Name, user.Type, user.Group, user.Attributes, registrar.Name) if err != nil { return "", err } diff --git a/cli/server/revoke.go b/cli/server/revoke.go index 02707a211..656fd03f9 100644 --- a/cli/server/revoke.go +++ b/cli/server/revoke.go @@ -23,17 +23,17 @@ import ( "io/ioutil" "net/http" - "github.com/cloudflare/cfssl/api" + cfsslapi "github.com/cloudflare/cfssl/api" "github.com/cloudflare/cfssl/log" - "github.com/hyperledger/fabric-cop/idp" + "github.com/hyperledger/fabric-cop/api" "github.com/hyperledger/fabric-cop/util" ) // NewRevokeHandler is constructor for revoke handler func NewRevokeHandler() (h http.Handler, err error) { // NewHandler is constructor for register handler - return &api.HTTPHandler{ + return &cfsslapi.HTTPHandler{ Handler: &revokeHandler{}, Methods: []string{"POST"}}, nil } @@ -73,7 +73,7 @@ func (h *revokeHandler) Handle(w http.ResponseWriter, r *http.Request) error { } // Parse revoke request body - var req idp.RevocationRequest + var req api.RevocationRequestNet err = json.Unmarshal(body, &req) if err != nil { return badRequest(w, err) @@ -112,5 +112,5 @@ func (h *revokeHandler) Handle(w http.ResponseWriter, r *http.Request) error { log.Debug("Revoke was successful: %+v", req) result := map[string]string{} - return api.SendResponse(w, result) + return cfsslapi.SendResponse(w, result) } diff --git a/cli/server/server_test.go b/cli/server/server_test.go index 3209543f8..949934e47 100644 --- a/cli/server/server_test.go +++ b/cli/server/server_test.go @@ -25,9 +25,9 @@ import ( "testing" "time" + "github.com/hyperledger/fabric-cop/api" "github.com/hyperledger/fabric-cop/cli/server/dbutil" "github.com/hyperledger/fabric-cop/cli/server/ldap" - "github.com/hyperledger/fabric-cop/idp" "github.com/hyperledger/fabric-cop/lib" ) @@ -91,7 +91,7 @@ func TestRegisterUser(t *testing.T) { return } - enrollReq := &idp.EnrollmentRequest{ + enrollReq := &api.EnrollmentRequest{ Name: "admin", Secret: "adminpw", } @@ -108,15 +108,13 @@ func TestRegisterUser(t *testing.T) { return } - regReq := &idp.RegistrationRequest{ + regReq := &api.RegistrationRequest{ Name: "TestUser1", Type: "Client", Group: "bank_a", } - regReq.Registrar = id - - _, err = c.Register(regReq) + _, err = id.Register(regReq) if err != nil { t.Error(err) } @@ -147,7 +145,7 @@ func TestEnrollUser(t *testing.T) { return } - req := &idp.EnrollmentRequest{ + req := &api.EnrollmentRequest{ Name: "testUser", Secret: "user1", } @@ -158,11 +156,9 @@ func TestEnrollUser(t *testing.T) { return } - reenrollReq := &idp.ReenrollmentRequest{ - ID: id, - } + reenrollReq := &api.ReenrollmentRequest{} - _, err = c.Reenroll(reenrollReq) + _, err = id.Reenroll(reenrollReq) if err != nil { t.Error("reenroll of user 'testUser' failed") return @@ -181,7 +177,7 @@ func TestRevoke(t *testing.T) { return } - req := &idp.EnrollmentRequest{ + req := &api.EnrollmentRequest{ Name: "admin2", Secret: "adminpw2", } @@ -192,17 +188,17 @@ func TestRevoke(t *testing.T) { return } - err = id.Revoke(&idp.RevocationRequest{}) + err = id.Revoke(&api.RevocationRequest{}) if err == nil { t.Error("Revoke with no args should have failed but did not") } - err = id.Revoke(&idp.RevocationRequest{Serial: "foo", AKI: "bar"}) + err = id.Revoke(&api.RevocationRequest{Serial: "foo", AKI: "bar"}) if err == nil { t.Error("Revoke with bogus serial and AKI should have failed but did not") } - err = id.Revoke(&idp.RevocationRequest{Name: "foo"}) + err = id.Revoke(&api.RevocationRequest{Name: "foo"}) if err == nil { t.Error("Revoke with bogus name should have failed but did not") } @@ -228,9 +224,7 @@ func TestGetTCerts(t *testing.T) { return } // Getting TCerts - _, err = id.GetPrivateSigners(&idp.GetPrivateSignersRequest{ - Count: 1, - }) + _, err = id.GetTCertBatch(&api.GetTCertBatchRequest{Count: 1}) if err != nil { t.Errorf("GetPrivateSigners failed: %s", err) } @@ -250,14 +244,13 @@ func TestMaxEnrollment(t *testing.T) { CFG.UsrReg.MaxEnrollments = 2 - regReq := &idp.RegistrationRequest{ - Name: "MaxTestUser", - Type: "Client", - Group: "bank_a", - Registrar: id, + regReq := &api.RegistrationRequest{ + Name: "MaxTestUser", + Type: "Client", + Group: "bank_a", } - resp, err := c.Register(regReq) + resp, err := id.Register(regReq) if err != nil { t.Error(err) } @@ -267,20 +260,21 @@ func TestMaxEnrollment(t *testing.T) { t.Fatalf("Failed decoding secret: %s", err) } - enrollReq := &idp.EnrollmentRequest{ + secret := string(secretBytes) + enrollReq := &api.EnrollmentRequest{ Name: "MaxTestUser", - Secret: string(secretBytes), + Secret: secret, } _, err = c.Enroll(enrollReq) if err != nil { - t.Error("Enroll of user 'MaxTestUser' failed") + t.Errorf("Enroll of user 'MaxTestUser' failed with secret '%s'", secret) return } _, err = c.Enroll(enrollReq) if err != nil { - t.Error("Enroll of user 'MaxTestUser' failed") + t.Error("Reenroll of user 'MaxTestUser' failed") return } @@ -302,7 +296,7 @@ func testUnregisteredUser(t *testing.T) { copServer := `{"serverURL":"https://localhost:8888"}` c, _ := lib.NewClient(copServer) - req := &idp.EnrollmentRequest{ + req := &api.EnrollmentRequest{ Name: "Unregistered", Secret: "test", } @@ -318,7 +312,7 @@ func testIncorrectToken(t *testing.T) { copServer := `{"serverURL":"https://localhost:8888"}` c, _ := lib.NewClient(copServer) - req := &idp.EnrollmentRequest{ + req := &api.EnrollmentRequest{ Name: "notadmin", Secret: "pass1", } @@ -334,7 +328,7 @@ func testEnrollingUser(t *testing.T) { copServer := `{"serverURL":"https://localhost:8888"}` c, _ := lib.NewClient(copServer) - req := &idp.EnrollmentRequest{ + req := &api.EnrollmentRequest{ Name: "testUser2", Secret: "user2", } @@ -386,7 +380,7 @@ func TestExpiration(t *testing.T) { // Enroll this user using the "expiry" profile which is configured // to expire after 1 second - regReq := &idp.EnrollmentRequest{ + regReq := &api.EnrollmentRequest{ Name: "expiryUser", Secret: "expirypw", Profile: "expiry", diff --git a/cli/server/spi/userregistry.go b/cli/server/spi/userregistry.go index 319c89e5e..06776336b 100644 --- a/cli/server/spi/userregistry.go +++ b/cli/server/spi/userregistry.go @@ -21,7 +21,7 @@ limitations under the License. package spi -import "github.com/hyperledger/fabric-cop/idp" +import "github.com/hyperledger/fabric-cop/api" // UserInfo contains information about a user type UserInfo struct { @@ -29,7 +29,7 @@ type UserInfo struct { Pass string Type string Group string - Attributes []idp.Attribute + Attributes []api.Attribute } // GroupInfo defines a group name and its parent diff --git a/cli/server/tcert.go b/cli/server/tcert.go index 13e09b229..6c439d646 100644 --- a/cli/server/tcert.go +++ b/cli/server/tcert.go @@ -22,10 +22,10 @@ import ( "io/ioutil" "net/http" - "github.com/cloudflare/cfssl/api" + cfsslapi "github.com/cloudflare/cfssl/api" cerr "github.com/cloudflare/cfssl/errors" "github.com/cloudflare/cfssl/log" - "github.com/hyperledger/fabric-cop/idp" + "github.com/hyperledger/fabric-cop/api" "github.com/hyperledger/fabric-cop/lib/tcert" "github.com/hyperledger/fabric-cop/util" ) @@ -61,7 +61,7 @@ func initTCertHandler() (h http.Handler, err error) { return nil, err } keyTree := tcert.NewKeyTree(csp, rootKey) - handler := &api.HTTPHandler{ + handler := &cfsslapi.HTTPHandler{ Handler: &tcertHandler{mgr: mgr, keyTree: keyTree}, Methods: []string{"POST"}, } @@ -84,7 +84,7 @@ func (h *tcertHandler) handle(w http.ResponseWriter, r *http.Request) error { if err != nil { return fmt.Errorf("Failure reading request body: %s", err) } - req := &idp.GetPrivateSignersRequest{} + req := &api.GetTCertBatchRequestNet{} err = util.Unmarshal(body, req, "tcert request") if err != nil { return err @@ -127,7 +127,7 @@ func (h *tcertHandler) handle(w http.ResponseWriter, r *http.Request) error { } // Write the response - api.SendResponse(w, resp) + cfsslapi.SendResponse(w, resp) // Success return nil diff --git a/idp/idp.go b/idp/idp.go deleted file mode 100644 index 11b8f2d64..000000000 --- a/idp/idp.go +++ /dev/null @@ -1,255 +0,0 @@ -/* -Copyright IBM Corp. 2016 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 idp contains Identity Provider APIs as used by Hyperledger Fabric -package idp - -import ( - "time" - - "github.com/cloudflare/cfssl/csr" -) - -/* - * The identity provider APIs are split into 3 main interfaces: - * 1) ClientAPI - the interface used by a client SDK; - * 2) PeerAPI - the interface used by a peer; - * 3) ChaincodeAPI - the interface used by a chaincode. - */ - -// ClientAPI is the API used by the client SDK to interface with the IDP -type ClientAPI interface { - - // Capabilities returns the capabilities of this IDP client - Capabilities() []Capability - - // Register a new identity - // @param req The registration request - Register(req *RegistrationRequest) (*RegistrationResponse, error) - - // Enroll a new identity - // @param req The enrollment request - Enroll(req *EnrollmentRequest) (Identity, error) - - // RegisterAndEnroll registers and enrolls a new identity - // @param req The registration request - RegisterAndEnroll(req *RegistrationRequest) (Identity, error) - - // ImportSigner imports a signer from an external CA - // @param req The import request - ImportSigner(req *ImportSignerRequest) (Signer, error) -} - -// PeerAPI is the API used by the peer pertaining to the IDP -type PeerAPI interface { - - // A peer can sign and verify - Signer -} - -// ChaincodeAPI is the API used by the chaincode pertaining to the IDP -type ChaincodeAPI interface { - - // TODO: For ZRL to define - -} - -// Identity represents an arbitrary identity -type Identity interface { - - // Name returns the identity name - GetName() string - - // GetPublicSigner returns the public signer for this identity - GetPublicSigner() TemporalSigner - - // GetPrivateSigners returns private signers for this identity - GetPrivateSigners(req *GetPrivateSignersRequest) ([]TemporalSigner, error) - - // GetAttributeNames returns the names of all attributes associated with this identity - GetAttributeNames() ([]string, error) - - // Delete this identity completely and revoke all of it's signers - Delete() error -} - -// TemporalSigner is a signer which can be renewed and revoked -type TemporalSigner interface { - - // Extends Signer - Signer - - // Renew this identity - Renew() error - - // Revoke this identity - Revoke() error -} - -// Signer interface -type Signer interface { - - // Extends Verifier - Verifier - - // Sign the message - Sign(msg []byte) ([]byte, error) - - // SignOpts the message with options - SignOpts(msg []byte, opts SignatureOpts) ([]byte, error) - - // NewAttributeProof creates a proof for an attribute - NewAttributeProof(spec *AttributeProofSpec) (proof []byte, err error) -} - -// Verifier interface -type Verifier interface { - - // Verify myself - VerifySelf() error - - // Verify a message given a signature over the message - Verify(msg []byte, sig []byte) error - - // Verify a signature over some message with specific options - VerifyOpts(msg []byte, sig []byte, opts SignatureOpts) error - - // VerifyAttributes verifies attributes given proofs - VerifyAttributes(proof [][]byte, spec *AttributeProofSpec) error -} - -// RegistrationRequest for a new identity -type RegistrationRequest struct { - // Name is the unique name of the identity - Name string `json:"id"` - // Type of identity being registered (e.g. "peer, app, user") - Type string `json:"type"` - // Group name associated with the identity - Group string `json:"group"` - // Attributes associated with this identity - Attributes []Attribute `json:"attrs,omitempty"` - // Registrar is the identity that is performing the registration - Registrar Identity `json:"registrar"` -} - -// RevocationRequest is a revocation request for a single certificate or all certificates -// associated with an identity. -// associated with an identity -type RevocationRequest struct { - // Name of the identity whose certificates should be revoked - // If this field is omitted, then Serial must be specified - Name string `json:"id,omitempty"` - // Serial number of the certificate to be revoked - // If this is omitted, then Name must be specified - Serial string `json:"serial,omitempty"` - // AKI (Authority Key Identifier) of the certificate to be revoked - AKI string `json:"aki,omitempty"` - // 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 `json:"reason,omitempty"` -} - -// RegistrationResponse is a registration response -type RegistrationResponse struct { - // The optional secret returned from a registration response - Secret string `json:"credential,omitempty"` -} - -// EnrollmentRequest is a request to enroll an identity -type EnrollmentRequest struct { - // The identity name to enroll - Name string `json:"name"` - // The secret returned via Register - Secret string `json:"secret,omitempty"` - // Hosts is a comma-separated host list in the CSR - Hosts string `json:"hosts,omitempty"` - // Profile is the name of the signing profile to use in issuing the certificate - Profile string `json:"profile,omitempty"` - // Label is the label to use in HSM operations - Label string `json:"label,omitempty"` - // CSR is Certificate Signing Request info - CSR *CSRInfo `json:"csr,omitempty"` -} - -// ReenrollmentRequest is a request to enroll an identity -type ReenrollmentRequest struct { - // Identity is the identity being reenrolled - ID Identity `json:"id"` - // Hosts is a comma-separated host list in the CSR - Hosts string `json:"hosts,omitempty"` - // Profile is the name of the signing profile to use in issuing the certificate - Profile string `json:"profile,omitempty"` - // Label is the label to use in HSM operations - Label string `json:"label,omitempty"` - // CSR is Certificate Signing Request info - CSR *CSRInfo `json:"csr,omitempty"` -} - -// CSRInfo is Certificate Signing Request information -type CSRInfo struct { - CN string `json:"cn"` - Names []csr.Name `json:"names,omitempty"` - Hosts []string `json:"hosts,omitempty"` - KeyRequest *csr.BasicKeyRequest `json:"key,omitempty"` - CA *csr.CAConfig `json:"ca,omitempty"` - SerialNumber string `json:"serial,omitempty"` -} - -// ImportSignerRequest is required when importing a signer from an external CA -type ImportSignerRequest struct { - // The certificate to import - Cert []byte `json:"cert"` - // The private key to import (optional) - Key []byte `json:"key,omitempty"` -} - -// GetPrivateSignersRequest is input provided to get private signers -type GetPrivateSignersRequest struct { - Count uint `json:"count"` - AttrNames []string `json:"attr_names,omitempty"` - EncryptAttrs bool `json:"encrypt_attrs,omitempty"` - ValidityPeriod time.Duration `json:"validity_period,omitempty"` -} - -// SignatureOpts are signature options -type SignatureOpts interface { - Policy() []string - Label() string -} - -// Attribute is an arbitrary name/value pair -type Attribute struct { - Name string `json:"name"` - Value string `json:"value"` -} - -// AttributeProofSpec is an attribute proof specification -type AttributeProofSpec struct { - Attributes []string - Message []byte -} - -// Capability is a capability of a CA -type Capability int - -// The capabilities of a CA relative to the CA API -const ( - REGISTRATION Capability = iota // CA has registrar capability - ENROLLMENT // CA has enrollment capability - ATTRIBUTES // CA has attributes capability - ANONYMITY // CA support anonymous identities - UNLINKABILITY // CA support unlinkable identities -) diff --git a/lib/client.go b/lib/client.go index 8888a321a..d38b16816 100644 --- a/lib/client.go +++ b/lib/client.go @@ -20,7 +20,6 @@ import ( "bytes" "encoding/base64" "encoding/json" - "errors" "fmt" "io/ioutil" "net" @@ -32,12 +31,11 @@ import ( "strconv" "strings" - "github.com/cloudflare/cfssl/api" + cfsslapi "github.com/cloudflare/cfssl/api" "github.com/cloudflare/cfssl/csr" "github.com/cloudflare/cfssl/log" "github.com/cloudflare/cfssl/signer" - cop "github.com/hyperledger/fabric-cop/api" - "github.com/hyperledger/fabric-cop/idp" + "github.com/hyperledger/fabric-cop/api" "github.com/hyperledger/fabric-cop/lib/tls" "github.com/hyperledger/fabric-cop/util" ) @@ -72,62 +70,9 @@ type Client struct { HomeDir string `json:"homeDir,omitempty"` } -// Capabilities returns the capabilities COP -func (c *Client) Capabilities() []idp.Capability { - return []idp.Capability{ - idp.REGISTRATION, - idp.ENROLLMENT, - idp.ATTRIBUTES, - idp.ANONYMITY, - idp.UNLINKABILITY, - } -} - -// Register registers a new identity -// @param req The registration request -func (c *Client) Register(req *idp.RegistrationRequest) (*idp.RegistrationResponse, error) { - log.Debugf("Register %+v", req) - // Send a post to the "register" endpoint with req as body - - if req.Name == "" { - return nil, errors.New("Register was called without a Name set") - } - if req.Group == "" { - return nil, errors.New("Register was called without a Group set") - } - if req.Registrar == nil { - return nil, errors.New("Register was called without a Registrar identity set") - } - - var request cop.RegisterRequest - request.User = req.Name - request.Type = req.Type - request.Group = req.Group - request.Attributes = req.Attributes - request.CallerID = req.Registrar.GetName() - - reqBody, err := util.Marshal(request, "RegistrationRequest") - if err != nil { - return nil, err - } - - buf, err2 := req.Registrar.(*Identity).Post("register", reqBody) - if err2 != nil { - return nil, err2 - } - - var response api.Response - json.Unmarshal(buf, &response) - resp := new(idp.RegistrationResponse) - resp.Secret = response.Result.(string) - - log.Debug("The register request completely successfully") - return resp, nil -} - // Enroll enrolls a new identity // @param req The enrollment request -func (c *Client) Enroll(req *idp.EnrollmentRequest) (*Identity, error) { +func (c *Client) Enroll(req *api.EnrollmentRequest) (*Identity, error) { log.Debugf("Enrolling %+v", req) // Generate the CSR @@ -155,82 +100,30 @@ func (c *Client) Enroll(req *idp.EnrollmentRequest) (*Identity, error) { return nil, err } post.SetBasicAuth(req.Name, req.Secret) - cert, err := c.SendPost(post) + result, err := c.SendPost(post) if err != nil { return nil, err } // Create an identity from the key and certificate in the response - return c.newIdentityFromResponse(req.Name, cert, key) -} - -// Reenroll reenrolls an existing Identity and returns a new Identity -// @param req The reenrollment request -func (c *Client) Reenroll(req *idp.ReenrollmentRequest) (*Identity, error) { - log.Debugf("Reenrolling %+v", req) - - if req.ID == nil { - return nil, errors.New("ReenrollmentRequest.ID was not set") - } - - id := req.ID.(*Identity) - - csrPEM, key, err := c.GenCSR(req.CSR, id.GetName()) - if err != nil { - return nil, err - } - - // Get the body of the request - sreq := signer.SignRequest{ - Hosts: signer.SplitHosts(req.Hosts), - Request: string(csrPEM), - Profile: req.Profile, - Label: req.Label, - } - body, err := util.Marshal(sreq, "SignRequest") - if err != nil { - return nil, err - } - - cert, err := id.Post("reenroll", body) - if err != nil { - return nil, err - } - - return c.newIdentityFromResponse(id.GetName(), cert, key) + return c.newIdentityFromResponse(result, req.Name, key) } // newIdentityFromResponse returns an Identity for enroll and reenroll responses +// @param result The result from server // @param id Name of identity being enrolled or reenrolled -// @param cert The certificate which was issued // @param key The private key which was used to sign the request -func (c *Client) newIdentityFromResponse(id string, cert, key []byte) (*Identity, error) { +func (c *Client) newIdentityFromResponse(result interface{}, id string, key []byte) (*Identity, error) { log.Debugf("newIdentityFromResponse %s", id) - - var resp api.Response - err := json.Unmarshal(cert, &resp) + certByte, err := base64.StdEncoding.DecodeString(result.(string)) if err != nil { - return nil, err - } - - certByte, _ := base64.StdEncoding.DecodeString(resp.Result.(string)) - - if resp.Result != nil && resp.Success == true { - log.Debugf("newIdentityFromResponse success for %s", id) - return newIdentity(c, id, key, certByte), nil + return nil, fmt.Errorf("Invalid response format from server: %s", err) } - - return nil, cop.NewError(cop.EnrollingUserError, "Failed to reenroll user") -} - -// RegisterAndEnroll registers and enrolls a new identity -// @param req The registration request -func (c *Client) RegisterAndEnroll(req *idp.RegistrationRequest) (*Identity, error) { - return nil, errors.New("NotImplemented") + return newIdentity(c, id, key, certByte), nil } // GenCSR generates a CSR (Certificate Signing Request) -func (c *Client) GenCSR(req *idp.CSRInfo, id string) ([]byte, []byte, error) { +func (c *Client) GenCSR(req *api.CSRInfo, id string) ([]byte, []byte, error) { log.Debugf("GenCSR %+v", req) cr := c.newCertificateRequest(req) @@ -247,7 +140,7 @@ func (c *Client) GenCSR(req *idp.CSRInfo, id string) ([]byte, []byte, error) { // newCertificateRequest creates a certificate request which is used to generate // a CSR (Certificate Signing Request) -func (c *Client) newCertificateRequest(req *idp.CSRInfo) *csr.CertificateRequest { +func (c *Client) newCertificateRequest(req *api.CSRInfo) *csr.CertificateRequest { cr := csr.CertificateRequest{} if req != nil && req.Names != nil { cr.Names = req.Names @@ -272,12 +165,6 @@ func (c *Client) newCertificateRequest(req *idp.CSRInfo) *csr.CertificateRequest return &cr } -// ImportSigner imports a signer from an external CA -// @param req The import request -func (c *Client) ImportSigner(req *idp.ImportSignerRequest) (idp.Signer, error) { - return nil, errors.New("NotImplemented") -} - // LoadMyIdentity loads the client's identity from disk func (c *Client) LoadMyIdentity() (*Identity, error) { return c.LoadIdentity(c.GetMyKeyFile(), c.GetMyCertFile()) @@ -343,12 +230,12 @@ func (c *Client) NewIdentity(key, cert []byte) (*Identity, error) { // LoadCSRInfo reads CSR (Certificate Signing Request) from a file // @parameter path The path to the file contains CSR info in JSON format -func (c *Client) LoadCSRInfo(path string) (*idp.CSRInfo, error) { +func (c *Client) LoadCSRInfo(path string) (*api.CSRInfo, error) { csrJSON, err := ioutil.ReadFile(path) if err != nil { return nil, err } - var csrInfo idp.CSRInfo + var csrInfo api.CSRInfo err = util.Unmarshal(csrJSON, &csrInfo, "LoadCSRInfo") if err != nil { return nil, err @@ -364,32 +251,31 @@ func (c *Client) NewPost(endpoint string, reqBody []byte) (*http.Request, error) } req, err := http.NewRequest("POST", curl, bytes.NewReader(reqBody)) if err != nil { - msg := fmt.Sprintf("failed to create new request to %s: %v", curl, err) - log.Debug(msg) - return nil, cop.NewError(cop.CFSSL, msg) + return nil, fmt.Errorf("Failed posting to %s: %s", curl, err) } return req, nil } // SendPost sends a request to the LDAP server and returns a response -func (c *Client) SendPost(req *http.Request) (respBody []byte, err error) { - log.Debugf("Sending request\n%s", util.HTTPRequestToString(req)) +func (c *Client) SendPost(req *http.Request) (interface{}, error) { + reqStr := util.HTTPRequestToString(req) + log.Debugf("Sending request\n%s", reqStr) configFile, err := c.getClientConfig(c.HomeDir) if err != nil { - log.Errorf("Failed to load client configuration file [error: %s]", err) + return nil, fmt.Errorf("Failed to load client config file [%s]; not sending\n%s", err, reqStr) } var cfg = new(tls.ClientTLSConfig) err = json.Unmarshal(configFile, cfg) if err != nil { - log.Errorf("Error: %s", err) + return nil, fmt.Errorf("Failed to parse client config file [%s]; not sending\n%s", err, reqStr) } tlsConfig, err := tls.GetClientTLSConfig(cfg) if err != nil { - log.Errorf("Failed to get client TLS configuration [error: %s]", err) + return nil, fmt.Errorf("Failed to get client TLS config [%s]; not sending\n%s", err, reqStr) } tr := &http.Transport{ @@ -397,44 +283,48 @@ func (c *Client) SendPost(req *http.Request) (respBody []byte, err error) { } httpClient := &http.Client{Transport: tr} - resp, err := httpClient.Do(req) if err != nil { - msg := fmt.Sprintf("POST failed: %v", err) - log.Debug(msg) - return nil, errors.New(msg) + return nil, fmt.Errorf("POST failure [%s]; not sending\n%s", err, reqStr) } + var respBody []byte if resp.Body != nil { respBody, err = ioutil.ReadAll(resp.Body) defer resp.Body.Close() if err != nil { - msg := fmt.Sprintf("failed to read response: %v", err) - log.Debug(msg) - return nil, errors.New(msg) + return nil, fmt.Errorf("Failed to read response [%s] of request:\n%s", err, reqStr) } log.Debugf("Received response\n%s", util.HTTPResponseToString(resp)) } - if resp.StatusCode >= 400 { - var msg string - body := new(api.Response) + var body *cfsslapi.Response + if respBody != nil && len(respBody) > 0 { + body = new(cfsslapi.Response) err = json.Unmarshal(respBody, body) - if err != nil && len(body.Errors) > 0 { - msg = body.Errors[0].Message - } else { - msg = fmt.Sprintf("HTTP status code=%d; %s", resp.StatusCode, string(respBody)) + if err != nil { + return nil, fmt.Errorf("Failed to parse response [%s] for request:\n%s", err, reqStr) + } + if len(body.Errors) > 0 { + msg := body.Errors[0].Message + return nil, fmt.Errorf("Error response from server was '%s' for request:\n%s", msg, reqStr) } - msg = fmt.Sprintf("Error response from COP server; %s", msg) - log.Debugf("%s", msg) - return nil, errors.New(msg) } - return respBody, nil + scode := resp.StatusCode + if scode >= 400 { + return nil, fmt.Errorf("Failed with server status code %d for request:\n%s", scode, reqStr) + } + if body == nil { + return nil, nil + } + if !body.Success { + return nil, fmt.Errorf("Server returned failure for request:\n%s", reqStr) + } + return body.Result, nil } -func (c *Client) getURL(endpoint string) (string, cop.Error) { +func (c *Client) getURL(endpoint string) (string, error) { nurl, err := normalizeURL(c.ServerURL) if err != nil { - log.Debugf("error getting server URL: %s", err) - return "", cop.WrapError(err, cop.CFSSL, "error getting URL for %s", endpoint) + return "", err } rtn := fmt.Sprintf("%s/api/v1/cfssl/%s", nurl, endpoint) return rtn, nil diff --git a/lib/client_test.go b/lib/client_test.go index ead2506d9..bc117a997 100644 --- a/lib/client_test.go +++ b/lib/client_test.go @@ -27,8 +27,8 @@ import ( "time" "github.com/cloudflare/cfssl/log" + "github.com/hyperledger/fabric-cop/api" "github.com/hyperledger/fabric-cop/cli/server" - "github.com/hyperledger/fabric-cop/idp" ) const ( @@ -48,23 +48,23 @@ func TestAllClient(t *testing.T) { c := getClient() testRegister(c, t) - testRegisterWithoutRegistrar(c, t) testEnrollIncorrectPassword(c, t) testEnroll(c, t) testDoubleEnroll(c, t) testReenroll(c, t) - testRevocation(c, t, "revoker", "revokerpw", true) - testRevocation(c, t, "notadmin", "pass", false) + testRevocation(c, t, "revoker", "revokerpw", true, true) + testRevocation(c, t, "nonrevoker", "nonrevokerpw", true, false) + testRevocation(c, t, "revoker2", "revokerpw2", false, true) + testRevocation(c, t, "nonrevoker2", "nonrevokerpw2", false, false) testLoadCSRInfo(c, t) testLoadNoCSRInfo(c, t) testLoadBadCSRInfo(c, t) - testCapabilities(c, t) } func testRegister(c *Client, t *testing.T) { // Enroll admin - enrollReq := &idp.EnrollmentRequest{ + enrollReq := &api.EnrollmentRequest{ Name: "admin", Secret: "adminpw", } @@ -75,35 +75,21 @@ func testRegister(c *Client, t *testing.T) { } // Register as admin - registerReq := &idp.RegistrationRequest{ - Name: "TestUser", - Type: "Client", - Group: "bank_a", - Registrar: id, + registerReq := &api.RegistrationRequest{ + Name: "TestUser", + Type: "Client", + Group: "bank_a", } - _, err = c.Register(registerReq) + _, err = id.Register(registerReq) if err != nil { t.Errorf("Register failed: %s", err) } } -func testRegisterWithoutRegistrar(c *Client, t *testing.T) { - - req := &idp.RegistrationRequest{ - Name: "TestUser", - Type: "Client", - } - - _, err := c.Register(req) - if err == nil { - t.Error("Register should have failed during registration without registrar") - } -} - func testEnrollIncorrectPassword(c *Client, t *testing.T) { - req := &idp.EnrollmentRequest{ + req := &api.EnrollmentRequest{ Name: "testUser", Secret: "incorrect", } @@ -116,7 +102,7 @@ func testEnrollIncorrectPassword(c *Client, t *testing.T) { func testEnroll(c *Client, t *testing.T) { - req := &idp.EnrollmentRequest{ + req := &api.EnrollmentRequest{ Name: "testUser", Secret: "user1", } @@ -126,6 +112,19 @@ func testEnroll(c *Client, t *testing.T) { t.Errorf("Enroll failed: %s", err) } + if id.GetName() != "testUser" { + t.Error("Incorrect name retrieved") + } + + if id.GetECert() == nil { + t.Error("No ECert was returned") + } + + _, err = id.GetTCertBatch(&api.GetTCertBatchRequest{Count: 1}) + if err != nil { + t.Errorf("Failed to get batch of TCerts") + } + err = id.Store() if err != nil { t.Errorf("testEnroll: store failed: %s", err) @@ -135,7 +134,7 @@ func testEnroll(c *Client, t *testing.T) { func testDoubleEnroll(c *Client, t *testing.T) { - req := &idp.EnrollmentRequest{ + req := &api.EnrollmentRequest{ Name: "testUser", Secret: "user1", } @@ -153,7 +152,7 @@ func testReenroll(c *Client, t *testing.T) { t.Errorf("testReenroll: failed LoadMyIdentity: %s", err) return } - id, err = c.Reenroll(&idp.ReenrollmentRequest{ID: id}) + id, err = id.Reenroll(&api.ReenrollmentRequest{}) if err != nil { t.Errorf("testReenroll: failed reenroll: %s", err) return @@ -164,8 +163,8 @@ func testReenroll(c *Client, t *testing.T) { } } -func testRevocation(c *Client, t *testing.T, user, secret string, shouldPass bool) { - req := &idp.EnrollmentRequest{ +func testRevocation(c *Client, t *testing.T, user, secret string, ecertOnly, shouldPass bool) { + req := &api.EnrollmentRequest{ Name: user, Secret: secret, } @@ -174,7 +173,11 @@ func testRevocation(c *Client, t *testing.T, user, secret string, shouldPass boo t.Errorf("enroll of user '%s' with password '%s' failed", user, secret) return } - err = id.RevokeSelf() + if ecertOnly { + err = id.GetECert().RevokeSelf() + } else { + err = id.RevokeSelf() + } if shouldPass && err != nil { t.Errorf("testRevocation failed for user %s: %s", user, err) } else if !shouldPass && err == nil { @@ -203,13 +206,6 @@ func testLoadBadCSRInfo(c *Client, t *testing.T) { } } -func testCapabilities(c *Client, t *testing.T) { - caps := c.Capabilities() - if caps == nil { - t.Error("testCapabilities failed") - } -} - func TestSendBadPost(t *testing.T) { c := new(Client) curl := "fake" @@ -259,4 +255,5 @@ func runServer() { func TestLast(t *testing.T) { // Cleanup os.RemoveAll(dir) + os.Remove("../testdata/cop.db") } diff --git a/lib/identity.go b/lib/identity.go index d6a10be50..a36323aa9 100644 --- a/lib/identity.go +++ b/lib/identity.go @@ -22,42 +22,39 @@ import ( "net/http" "github.com/cloudflare/cfssl/log" - cop "github.com/hyperledger/fabric-cop/api" - "github.com/hyperledger/fabric-cop/idp" + "github.com/cloudflare/cfssl/signer" + "github.com/hyperledger/fabric-cop/api" "github.com/hyperledger/fabric-cop/util" ) func newIdentity(client *Client, name string, key []byte, cert []byte) *Identity { id := new(Identity) + id.name = name + id.ecert = newSigner(key, cert, id) id.client = client - id.Name = name - id.PublicSigner = newTemporalSigner(key, cert) return id } -// Identity is COP's implementation of an idp.Identity +// Identity is COP's implementation of an identity type Identity struct { - client *Client - Name string `json:"name"` - PublicSigner *TemporalSigner `json:"publicSigner"` + name string + ecert *Signer + client *Client } // GetName returns the identity name func (i *Identity) GetName() string { - return i.Name + return i.name } -// GetPublicSigner returns the public signer for this identity -func (i *Identity) GetPublicSigner() idp.TemporalSigner { - return i.PublicSigner +// GetECert returns the enrollment certificate signer for this identity +func (i *Identity) GetECert() *Signer { + return i.ecert } -// GetPrivateSigners returns private signers for this identity -// NOTE: The whole IDP abstraction is going to be removed and the name of this will just be -// GetTCerts or something normal. The IDP abstraction was originally done to allow for -// other providers, but that was done differently. -func (i *Identity) GetPrivateSigners(req *idp.GetPrivateSignersRequest) ([]idp.TemporalSigner, error) { - reqBody, err := util.Marshal(req, "idp.GetPrivateSignersRequest") +// GetTCertBatch returns a batch of TCerts for this identity +func (i *Identity) GetTCertBatch(req *api.GetTCertBatchRequest) ([]*Signer, error) { + reqBody, err := util.Marshal(req, "GetTCertBatchRequest") if err != nil { return nil, err } @@ -71,60 +68,100 @@ func (i *Identity) GetPrivateSigners(req *idp.GetPrivateSignersRequest) ([]idp.T return nil, nil } -// GetAttributeNames returns the names of all attributes associated with this identity -func (i *Identity) GetAttributeNames() ([]string, error) { - return nil, errors.New("NotImplemented") +// Register registers a new identity +// @param req The registration request +func (i *Identity) Register(req *api.RegistrationRequest) (*api.RegistrationResponse, error) { + log.Debugf("Register %+v", req) + if req.Name == "" { + return nil, errors.New("Register was called without a Name set") + } + if req.Group == "" { + return nil, errors.New("Register was called without a Group set") + } + + reqBody, err := util.Marshal(req, "RegistrationRequest") + if err != nil { + return nil, err + } + + // Send a post to the "register" endpoint with req as body + secret, err := i.Post("register", reqBody) + if err != nil { + return nil, err + } + + log.Debug("The register request completely successfully") + return &api.RegistrationResponse{Secret: secret.(string)}, nil +} + +// Reenroll reenrolls an existing Identity and returns a new Identity +// @param req The reenrollment request +func (i *Identity) Reenroll(req *api.ReenrollmentRequest) (*Identity, error) { + log.Debugf("Reenrolling %s", req) + + csrPEM, key, err := i.client.GenCSR(req.CSR, i.GetName()) + if err != nil { + return nil, err + } + + // Get the body of the request + sreq := signer.SignRequest{ + Hosts: signer.SplitHosts(req.Hosts), + Request: string(csrPEM), + Profile: req.Profile, + Label: req.Label, + } + body, err := util.Marshal(sreq, "SignRequest") + if err != nil { + return nil, err + } + + result, err := i.Post("reenroll", body) + if err != nil { + return nil, err + } + + return i.client.newIdentityFromResponse(result, i.GetName(), key) } // Revoke the identity associated with 'id' -func (i *Identity) Revoke(req *idp.RevocationRequest) error { +func (i *Identity) Revoke(req *api.RevocationRequest) error { log.Debugf("Entering identity.Revoke %+v", req) reqBody, err := util.Marshal(req, "RevocationRequest") if err != nil { return err } - _, err2 := i.Post("revoke", reqBody) - if err2 != nil { - return err2 + _, err = i.Post("revoke", reqBody) + if err != nil { + return err } log.Debugf("Successfully revoked %+v", req) return nil } -// RevokeSelf revokes the current identity +// RevokeSelf revokes the current identity and all certificates func (i *Identity) RevokeSelf() error { name := i.GetName() log.Debugf("RevokeSelf %s", name) - serial, aki, err := i.PublicSigner.GetSerial() - if err != nil { - return err - } - req := &idp.RevocationRequest{ - Name: name, - Serial: serial, - AKI: aki, + req := &api.RevocationRequest{ + Name: name, } return i.Revoke(req) } -// Delete this identity completely and revoke all of it's signers -func (i *Identity) Delete() error { - return errors.New("NotImplemented") -} - -// Store write my identity info +// Store writes my identity info to disk func (i *Identity) Store() error { if i.client == nil { return fmt.Errorf("An identity with no client may not be stored") } - return i.client.StoreMyIdentity(i.PublicSigner.Key, i.PublicSigner.Cert) + return i.client.StoreMyIdentity(i.ecert.key, i.ecert.cert) } // Post sends arbtrary request body (reqBody) to an endpoint. // This adds an authorization header which contains the signature // of this identity over the body and non-signature part of the authorization header. // The return value is the body of the response. -func (i *Identity) Post(endpoint string, reqBody []byte) ([]byte, error) { +func (i *Identity) Post(endpoint string, reqBody []byte) (interface{}, error) { req, err := i.client.NewPost(endpoint, reqBody) if err != nil { return nil, err @@ -137,28 +174,13 @@ func (i *Identity) Post(endpoint string, reqBody []byte) ([]byte, error) { } func (i *Identity) addTokenAuthHdr(req *http.Request, body []byte) error { - log.Debug("addTokenAuthHdr begin") - cert := i.GetMyCert() - key := i.GetMyKey() // TODO: Will change for BCCSP since we can't see key - if cert == nil || key == nil { - return cop.NewError(cop.AuthorizationFailure, "Failed to set authorization header token") - } - token, tokenerr := util.CreateToken(cert, key, body) - if tokenerr != nil { - log.Debug("addTokenAuthHdr failed: CreateToken") - return cop.WrapError(tokenerr, 1, "test") + log.Debug("adding token-based authorization header") + cert := i.ecert.cert + key := i.ecert.key + token, err := util.CreateToken(cert, key, body) + if err != nil { + return fmt.Errorf("Failed to add token authorization header: %s", err) } req.Header.Set("authorization", token) - log.Debug("addTokenAuthHdr success") return nil } - -// GetMyCert returns the PEM-encoded cert -func (i *Identity) GetMyCert() []byte { - return i.PublicSigner.GetMyCert() -} - -// GetMyKey returns the PEM-encoded key -func (i *Identity) GetMyKey() []byte { - return i.PublicSigner.getMyKey() -} diff --git a/lib/identity_test.go b/lib/identity_test.go index ff4692640..cefc361b5 100644 --- a/lib/identity_test.go +++ b/lib/identity_test.go @@ -31,9 +31,7 @@ func getIdentity() *Identity { func TestIdentity(t *testing.T) { id := getIdentity() testGetName(id, t) - testGetPublicSigner(id, t) - testGetAttributeNames(id, t) - testDelete(id, t) + testGetECert(id, t) } func testGetName(id *Identity, t *testing.T) { @@ -43,19 +41,9 @@ func testGetName(id *Identity, t *testing.T) { } } -func testGetPublicSigner(id *Identity, t *testing.T) { - publicSigner := id.GetPublicSigner() - if publicSigner == nil { - t.Error("No public signer returned") +func testGetECert(id *Identity, t *testing.T) { + ecert := id.GetECert() + if ecert == nil { + t.Error("No ECert was returned") } } - -// Place holder test, method has not yet been implemented -func testGetAttributeNames(id *Identity, t *testing.T) { - id.GetAttributeNames() -} - -// Place holder test, method has not yet been implemented -func testDelete(id *Identity, t *testing.T) { - id.Delete() -} diff --git a/lib/signer.go b/lib/signer.go index 3157fabe3..b90ec27b8 100644 --- a/lib/signer.go +++ b/lib/signer.go @@ -17,37 +17,39 @@ limitations under the License. package lib import ( - "errors" - - "github.com/hyperledger/fabric-cop/idp" + "github.com/cloudflare/cfssl/log" + "github.com/hyperledger/fabric-cop/api" ) -func newSigner(key []byte, cert []byte) Signer { - return Signer{newVerifier(cert), key} +func newSigner(key, cert []byte, id *Identity) *Signer { + return &Signer{ + key: key, + cert: cert, + id: id, + client: id.client, + } } -// Signer implements idp.Signer interface +// Signer represents a signer +// Each identity may have multiple signers, currently one ecert and multiple tcerts type Signer struct { - Verifier - Key []byte `json:"key"` -} - -// Sign the message -func (s *Signer) Sign(msg []byte) ([]byte, error) { - return nil, errors.New("NotImplemented") -} - -// SignOpts the message with options -func (s *Signer) SignOpts(msg []byte, opts idp.SignatureOpts) ([]byte, error) { - return nil, errors.New("NotImplemented") -} - -// NewAttributeProof creates a proof for an attribute -func (s *Signer) NewAttributeProof(spec *idp.AttributeProofSpec) (proof []byte, err error) { - return nil, errors.New("NotImplemented") + name string + key []byte + cert []byte + id *Identity + client *Client } -// TODO: -func (s *Signer) getMyKey() []byte { - return s.Key +// RevokeSelf revokes only the certificate associated with this signer +func (s *Signer) RevokeSelf() error { + log.Debugf("RevokeSelf %s", s.name) + serial, aki, err := GetCertID(s.cert) + if err != nil { + return err + } + req := &api.RevocationRequest{ + Serial: serial, + AKI: aki, + } + return s.id.Revoke(req) } diff --git a/lib/signer_test.go b/lib/signer_test.go deleted file mode 100644 index f68a369c3..000000000 --- a/lib/signer_test.go +++ /dev/null @@ -1,75 +0,0 @@ -/* -Copyright IBM Corp. 2016 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 lib - -import ( - "encoding/json" - "io/ioutil" - "testing" - - "github.com/hyperledger/fabric-cop/idp" -) - -func TestSignerMarshalling(t *testing.T) { - signer := `{"publicSigner":{"cert":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIvVENDQWFTZ0F3SUJBZ0lVVmE1WkpVd0ZTcU9MVjFRcU94clY3TkVadnJRd0NnWUlLb1pJemowRUF3SXcKYlRFTE1Ba0dBMVVFQmhNQ1ZWTXhFekFSQmdOVkJBZ1RDa05oYkdsbWIzSnVhV0V4RmpBVUJnTlZCQWNURFZOaApiaUJHY21GdVkybHpZMjh4RXpBUkJnTlZCQW9UQ2tOc2IzVmtSbXhoY21VeEhEQWFCZ05WQkFzVEUxTjVjM1JsCmJYTWdSVzVuYVc1bFpYSnBibWN3SGhjTk1UWXhNVEF6TURVeE9EQXdXaGNOTVRjeE1UQXpNRFV4T0RBd1dqQVEKTVE0d0RBWURWUVFERXdWaFpHMXBiakJaTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCT0xtT081dwo3REh6RUtNdkpJZmxwZjhQb1Z5dk1Uays1UmorQ1NBcVhQQ0NoNndPTi9yMnAxZjF6cDRmQXhVak96S1VMNCtrCnRpVm9pVHJmUUJzY010bWpmekI5TUE0R0ExVWREd0VCL3dRRUF3SUZvREFkQmdOVkhTVUVGakFVQmdnckJnRUYKQlFjREFRWUlLd1lCQlFVSEF3SXdEQVlEVlIwVEFRSC9CQUl3QURBZEJnTlZIUTRFRmdRVUJqc2RIV3RPcEZQTQpSZ3VJT3VITm1iaDdKem93SHdZRFZSMGpCQmd3Rm9BVWZrMTRuOXM0M25NakNpNWdWWnZuRG4vUWhWY3dDZ1lJCktvWkl6ajBFQXdJRFJ3QXdSQUlnWjJLYVU5R29CYzhqQ0pRTDdTcDg2RUVXT282UXJzK0FpdWV5VVIwMVdtVUMKSUF1RE1wSTdMOXJvREpjV3Y5WWF3NmwzdEVBYTBmdVJGY21ncFFXWEQ4eUkKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=","key":"LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUFMVkFWK044azdOOXhvSEtOV3pzUFc5N0g2TFAvRlNkb2lKeWtaY2xRTkFvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFNHVZNDduRHNNZk1Rb3k4a2grV2wvdytoWEs4eE9UN2xHUDRKSUNwYzhJS0hyQTQzK3ZhbgpWL1hPbmg4REZTTTdNcFF2ajZTMkpXaUpPdDlBR3h3eTJRPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo="}}` - - var s Signer - err := json.Unmarshal([]byte(signer), &s) - if err != nil { - t.Error("err: ", err) - } -} - -func getSigner() Signer { - key, _ := ioutil.ReadFile("../tesdata/ec-key.pem") - cert, _ := ioutil.ReadFile("../tesdata/ec.pem") - s := newSigner(key, cert) - return s -} - -func TestSigner(t *testing.T) { - s := getSigner() - testGetMyKey(s, t) - testSign(s, t) - testSignOpts(s, t) - testNewAttributeProof(s, t) -} - -func testGetMyKey(s Signer, t *testing.T) { - key := s.getMyKey() - if key != nil { - t.Error("Failed to get key") - } -} - -// Place holder test, method has not yet been implemented -func testSign(s Signer, t *testing.T) { - msg := []byte("") - s.Sign(msg) -} - -// Place holder test, method has not yet been implemented -func testSignOpts(s Signer, t *testing.T) { - msg := []byte("") - opts := new(idp.SignatureOpts) - s.SignOpts(msg, *opts) -} - -func testNewAttributeProof(s Signer, t *testing.T) { - spec := new(idp.AttributeProofSpec) - s.NewAttributeProof(spec) -} diff --git a/lib/tcert/api.go b/lib/tcert/api.go index b20df4cd0..a30069ca6 100644 --- a/lib/tcert/api.go +++ b/lib/tcert/api.go @@ -29,16 +29,20 @@ import ( // GetBatchRequest defines input to the GetBatch API type GetBatchRequest struct { // Number of TCerts in the batch. - Count uint `json:"count"` + Count int `json:"count"` + // If PublicKeys is non nil, generates a TCert for each public key; + // in this case, the 'Count' field is ignored and the number of TCerts + // generated matches the number of public keys in the array. + PublicKeys [][]byte `json:"public_keys,omitempty"` // The attribute name and values that are to be inserted in the issued TCerts. Attrs []Attribute `json:"attrs,omitempty"` // EncryptAttrs denotes whether to encrypt attribute values or not. // When set to true, each issued TCert in the batch will contain encrypted attribute values. - EncryptAttrs bool `json:"encryptAttrs,omitempty"` + EncryptAttrs bool `json:"encrypt_attrs,omitempty"` // Certificate Validity Period. If specified, the value used // is the minimum of this value and the configured validity period // of the TCert manager. - ValidityPeriod time.Duration `json:"validityPeriod,omitempty"` + ValidityPeriod time.Duration `json:"validity_period,omitempty"` // The pre-key to be used for key derivation. PreKey string `json:"prekey"` } diff --git a/lib/tcert/tcert.go b/lib/tcert/tcert.go index 6acb7f868..02b4b1024 100644 --- a/lib/tcert/tcert.go +++ b/lib/tcert/tcert.go @@ -90,7 +90,7 @@ type Mgr struct { ValidityPeriod time.Duration // MaxAllowedBatchSize is the maximum number of TCerts which can be requested at a time. // The default value is 1000. - MaxAllowedBatchSize uint + MaxAllowedBatchSize int } // GetBatch gets a batch of TCerts diff --git a/lib/temporalsigner.go b/lib/temporalsigner.go deleted file mode 100644 index 4d367fdf5..000000000 --- a/lib/temporalsigner.go +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright IBM Corp. 2016 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 lib - -import "errors" - -func newTemporalSigner(key []byte, cert []byte) *TemporalSigner { - return &TemporalSigner{newSigner(key, cert)} -} - -// TemporalSigner implements idp.TemporalSigner -type TemporalSigner struct { - Signer -} - -// Renew renews the signer's certificate -func (ts *TemporalSigner) Renew() error { - return errors.New("NotImplemented") -} - -// Revoke revokes the signer's certificate -func (ts *TemporalSigner) Revoke() error { - return errors.New("NotImplemented") -} diff --git a/lib/temporalsigner_test.go b/lib/temporalsigner_test.go deleted file mode 100644 index 57f9c5d8f..000000000 --- a/lib/temporalsigner_test.go +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright IBM Corp. 2016 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 lib - -import "testing" - -func getTemporalSigner() *TemporalSigner { - temporal := newTemporalSigner([]byte("key"), []byte("cert")) - return temporal -} - -func TestTemporalSigner(t *testing.T) { - temporal := getTemporalSigner() - testRenew(temporal, t) -} - -// Place holder test, method has not yet been implemented -func testRenew(temporal *TemporalSigner, t *testing.T) { - temporal.Renew() -} - -func testRevoke(temporal *TemporalSigner, t *testing.T) { - temporal.Revoke() -} diff --git a/lib/tls/tls.go b/lib/tls/tls.go index 574433959..1010c40fb 100644 --- a/lib/tls/tls.go +++ b/lib/tls/tls.go @@ -19,10 +19,10 @@ package tls import ( "crypto/tls" "crypto/x509" + "fmt" "io/ioutil" "github.com/cloudflare/cfssl/log" - cop "github.com/hyperledger/fabric-cop/api" ) // ClientTLSConfig defines the root ca and client certificate and key files @@ -65,7 +65,7 @@ func GetClientTLSConfig(cfg *ClientTLSConfig) (*tls.Config, error) { } ok := caCertPool.AppendCertsFromPEM(caCert) if !ok { - return nil, cop.NewError(cop.TLSError, "Failed to parse and append certificate [certificate: %s]", cacert) + return nil, fmt.Errorf("Failed to parse and append certificate [certificate: %s]", cacert) } } diff --git a/lib/util.go b/lib/util.go new file mode 100644 index 000000000..72e801548 --- /dev/null +++ b/lib/util.go @@ -0,0 +1,48 @@ +/* +Copyright IBM Corp. 2016 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 lib + +import ( + "crypto/x509" + "encoding/hex" + "encoding/pem" + "fmt" +) + +// GetCertID returns both the serial number and AKI (Authority Key ID) for the certificate +func GetCertID(bytes []byte) (string, string, error) { + cert, err := BytesToX509Cert(bytes) + if err != nil { + return "", "", err + } + serial := cert.SerialNumber.String() + aki := hex.EncodeToString(cert.AuthorityKeyId) + return serial, aki, nil +} + +// BytesToX509Cert converts bytes (PEM or DER) to an X509 certificate +func BytesToX509Cert(bytes []byte) (*x509.Certificate, error) { + dcert, _ := pem.Decode(bytes) + if dcert != nil { + bytes = dcert.Bytes + } + cert, err := x509.ParseCertificate(bytes) + if err != nil { + return nil, fmt.Errorf("buffer was neither PEM nor DER encoding: %s", err) + } + return cert, err +} diff --git a/lib/verifier.go b/lib/verifier.go deleted file mode 100644 index 5b12b9ea5..000000000 --- a/lib/verifier.go +++ /dev/null @@ -1,93 +0,0 @@ -/* -Copyright IBM Corp. 2016 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 lib - -import ( - "crypto/x509" - "encoding/hex" - "encoding/pem" - "errors" - - "github.com/cloudflare/cfssl/log" - "github.com/hyperledger/fabric-cop/idp" - "github.com/hyperledger/fabric-cop/util" -) - -func newVerifier(cert []byte) Verifier { - return Verifier{cert} -} - -// Verifier implements the idp.Verifier interface -type Verifier struct { - Cert []byte `json:"cert"` -} - -// VerifySelf verifies myself -func (v *Verifier) VerifySelf() error { - return errors.New("NotImplemented") -} - -// Verify a signature over some message -func (v *Verifier) Verify(msg []byte, sig []byte) error { - return errors.New("NotImplemented") -} - -// VerifyOpts verifies a signature over some message with options -func (v *Verifier) VerifyOpts(msg []byte, sig []byte, opts idp.SignatureOpts) error { - return errors.New("NotImplemented") -} - -// VerifyAttributes verifies attributes given proofs -func (v *Verifier) VerifyAttributes(proof [][]byte, spec *idp.AttributeProofSpec) error { - return errors.New("NotImplemented") -} - -// Serialize a Verifier -func (v *Verifier) Serialize() ([]byte, error) { - return util.Marshal(v, "Verifier") -} - -// GetMyCert will return certificate in PEM encoding -func (v *Verifier) GetMyCert() []byte { - return v.Cert -} - -// GetX509Cert will return X509 certificate -func (v *Verifier) GetX509Cert() (*x509.Certificate, error) { - dcert, _ := pem.Decode(v.Cert) - if dcert == nil { - log.Debug("GetX509Cert.decode failed") - return nil, errors.New("pem decode failed") - } - cert, err := x509.ParseCertificate(dcert.Bytes) - if err != nil { - log.Debugf("GetX509Cert.parse err=%s", err) - return nil, err - } - return cert, err -} - -// GetSerial returns the serial number and AKI (Authority Key ID) -func (v *Verifier) GetSerial() (string, string, error) { - cert, err := v.GetX509Cert() - if err != nil { - return "", "", err - } - serial := cert.SerialNumber.String() - aki := hex.EncodeToString(cert.AuthorityKeyId) - return serial, aki, nil -} diff --git a/lib/verifier_test.go b/lib/verifier_test.go deleted file mode 100644 index 1dba11e79..000000000 --- a/lib/verifier_test.go +++ /dev/null @@ -1,94 +0,0 @@ -/* -Copyright IBM Corp. 2016 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 lib - -import ( - "encoding/json" - "io/ioutil" - "testing" - - "github.com/hyperledger/fabric-cop/idp" -) - -func TestMarshalling(t *testing.T) { - cert := `{"cert":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIvVENDQWFTZ0F3SUJBZ0lVVmE1WkpVd0ZTcU9MVjFRcU94clY3TkVadnJRd0NnWUlLb1pJemowRUF3SXcKYlRFTE1Ba0dBMVVFQmhNQ1ZWTXhFekFSQmdOVkJBZ1RDa05oYkdsbWIzSnVhV0V4RmpBVUJnTlZCQWNURFZOaApiaUJHY21GdVkybHpZMjh4RXpBUkJnTlZCQW9UQ2tOc2IzVmtSbXhoY21VeEhEQWFCZ05WQkFzVEUxTjVjM1JsCmJYTWdSVzVuYVc1bFpYSnBibWN3SGhjTk1UWXhNVEF6TURVeE9EQXdXaGNOTVRjeE1UQXpNRFV4T0RBd1dqQVEKTVE0d0RBWURWUVFERXdWaFpHMXBiakJaTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCT0xtT081dwo3REh6RUtNdkpJZmxwZjhQb1Z5dk1Uays1UmorQ1NBcVhQQ0NoNndPTi9yMnAxZjF6cDRmQXhVak96S1VMNCtrCnRpVm9pVHJmUUJzY010bWpmekI5TUE0R0ExVWREd0VCL3dRRUF3SUZvREFkQmdOVkhTVUVGakFVQmdnckJnRUYKQlFjREFRWUlLd1lCQlFVSEF3SXdEQVlEVlIwVEFRSC9CQUl3QURBZEJnTlZIUTRFRmdRVUJqc2RIV3RPcEZQTQpSZ3VJT3VITm1iaDdKem93SHdZRFZSMGpCQmd3Rm9BVWZrMTRuOXM0M25NakNpNWdWWnZuRG4vUWhWY3dDZ1lJCktvWkl6ajBFQXdJRFJ3QXdSQUlnWjJLYVU5R29CYzhqQ0pRTDdTcDg2RUVXT282UXJzK0FpdWV5VVIwMVdtVUMKSUF1RE1wSTdMOXJvREpjV3Y5WWF3NmwzdEVBYTBmdVJGY21ncFFXWEQ4eUkKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo="}` - - var v Verifier - err := json.Unmarshal([]byte(cert), &v) - if err != nil { - t.Error("err: ", err) - } - -} - -func getVerifier() Verifier { - cert, _ := ioutil.ReadFile("../tesdata/ec.pem") - v := newVerifier(cert) - return v -} - -func TestVerifier(t *testing.T) { - v := getVerifier() - testGetCert(v, t) - testSerialize(v, t) - testVerifySelf(v, t) - testVerify(v, t) - testVerifyOpts(v, t) - testVerifyAttributes(v, t) - -} - -func testGetCert(v Verifier, t *testing.T) { - cert := v.GetMyCert() - if cert != nil { - t.Error("Failed to get cert") - } -} - -func testSerialize(v Verifier, t *testing.T) { - _, err := v.Serialize() - if err != nil { - t.Error("Failed to serialize to verifier object") - } -} - -// Place holder test, method has not yet been implemented -func testVerifySelf(v Verifier, t *testing.T) { - v.VerifySelf() -} - -// Place holder test, method has not yet been implemented -func testVerify(v Verifier, t *testing.T) { - msg := []byte("") - sig := []byte("") - v.Verify(msg, sig) -} - -// Place holder test, method has not yet been implemented -func testVerifyOpts(v Verifier, t *testing.T) { - msg := []byte("") - sig := []byte("") - opts := new(idp.SignatureOpts) - v.VerifyOpts(msg, sig, *opts) -} - -// Place holder test, method has not yet been implemented -func testVerifyAttributes(v Verifier, t *testing.T) { - proof := [][]byte{[]byte("")} - spec := new(idp.AttributeProofSpec) - v.VerifyAttributes(proof, spec) -} diff --git a/scripts/bash_profile b/scripts/bash_profile index 50421c5cc..53405b6cc 100644 --- a/scripts/bash_profile +++ b/scripts/bash_profile @@ -166,6 +166,6 @@ function gencov { echo "Generating coverage report ..." go get github.com/axw/gocov/gocov go get -u gopkg.in/matm/v1/gocov-html - gocov test `go list ./... | grep -Ev '/vendor/|/idp|/dbutil|/ldap'` | gocov-html > /tmp/coverage.html + gocov test `go list ./... | grep -Ev '/vendor/|/api|/dbutil|/ldap'` | gocov-html > /tmp/coverage.html echo "View the coverage report by pasting the following URL in your browser: file:///tmp/coverage.html" } diff --git a/scripts/run_tests b/scripts/run_tests index 90dfd0768..4cc721f91 100755 --- a/scripts/run_tests +++ b/scripts/run_tests @@ -9,7 +9,7 @@ export PATH=$PATH:$GOPATH/bin go get github.com/axw/gocov/... go get github.com/AlekSi/gocov-xml -PKGS=`go list github.com/hyperledger/fabric-cop/... | grep -Ev '/vendor/|/idp|/dbutil|/ldap'` +PKGS=`go list github.com/hyperledger/fabric-cop/... | grep -Ev '/vendor/|/api|/dbutil|/ldap'` gocov test $PKGS | gocov-xml > coverage.xml diff --git a/testdata/testconfig.json b/testdata/testconfig.json index 63f5b8a44..ed33b8d15 100644 --- a/testdata/testconfig.json +++ b/testdata/testconfig.json @@ -39,6 +39,22 @@ "group": "bank_a", "attrs": [{"name":"hf.Revoker", "value": "true"}] }, + "revoker2": { + "pass": "revokerpw2", + "type": "client", + "group": "bank_a", + "attrs": [{"name":"hf.Revoker", "value": "true"}] + }, + "nonrevoker": { + "pass": "nonrevokerpw", + "type": "client", + "group": "bank_a" + }, + "nonrevoker2": { + "pass": "nonrevokerpw2", + "type": "client", + "group": "bank_a" + }, "notadmin": { "pass": "pass", "type": "client", diff --git a/util/util.go b/util/util.go index aead36210..f26521da5 100644 --- a/util/util.go +++ b/util/util.go @@ -40,8 +40,6 @@ import ( "strings" "time" - "github.com/cloudflare/cfssl/log" - cop "github.com/hyperledger/fabric-cop/api" "github.com/jmoiron/sqlx" ) @@ -91,23 +89,13 @@ func RemoveQuotes(str string) string { } // ReadFile reads a file -func ReadFile(file string) ([]byte, cop.Error) { - bytes, err := ioutil.ReadFile(file) - if err != nil { - log.Errorf("failure reading file '%s': %s", file, err) - return nil, cop.WrapError(err, cop.ReadFileError, "failed reading file at %s", file) - } - return bytes, nil +func ReadFile(file string) ([]byte, error) { + return ioutil.ReadFile(file) } // WriteFile writes a file -func WriteFile(file string, buf []byte, perm os.FileMode) cop.Error { - err := ioutil.WriteFile(file, buf, perm) - if err != nil { - log.Errorf("failure writing file '%s': %s", file, err) - return cop.WrapError(err, cop.WriteFileError, "failed reading file at %s", file) - } - return nil +func WriteFile(file string, buf []byte, perm os.FileMode) error { + return ioutil.WriteFile(file, buf, perm) } // FileExists checks to see if a file exists @@ -121,21 +109,19 @@ func FileExists(name string) bool { } // Marshal to bytes -func Marshal(from interface{}, what string) ([]byte, cop.Error) { +func Marshal(from interface{}, what string) ([]byte, error) { buf, err := json.Marshal(from) if err != nil { - log.Errorf("failure unmarshalling '%s': %s", what, err) - return nil, cop.WrapError(err, cop.MarshallError, "error marshalling %s", what) + return nil, fmt.Errorf("Failed to marshal %s: %s", what, err) } return buf, nil } // Unmarshal from bytes -func Unmarshal(from []byte, to interface{}, what string) cop.Error { +func Unmarshal(from []byte, to interface{}, what string) error { err := json.Unmarshal(from, to) if err != nil { - log.Errorf("failure unmarshalling '%s': %s", what, err) - return cop.WrapError(err, cop.UnmarshallError, "error unmarshalling %s", what) + return fmt.Errorf("Failed to unmarshal %s: %s", what, err) } return nil } @@ -416,13 +402,11 @@ func MakeFileAbs(file, dir string) (string, error) { return "", nil } if filepath.IsAbs(file) { - log.Debugf("%s is already an absolute file", file) return file, nil } path, err := filepath.Abs(filepath.Join(dir, file)) if err != nil { return "", fmt.Errorf("Failed making '%s' absolute based on '%s'", file, dir) } - log.Debugf("absolute file path for %s is %s", file, path) return path, nil }