[#139] sdk/acl: Add eACL structures
Signed-off-by: Alex Vanin <alexey@nspcc.ru>
This commit is contained in:
parent
182a3af9b8
commit
edf2e2d62f
6 changed files with 592 additions and 0 deletions
224
pkg/acl/eacl/enums.go
Normal file
224
pkg/acl/eacl/enums.go
Normal file
|
@ -0,0 +1,224 @@
|
||||||
|
package eacl
|
||||||
|
|
||||||
|
import (
|
||||||
|
v2acl "github.com/nspcc-dev/neofs-api-go/v2/acl"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Action taken if EACL record matched request.
|
||||||
|
type Action uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
ActionUnknown Action = iota
|
||||||
|
ActionAllow
|
||||||
|
ActionDeny
|
||||||
|
)
|
||||||
|
|
||||||
|
// Operation is a object service method to match request.
|
||||||
|
type Operation uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
OperationUnknown Operation = iota
|
||||||
|
OperationGet
|
||||||
|
OperationHead
|
||||||
|
OperationPut
|
||||||
|
OperationDelete
|
||||||
|
OperationSearch
|
||||||
|
OperationRange
|
||||||
|
OperationRangeHash
|
||||||
|
)
|
||||||
|
|
||||||
|
// Role is a group of request senders to match request.
|
||||||
|
type Role uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
RoleUnknown Role = iota
|
||||||
|
// RoleUser is a group of senders that contains only key of container owner.
|
||||||
|
RoleUser
|
||||||
|
// RoleSystem is a group of senders that contains keys of container nodes and
|
||||||
|
// inner ring nodes.
|
||||||
|
RoleSystem
|
||||||
|
// RoleOthers is a group of senders that contains none of above keys.
|
||||||
|
RoleOthers
|
||||||
|
)
|
||||||
|
|
||||||
|
// Match is binary operation on filer name and value to check if request is matched.
|
||||||
|
type Match uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
MatchUnknown Match = iota
|
||||||
|
MatchStringEqual
|
||||||
|
MatchStringNotEqual
|
||||||
|
)
|
||||||
|
|
||||||
|
// FilterHeaderType indicates source of headers to make matches.
|
||||||
|
type FilterHeaderType uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
HeaderTypeUnknown FilterHeaderType = iota
|
||||||
|
HeaderFromRequest
|
||||||
|
HeaderFromObject
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// HdrObjSysNameID is a name of ID field in system header of object.
|
||||||
|
HdrObjSysNameID = "_ID"
|
||||||
|
|
||||||
|
// HdrObjSysNameCID is a name of cid field in system header of object.
|
||||||
|
HdrObjSysNameCID = "_CID"
|
||||||
|
|
||||||
|
// HdrObjSysNameOwnerID is a name of OwnerID field in system header of object.
|
||||||
|
HdrObjSysNameOwnerID = "_OWNER_ID"
|
||||||
|
|
||||||
|
// HdrObjSysNameVersion is a name of version field in system header of object.
|
||||||
|
HdrObjSysNameVersion = "_VERSION"
|
||||||
|
|
||||||
|
// HdrObjSysNamePayloadLength is a name of PayloadLength field in system header of object.
|
||||||
|
HdrObjSysNamePayloadLength = "_PAYLOAD_LENGTH"
|
||||||
|
|
||||||
|
// HdrObjSysNameCreatedEpoch is a name of CreatedAt.Epoch field in system header of object.
|
||||||
|
HdrObjSysNameCreatedEpoch = "_CREATED_EPOCH"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a Action) ToV2() v2acl.Action {
|
||||||
|
switch a {
|
||||||
|
case ActionAllow:
|
||||||
|
return v2acl.ActionAllow
|
||||||
|
case ActionDeny:
|
||||||
|
return v2acl.ActionDeny
|
||||||
|
default:
|
||||||
|
return v2acl.ActionUnknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ActionFromV2(action v2acl.Action) (a Action) {
|
||||||
|
switch action {
|
||||||
|
case v2acl.ActionAllow:
|
||||||
|
a = ActionAllow
|
||||||
|
case v2acl.ActionDeny:
|
||||||
|
a = ActionDeny
|
||||||
|
default:
|
||||||
|
a = ActionUnknown
|
||||||
|
}
|
||||||
|
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o Operation) ToV2() v2acl.Operation {
|
||||||
|
switch o {
|
||||||
|
case OperationGet:
|
||||||
|
return v2acl.OperationGet
|
||||||
|
case OperationHead:
|
||||||
|
return v2acl.OperationHead
|
||||||
|
case OperationPut:
|
||||||
|
return v2acl.OperationPut
|
||||||
|
case OperationDelete:
|
||||||
|
return v2acl.OperationDelete
|
||||||
|
case OperationSearch:
|
||||||
|
return v2acl.OperationSearch
|
||||||
|
case OperationRange:
|
||||||
|
return v2acl.OperationRange
|
||||||
|
case OperationRangeHash:
|
||||||
|
return v2acl.OperationRangeHash
|
||||||
|
default:
|
||||||
|
return v2acl.OperationUnknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func OperationFromV2(operation v2acl.Operation) (o Operation) {
|
||||||
|
switch operation {
|
||||||
|
case v2acl.OperationGet:
|
||||||
|
o = OperationGet
|
||||||
|
case v2acl.OperationHead:
|
||||||
|
o = OperationHead
|
||||||
|
case v2acl.OperationPut:
|
||||||
|
o = OperationPut
|
||||||
|
case v2acl.OperationDelete:
|
||||||
|
o = OperationDelete
|
||||||
|
case v2acl.OperationSearch:
|
||||||
|
o = OperationSearch
|
||||||
|
case v2acl.OperationRange:
|
||||||
|
o = OperationRange
|
||||||
|
case v2acl.OperationRangeHash:
|
||||||
|
o = OperationRangeHash
|
||||||
|
default:
|
||||||
|
o = OperationUnknown
|
||||||
|
}
|
||||||
|
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Role) ToV2() v2acl.Role {
|
||||||
|
switch r {
|
||||||
|
case RoleUser:
|
||||||
|
return v2acl.RoleUser
|
||||||
|
case RoleSystem:
|
||||||
|
return v2acl.RoleSystem
|
||||||
|
case RoleOthers:
|
||||||
|
return v2acl.RoleOthers
|
||||||
|
default:
|
||||||
|
return v2acl.RoleUnknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func RoleFromV2(role v2acl.Role) (r Role) {
|
||||||
|
switch role {
|
||||||
|
case v2acl.RoleUser:
|
||||||
|
r = RoleUser
|
||||||
|
case v2acl.RoleSystem:
|
||||||
|
r = RoleSystem
|
||||||
|
case v2acl.RoleOthers:
|
||||||
|
r = RoleOthers
|
||||||
|
default:
|
||||||
|
r = RoleUnknown
|
||||||
|
}
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Match) ToV2() v2acl.MatchType {
|
||||||
|
switch m {
|
||||||
|
case MatchStringEqual:
|
||||||
|
return v2acl.MatchTypeStringEqual
|
||||||
|
case MatchStringNotEqual:
|
||||||
|
return v2acl.MatchTypeStringNotEqual
|
||||||
|
default:
|
||||||
|
return v2acl.MatchTypeUnknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MatchFromV2(match v2acl.MatchType) (m Match) {
|
||||||
|
switch match {
|
||||||
|
case v2acl.MatchTypeStringEqual:
|
||||||
|
m = MatchStringEqual
|
||||||
|
case v2acl.MatchTypeStringNotEqual:
|
||||||
|
m = MatchStringNotEqual
|
||||||
|
default:
|
||||||
|
m = MatchUnknown
|
||||||
|
}
|
||||||
|
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h FilterHeaderType) ToV2() v2acl.HeaderType {
|
||||||
|
switch h {
|
||||||
|
case HeaderFromRequest:
|
||||||
|
return v2acl.HeaderTypeRequest
|
||||||
|
case HeaderFromObject:
|
||||||
|
return v2acl.HeaderTypeObject
|
||||||
|
default:
|
||||||
|
return v2acl.HeaderTypeUnknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func FilterHeaderTypeFromV2(header v2acl.HeaderType) (h FilterHeaderType) {
|
||||||
|
switch header {
|
||||||
|
case v2acl.HeaderTypeRequest:
|
||||||
|
h = HeaderFromRequest
|
||||||
|
case v2acl.HeaderTypeObject:
|
||||||
|
h = HeaderFromObject
|
||||||
|
default:
|
||||||
|
h = HeaderTypeUnknown
|
||||||
|
}
|
||||||
|
|
||||||
|
return h
|
||||||
|
}
|
55
pkg/acl/eacl/filter.go
Normal file
55
pkg/acl/eacl/filter.go
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
package eacl
|
||||||
|
|
||||||
|
import (
|
||||||
|
v2acl "github.com/nspcc-dev/neofs-api-go/v2/acl"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Filter defines check conditions if request header is matched or not. Matched
|
||||||
|
// header means that request should be processed according to EACL action.
|
||||||
|
type Filter struct {
|
||||||
|
from FilterHeaderType
|
||||||
|
name string
|
||||||
|
matcher Match
|
||||||
|
value string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Filter) Value() string {
|
||||||
|
return a.value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Filter) Matcher() Match {
|
||||||
|
return a.matcher
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Filter) Name() string {
|
||||||
|
return a.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Filter) From() FilterHeaderType {
|
||||||
|
return a.from
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Filter) ToV2() *v2acl.HeaderFilter {
|
||||||
|
filter := new(v2acl.HeaderFilter)
|
||||||
|
filter.SetValue(a.value)
|
||||||
|
filter.SetName(a.name)
|
||||||
|
filter.SetMatchType(a.matcher.ToV2())
|
||||||
|
filter.SetHeaderType(a.from.ToV2())
|
||||||
|
|
||||||
|
return filter
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFilterFromV2(filter *v2acl.HeaderFilter) *Filter {
|
||||||
|
f := new(Filter)
|
||||||
|
|
||||||
|
if filter == nil {
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
f.from = FilterHeaderTypeFromV2(filter.GetHeaderType())
|
||||||
|
f.matcher = MatchFromV2(filter.GetMatchType())
|
||||||
|
f.name = filter.GetName()
|
||||||
|
f.value = filter.GetValue()
|
||||||
|
|
||||||
|
return f
|
||||||
|
}
|
127
pkg/acl/eacl/record.go
Normal file
127
pkg/acl/eacl/record.go
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
package eacl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
|
||||||
|
v2acl "github.com/nspcc-dev/neofs-api-go/v2/acl"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// Record of the EACL rule, that defines EACL action, targets for this action,
|
||||||
|
// object service operation and filters for request headers.
|
||||||
|
Record struct {
|
||||||
|
action Action
|
||||||
|
operation Operation
|
||||||
|
filters []Filter
|
||||||
|
targets []Target
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (r Record) Targets() []Target {
|
||||||
|
return r.targets
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Record) Filters() []Filter {
|
||||||
|
return r.filters
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Record) Operation() Operation {
|
||||||
|
return r.operation
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Record) SetOperation(operation Operation) {
|
||||||
|
r.operation = operation
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Record) Action() Action {
|
||||||
|
return r.action
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Record) SetAction(action Action) {
|
||||||
|
r.action = action
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Record) AddTarget(role Role, keys ...ecdsa.PublicKey) {
|
||||||
|
t := Target{
|
||||||
|
role: role,
|
||||||
|
keys: make([]ecdsa.PublicKey, 0, len(keys)),
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range keys {
|
||||||
|
t.keys = append(t.keys, keys[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
r.targets = append(r.targets, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Record) AddFilter(from FilterHeaderType, matcher Match, name, value string) {
|
||||||
|
filter := Filter{
|
||||||
|
from: from,
|
||||||
|
name: name,
|
||||||
|
matcher: matcher,
|
||||||
|
value: value,
|
||||||
|
}
|
||||||
|
|
||||||
|
r.filters = append(r.filters, filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Record) ToV2() *v2acl.Record {
|
||||||
|
targets := make([]*v2acl.Target, 0, len(r.targets))
|
||||||
|
for _, target := range r.targets {
|
||||||
|
targets = append(targets, target.ToV2())
|
||||||
|
}
|
||||||
|
|
||||||
|
filters := make([]*v2acl.HeaderFilter, 0, len(r.filters))
|
||||||
|
for _, filter := range r.filters {
|
||||||
|
filters = append(filters, filter.ToV2())
|
||||||
|
}
|
||||||
|
|
||||||
|
v2 := new(v2acl.Record)
|
||||||
|
|
||||||
|
v2.SetAction(r.action.ToV2())
|
||||||
|
v2.SetOperation(r.operation.ToV2())
|
||||||
|
v2.SetTargets(targets)
|
||||||
|
v2.SetFilters(filters)
|
||||||
|
|
||||||
|
return v2
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRecord() *Record {
|
||||||
|
return new(Record)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateRecord(action Action, operation Operation) *Record {
|
||||||
|
r := NewRecord()
|
||||||
|
r.action = action
|
||||||
|
r.operation = operation
|
||||||
|
r.targets = []Target{}
|
||||||
|
r.filters = []Filter{}
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRecordFromV2(record *v2acl.Record) *Record {
|
||||||
|
r := NewRecord()
|
||||||
|
|
||||||
|
if record == nil {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
r.action = ActionFromV2(record.GetAction())
|
||||||
|
r.operation = OperationFromV2(record.GetOperation())
|
||||||
|
|
||||||
|
v2targets := record.GetTargets()
|
||||||
|
v2filters := record.GetFilters()
|
||||||
|
|
||||||
|
r.targets = make([]Target, 0, len(v2targets))
|
||||||
|
for i := range v2targets {
|
||||||
|
r.targets = append(r.targets, *NewTargetFromV2(v2targets[i]))
|
||||||
|
}
|
||||||
|
|
||||||
|
r.filters = make([]Filter, 0, len(v2filters))
|
||||||
|
for i := range v2filters {
|
||||||
|
r.filters = append(r.filters, *NewFilterFromV2(v2filters[i]))
|
||||||
|
}
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
113
pkg/acl/eacl/table.go
Normal file
113
pkg/acl/eacl/table.go
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
package eacl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neofs-api-go/pkg"
|
||||||
|
"github.com/nspcc-dev/neofs-api-go/pkg/container"
|
||||||
|
v2acl "github.com/nspcc-dev/neofs-api-go/v2/acl"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// Table is a group of EACL records for single container.
|
||||||
|
Table struct {
|
||||||
|
version pkg.Version
|
||||||
|
cid *container.ID
|
||||||
|
records []Record
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t Table) CID() *container.ID {
|
||||||
|
return t.cid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Table) SetCID(cid *container.ID) {
|
||||||
|
t.cid = cid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Table) Version() pkg.Version {
|
||||||
|
return t.version
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Table) SetVersion(version pkg.Version) {
|
||||||
|
t.version = version
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Table) Records() []Record {
|
||||||
|
return t.records
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Table) AddRecord(r *Record) {
|
||||||
|
if r != nil {
|
||||||
|
t.records = append(t.records, *r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Table) ToV2() *v2acl.Table {
|
||||||
|
v2 := new(v2acl.Table)
|
||||||
|
|
||||||
|
if t.cid != nil {
|
||||||
|
v2.SetContainerID(t.cid.ToV2())
|
||||||
|
}
|
||||||
|
|
||||||
|
records := make([]*v2acl.Record, 0, len(t.records))
|
||||||
|
for _, record := range t.records {
|
||||||
|
records = append(records, record.ToV2())
|
||||||
|
}
|
||||||
|
|
||||||
|
v2.SetVersion(t.version.ToV2())
|
||||||
|
v2.SetRecords(records)
|
||||||
|
|
||||||
|
return v2
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTable() *Table {
|
||||||
|
t := new(Table)
|
||||||
|
t.SetVersion(*pkg.SDKVersion())
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateTable(cid container.ID) *Table {
|
||||||
|
t := NewTable()
|
||||||
|
t.SetCID(&cid)
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTableFromV2(table *v2acl.Table) *Table {
|
||||||
|
t := new(Table)
|
||||||
|
|
||||||
|
if table == nil {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// set version
|
||||||
|
if v := table.GetVersion(); v != nil {
|
||||||
|
version := pkg.Version{}
|
||||||
|
version.SetMajor(v.GetMajor())
|
||||||
|
version.SetMinor(v.GetMinor())
|
||||||
|
|
||||||
|
t.SetVersion(version)
|
||||||
|
}
|
||||||
|
|
||||||
|
// set container id
|
||||||
|
if cid := table.GetContainerID(); cid != nil {
|
||||||
|
if t.cid == nil {
|
||||||
|
t.cid = new(container.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
var h [sha256.Size]byte
|
||||||
|
copy(h[:], table.GetContainerID().GetValue())
|
||||||
|
t.cid.SetSHA256(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// set eacl records
|
||||||
|
v2records := table.GetRecords()
|
||||||
|
t.records = make([]Record, 0, len(v2records))
|
||||||
|
for i := range v2records {
|
||||||
|
t.records = append(t.records, *NewRecordFromV2(v2records[i]))
|
||||||
|
}
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
17
pkg/acl/eacl/table_test.go
Normal file
17
pkg/acl/eacl/table_test.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package eacl_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neofs-api-go/pkg/acl/eacl"
|
||||||
|
"github.com/nspcc-dev/neofs-crypto/test"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TextExample(t *testing.T) {
|
||||||
|
record := eacl.CreateRecord(eacl.ActionDeny, eacl.OperationPut)
|
||||||
|
record.AddFilter(eacl.HeaderFromObject, eacl.MatchStringEqual, "filename", "cat.jpg")
|
||||||
|
record.AddTarget(eacl.RoleOthers, test.DecodeKey(1).PublicKey, test.DecodeKey(2).PublicKey)
|
||||||
|
|
||||||
|
table := eacl.NewTable()
|
||||||
|
table.AddRecord(record)
|
||||||
|
}
|
56
pkg/acl/eacl/target.go
Normal file
56
pkg/acl/eacl/target.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
package eacl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
|
||||||
|
v2acl "github.com/nspcc-dev/neofs-api-go/v2/acl"
|
||||||
|
crypto "github.com/nspcc-dev/neofs-crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Target is a group of request senders to match EACL. Defined by role enum
|
||||||
|
// and set of public keys.
|
||||||
|
type Target struct {
|
||||||
|
role Role
|
||||||
|
keys []ecdsa.PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Target) Keys() []ecdsa.PublicKey {
|
||||||
|
return t.keys
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Target) Role() Role {
|
||||||
|
return t.role
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Target) ToV2() *v2acl.Target {
|
||||||
|
keys := make([][]byte, 0, len(t.keys))
|
||||||
|
for i := range t.keys {
|
||||||
|
key := crypto.MarshalPublicKey(&t.keys[i])
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
target := new(v2acl.Target)
|
||||||
|
|
||||||
|
target.SetRole(t.role.ToV2())
|
||||||
|
target.SetKeyList(keys)
|
||||||
|
|
||||||
|
return target
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTargetFromV2(target *v2acl.Target) *Target {
|
||||||
|
t := new(Target)
|
||||||
|
|
||||||
|
if target == nil {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
t.role = RoleFromV2(target.GetRole())
|
||||||
|
v2keys := target.GetKeyList()
|
||||||
|
t.keys = make([]ecdsa.PublicKey, 0, len(v2keys))
|
||||||
|
for i := range v2keys {
|
||||||
|
key := crypto.UnmarshalPublicKey(v2keys[i])
|
||||||
|
t.keys = append(t.keys, *key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
Loading…
Reference in a new issue