Skip to content

Commit 3a7f893

Browse files
author
Keith Smith
committed
[FAB-5346] Moving attrmgr to fabric
This change set is simply moving a small package from fabric-ca to fabric to avoid vendoring fabric-ca from fabric. This package was at fabric-ca/attrmgr and moving to fabric/common/attrmgr. Change-Id: Id24b83e8fb7f0f2b6687088809f91fe4264f889e Signed-off-by: Keith Smith <bksmith@us.ibm.com>
1 parent 30e6f49 commit 3a7f893

File tree

2 files changed

+326
-0
lines changed

2 files changed

+326
-0
lines changed

common/attrmgr/attrmgr.go

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
/*
2+
Copyright IBM Corp. 2017 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
/*
18+
* The attrmgr package contains utilities for managing attributes.
19+
* Attributes are added to an X509 certificate as an extension.
20+
*/
21+
22+
package attrmgr
23+
24+
import (
25+
"crypto/x509"
26+
"crypto/x509/pkix"
27+
"encoding/asn1"
28+
"encoding/json"
29+
"fmt"
30+
31+
"github.com/pkg/errors"
32+
)
33+
34+
var (
35+
// AttrOID is the ASN.1 object identifier for an attribute extension in an
36+
// X509 certificate
37+
AttrOID = asn1.ObjectIdentifier{1, 2, 3, 4, 5, 6, 7, 8, 1}
38+
// AttrOIDString is the string version of AttrOID
39+
AttrOIDString = "1.2.3.4.5.6.7.8.1"
40+
)
41+
42+
// Attribute is a name/value pair
43+
type Attribute interface {
44+
// GetName returns the name of the attribute
45+
GetName() string
46+
// GetValue returns the value of the attribute
47+
GetValue() string
48+
}
49+
50+
// AttributeRequest is a request for an attribute
51+
type AttributeRequest interface {
52+
// GetName returns the name of an attribute
53+
GetName() string
54+
// IsRequired returns true if the attribute is required
55+
IsRequired() bool
56+
}
57+
58+
// New constructs an attribute manager
59+
func New() *Mgr { return &Mgr{} }
60+
61+
// Mgr is the attribute manager and is the main object for this package
62+
type Mgr struct{}
63+
64+
// ProcessAttributeRequestsForCert add attributes to an X509 certificate, given
65+
// attribute requests and attributes.
66+
func (mgr *Mgr) ProcessAttributeRequestsForCert(requests []AttributeRequest, attributes []Attribute, cert *x509.Certificate) error {
67+
attrs, err := mgr.ProcessAttributeRequests(requests, attributes)
68+
if err != nil {
69+
return err
70+
}
71+
return mgr.AddAttributesToCert(attrs, cert)
72+
}
73+
74+
// ProcessAttributeRequests takes an array of attribute requests and an identity's attributes
75+
// and returns an Attributes object containing the requested attributes.
76+
func (mgr *Mgr) ProcessAttributeRequests(requests []AttributeRequest, attributes []Attribute) (*Attributes, error) {
77+
attrsMap := map[string]string{}
78+
attrs := &Attributes{Attrs: attrsMap}
79+
missingRequiredAttrs := []string{}
80+
// For each of the attribute requests
81+
for _, req := range requests {
82+
// Get the attribute
83+
name := req.GetName()
84+
attr := getAttrByName(name, attributes)
85+
if attr == nil {
86+
if req.IsRequired() {
87+
// Didn't find attribute and it was required; return error below
88+
missingRequiredAttrs = append(missingRequiredAttrs, name)
89+
}
90+
// Skip attribute requests which aren't required
91+
continue
92+
}
93+
attrsMap[name] = attr.GetValue()
94+
}
95+
if len(missingRequiredAttrs) > 0 {
96+
return nil, errors.Errorf("The following required attributes are missing: %+v",
97+
missingRequiredAttrs)
98+
}
99+
return attrs, nil
100+
}
101+
102+
// AddAttributesToCert adds public attribute info to an X509 certificate.
103+
func (mgr *Mgr) AddAttributesToCert(attrs *Attributes, cert *x509.Certificate) error {
104+
buf, err := json.Marshal(attrs)
105+
if err != nil {
106+
return errors.Wrap(err, "Failed to marshal attributes")
107+
}
108+
ext := pkix.Extension{
109+
Id: AttrOID,
110+
Critical: false,
111+
Value: buf,
112+
}
113+
cert.Extensions = append(cert.Extensions, ext)
114+
return nil
115+
}
116+
117+
// GetAttributesFromCert gets the attributes from a certificate.
118+
func (mgr *Mgr) GetAttributesFromCert(cert *x509.Certificate) (*Attributes, error) {
119+
// Get certificate attributes from the certificate if it exists
120+
buf, err := getAttributesFromCert(cert)
121+
if err != nil {
122+
return nil, err
123+
}
124+
// Unmarshal into attributes object
125+
attrs := &Attributes{}
126+
if buf != nil {
127+
err := json.Unmarshal(buf, attrs)
128+
if err != nil {
129+
return nil, errors.Wrap(err, "Failed to unmarshal attributes from certificate")
130+
}
131+
}
132+
return attrs, nil
133+
}
134+
135+
// Attributes contains attribute names and values
136+
type Attributes struct {
137+
Attrs map[string]string `json:"attrs"`
138+
}
139+
140+
// Names returns the names of the attributes
141+
func (a *Attributes) Names() []string {
142+
i := 0
143+
names := make([]string, len(a.Attrs))
144+
for name := range a.Attrs {
145+
names[i] = name
146+
i++
147+
}
148+
return names
149+
}
150+
151+
// Contains returns true if the named attribute is found
152+
func (a *Attributes) Contains(name string) bool {
153+
_, ok := a.Attrs[name]
154+
return ok
155+
}
156+
157+
// Value returns an attribute's value
158+
func (a *Attributes) Value(name string) (string, bool, error) {
159+
attr, ok := a.Attrs[name]
160+
return attr, ok, nil
161+
}
162+
163+
// True returns nil if the value of attribute 'name' is true;
164+
// otherwise, an appropriate error is returned.
165+
func (a *Attributes) True(name string) error {
166+
val, ok, err := a.Value(name)
167+
if err != nil {
168+
return err
169+
}
170+
if !ok {
171+
return fmt.Errorf("Attribute '%s' was not found", name)
172+
}
173+
if val != "true" {
174+
return fmt.Errorf("Attribute '%s' is not true", name)
175+
}
176+
return nil
177+
}
178+
179+
// Get the attribute info from a certificate extension, or return nil if not found
180+
func getAttributesFromCert(cert *x509.Certificate) ([]byte, error) {
181+
for _, ext := range cert.Extensions {
182+
if isAttrOID(ext.Id) {
183+
return ext.Value, nil
184+
}
185+
}
186+
return nil, nil
187+
}
188+
189+
// Is the object ID equal to the attribute info object ID?
190+
func isAttrOID(oid asn1.ObjectIdentifier) bool {
191+
if len(oid) != len(AttrOID) {
192+
return false
193+
}
194+
for idx, val := range oid {
195+
if val != AttrOID[idx] {
196+
return false
197+
}
198+
}
199+
return true
200+
}
201+
202+
// Get an attribute from 'attrs' by its name, or nil if not found
203+
func getAttrByName(name string, attrs []Attribute) Attribute {
204+
for _, attr := range attrs {
205+
if attr.GetName() == name {
206+
return attr
207+
}
208+
}
209+
return nil
210+
}

common/attrmgr/attrmgr_test.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
Copyright IBM Corp. 2016 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
package attrmgr_test
17+
18+
import (
19+
"crypto/x509"
20+
"testing"
21+
22+
"github.com/hyperledger/fabric/common/attrmgr"
23+
"github.com/stretchr/testify/assert"
24+
)
25+
26+
// TestAttrs tests attributes
27+
func TestAttrs(t *testing.T) {
28+
mgr := attrmgr.New()
29+
attrs := []attrmgr.Attribute{
30+
&Attribute{Name: "attr1", Value: "val1"},
31+
&Attribute{Name: "attr2", Value: "val2"},
32+
&Attribute{Name: "attr3", Value: "val3"},
33+
&Attribute{Name: "boolAttr", Value: "true"},
34+
}
35+
reqs := []attrmgr.AttributeRequest{
36+
&AttributeRequest{Name: "attr1", Require: false},
37+
&AttributeRequest{Name: "attr2", Require: true},
38+
&AttributeRequest{Name: "boolAttr", Require: true},
39+
&AttributeRequest{Name: "noattr1", Require: false},
40+
}
41+
cert := &x509.Certificate{}
42+
43+
// Verify that the certificate has no attributes
44+
at, err := mgr.GetAttributesFromCert(cert)
45+
if err != nil {
46+
t.Fatalf("Failed to GetAttributesFromCert: %s", err)
47+
}
48+
numAttrs := len(at.Names())
49+
assert.True(t, numAttrs == 0, "expecting 0 attributes but found %d", numAttrs)
50+
51+
// Add attributes to certificate
52+
err = mgr.ProcessAttributeRequestsForCert(reqs, attrs, cert)
53+
if err != nil {
54+
t.Fatalf("Failed to ProcessAttributeRequestsForCert: %s", err)
55+
}
56+
57+
// Get attributes from the certificate and verify the count is correct
58+
at, err = mgr.GetAttributesFromCert(cert)
59+
if err != nil {
60+
t.Fatalf("Failed to GetAttributesFromCert: %s", err)
61+
}
62+
numAttrs = len(at.Names())
63+
assert.True(t, numAttrs == 3, "expecting 3 attributes but found %d", numAttrs)
64+
65+
// Check individual attributes
66+
checkAttr(t, "attr1", "val1", at)
67+
checkAttr(t, "attr2", "val2", at)
68+
checkAttr(t, "attr3", "", at)
69+
checkAttr(t, "noattr1", "", at)
70+
assert.NoError(t, at.True("boolAttr"))
71+
72+
// Negative test case: add required attributes which don't exist
73+
reqs = []attrmgr.AttributeRequest{
74+
&AttributeRequest{Name: "noattr1", Require: true},
75+
}
76+
err = mgr.ProcessAttributeRequestsForCert(reqs, attrs, cert)
77+
assert.Error(t, err)
78+
}
79+
80+
func checkAttr(t *testing.T, name, val string, attrs *attrmgr.Attributes) {
81+
v, ok, err := attrs.Value(name)
82+
assert.NoError(t, err)
83+
if val == "" {
84+
assert.False(t, attrs.Contains(name), "contains attribute '%s'", name)
85+
assert.False(t, ok, "attribute '%s' was found", name)
86+
} else {
87+
assert.True(t, attrs.Contains(name), "does not contain attribute '%s'", name)
88+
assert.True(t, ok, "attribute '%s' was not found", name)
89+
assert.True(t, v == val, "incorrect value for '%s'; expected '%s' but found '%s'", name, val, v)
90+
}
91+
}
92+
93+
type Attribute struct {
94+
Name, Value string
95+
}
96+
97+
func (a *Attribute) GetName() string {
98+
return a.Name
99+
}
100+
101+
func (a *Attribute) GetValue() string {
102+
return a.Value
103+
}
104+
105+
type AttributeRequest struct {
106+
Name string
107+
Require bool
108+
}
109+
110+
func (ar *AttributeRequest) GetName() string {
111+
return ar.Name
112+
}
113+
114+
func (ar *AttributeRequest) IsRequired() bool {
115+
return ar.Require
116+
}

0 commit comments

Comments
 (0)