package types import ( "errors" "math/big" "sync" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/p2p/enode" lru "github.com/hashicorp/golang-lru" ) type AccessType uint8 const ( ReadOnly AccessType = iota Transact ContractDeploy FullAccess ) type OrgStatus uint8 const ( OrgPendingApproval OrgStatus = iota + 1 OrgApproved OrgPendingSuspension OrgSuspended ) type OrgInfo struct { OrgId string `json:"orgId"` FullOrgId string `json:"fullOrgId"` ParentOrgId string `json:"parentOrgId"` UltimateParent string `json:"ultimateParent"` Level *big.Int `json:"level"` SubOrgList []string `json:"subOrgList"` Status OrgStatus `json:"status"` } type NodeStatus uint8 const ( NodePendingApproval NodeStatus = iota + 1 NodeApproved NodeDeactivated NodeBlackListed NodeRecoveryInitiated ) type AcctStatus uint8 const ( AcctPendingApproval AcctStatus = iota + 1 AcctActive AcctInactive AcctSuspended AcctBlacklisted AdminRevoked AcctRecoveryInitiated AcctRecoveryCompleted ) type NodeInfo struct { OrgId string `json:"orgId"` Url string `json:"url"` Status NodeStatus `json:"status"` } type RoleInfo struct { OrgId string `json:"orgId"` RoleId string `json:"roleId"` IsVoter bool `json:"isVoter"` IsAdmin bool `json:"isAdmin"` Access AccessType `json:"access"` Active bool `json:"active"` } type AccountInfo struct { OrgId string `json:"orgId"` RoleId string `json:"roleId"` AcctId common.Address `json:"acctId"` IsOrgAdmin bool `json:"isOrgAdmin"` Status AcctStatus `json:"status"` } type OrgDetailInfo struct { NodeList []NodeInfo `json:"nodeList"` RoleList []RoleInfo `json:"roleList"` AcctList []AccountInfo `json:"acctList"` SubOrgList []string `json:"subOrgList"` } // permission config for bootstrapping type PermissionConfig struct { UpgrdAddress common.Address `json:"upgrdableAddress"` InterfAddress common.Address `json:"interfaceAddress"` ImplAddress common.Address `json:"implAddress"` NodeAddress common.Address `json:"nodeMgrAddress"` AccountAddress common.Address `json:"accountMgrAddress"` RoleAddress common.Address `json:"roleMgrAddress"` VoterAddress common.Address `json:"voterMgrAddress"` OrgAddress common.Address `json:"orgMgrAddress"` NwAdminOrg string `json:"nwAdminOrg"` NwAdminRole string `json:"nwAdminRole"` OrgAdminRole string `json:"orgAdminRole"` Accounts []common.Address `json:"accounts"` //initial list of account that need full access SubOrgDepth *big.Int `json:"subOrgDepth"` SubOrgBreadth *big.Int `json:"subOrgBreadth"` } var ( ErrNotNetworkAdmin = errors.New("Operation can be performed by network admin only. Account not a network admin.") ErrNotOrgAdmin = errors.New("Operation can be performed by org admin only. Account not a org admin.") ErrNodePresent = errors.New("EnodeId already part of network.") ErrInvalidNode = errors.New("Invalid enode id") ErrInvalidAccount = errors.New("Invalid account id") ErrOrgExists = errors.New("Org already exist") ErrPendingApprovals = errors.New("Pending approvals for the organization. Approve first") ErrNothingToApprove = errors.New("Nothing to approve") ErrOpNotAllowed = errors.New("Operation not allowed") ErrNodeOrgMismatch = errors.New("Enode id passed does not belong to the organization.") ErrBlacklistedNode = errors.New("Blacklisted node. Operation not allowed") ErrBlacklistedAccount = errors.New("Blacklisted account. Operation not allowed") ErrAccountOrgAdmin = errors.New("Account already org admin for the org") ErrOrgAdminExists = errors.New("Org admin exist for the org") ErrAccountInUse = errors.New("Account already in use in another organization") ErrRoleExists = errors.New("Role exist for the org") ErrRoleActive = errors.New("Accounts linked to the role. Cannot be removed") ErrAdminRoles = errors.New("Admin role cannot be removed") ErrInvalidOrgName = errors.New("Org id cannot contain special characters") ErrInvalidParentOrg = errors.New("Invalid parent org id") ErrAccountNotThere = errors.New("Account does not exist") ErrOrgNotOwner = errors.New("Account does not belong to this org") ErrMaxDepth = errors.New("Max depth for sub orgs reached") ErrMaxBreadth = errors.New("Max breadth for sub orgs reached") ErrNodeDoesNotExists = errors.New("Node does not exist") ErrOrgDoesNotExists = errors.New("Org does not exist") ErrInactiveRole = errors.New("Role is already inactive") ErrInvalidRole = errors.New("Invalid role") ErrInvalidInput = errors.New("Invalid input") ErrNotMasterOrg = errors.New("Org is not a master org") ) var syncStarted = false var DefaultAccess = FullAccess var QIP714BlockReached = false var networkAdminRole string var orgAdminRole string var ( OrgInfoMap *OrgCache NodeInfoMap *NodeCache RoleInfoMap *RoleCache AcctInfoMap *AcctCache ) type OrgKey struct { OrgId string } type OrgCache struct { c *lru.Cache mux sync.Mutex evicted bool populateCacheFunc func(orgId string) (*OrgInfo, error) } func (o *OrgCache) PopulateCacheFunc(cf func(string) (*OrgInfo, error)) { o.populateCacheFunc = cf } func NewOrgCache(cacheSize int) *OrgCache { orgCache := OrgCache{evicted: false} onEvictedFunc := func(k interface{}, v interface{}) { orgCache.evicted = true } orgCache.c, _ = lru.NewWithEvict(cacheSize, onEvictedFunc) return &orgCache } type RoleKey struct { OrgId string RoleId string } type RoleCache struct { c *lru.Cache evicted bool populateCacheFunc func(*RoleKey) (*RoleInfo, error) } func (r *RoleCache) PopulateCacheFunc(cf func(*RoleKey) (*RoleInfo, error)) { r.populateCacheFunc = cf } func NewRoleCache(cacheSize int) *RoleCache { roleCache := RoleCache{evicted: false} onEvictedFunc := func(k interface{}, v interface{}) { roleCache.evicted = true } roleCache.c, _ = lru.NewWithEvict(cacheSize, onEvictedFunc) return &roleCache } type NodeKey struct { OrgId string Url string } type NodeCache struct { c *lru.Cache evicted bool populateCacheFunc func(string) (*NodeInfo, error) populateAndValidateFunc func(string, string) bool } func (n *NodeCache) PopulateValidateFunc(cf func(string, string) bool) { n.populateAndValidateFunc = cf } func (n *NodeCache) PopulateCacheFunc(cf func(string) (*NodeInfo, error)) { n.populateCacheFunc = cf } func NewNodeCache(cacheSize int) *NodeCache { nodeCache := NodeCache{evicted: false} onEvictedFunc := func(k interface{}, v interface{}) { nodeCache.evicted = true } nodeCache.c, _ = lru.NewWithEvict(cacheSize, onEvictedFunc) return &nodeCache } type AccountKey struct { AcctId common.Address } type AcctCache struct { c *lru.Cache evicted bool populateCacheFunc func(account common.Address) (*AccountInfo, error) } func (a *AcctCache) PopulateCacheFunc(cf func(common.Address) (*AccountInfo, error)) { a.populateCacheFunc = cf } func NewAcctCache(cacheSize int) *AcctCache { acctCache := AcctCache{evicted: false} onEvictedFunc := func(k interface{}, v interface{}) { acctCache.evicted = true } acctCache.c, _ = lru.NewWithEvict(cacheSize, onEvictedFunc) return &acctCache } func (pc *PermissionConfig) IsEmpty() bool { return pc.InterfAddress == common.HexToAddress("0x0") } func SetSyncStatus() { syncStarted = true } func GetSyncStatus() bool { return syncStarted } // sets the default access to Readonly upon QIP714Blokc func SetDefaultAccess() { DefaultAccess = ReadOnly QIP714BlockReached = true } // sets default access to readonly and initializes the values for // network admin role and org admin role func SetDefaults(nwRoleId, oaRoleId string) { networkAdminRole = nwRoleId orgAdminRole = oaRoleId } func GetDefaults() (string, string, AccessType) { return networkAdminRole, orgAdminRole, DefaultAccess } func (o *OrgCache) UpsertOrg(orgId, parentOrg, ultimateParent string, level *big.Int, status OrgStatus) { defer o.mux.Unlock() o.mux.Lock() var key OrgKey if parentOrg == "" { key = OrgKey{OrgId: orgId} } else { key = OrgKey{OrgId: parentOrg + "." + orgId} pkey := OrgKey{OrgId: parentOrg} if ent, ok := o.c.Get(pkey); ok { porg := ent.(*OrgInfo) if !containsKey(porg.SubOrgList, key.OrgId) { porg.SubOrgList = append(porg.SubOrgList, key.OrgId) o.c.Add(pkey, porg) } } } norg := &OrgInfo{orgId, key.OrgId, parentOrg, ultimateParent, level, nil, status} o.c.Add(key, norg) } func (o *OrgCache) UpsertOrgWithSubOrgList(orgRec *OrgInfo) { var key OrgKey if orgRec.ParentOrgId == "" { key = OrgKey{OrgId: orgRec.OrgId} } else { key = OrgKey{OrgId: orgRec.ParentOrgId + "." + orgRec.OrgId} } orgRec.FullOrgId = key.OrgId o.c.Add(key, orgRec) } func containsKey(s []string, e string) bool { for _, a := range s { if a == e { return true } } return false } func (o *OrgCache) GetOrg(orgId string) (*OrgInfo, error) { key := OrgKey{OrgId: orgId} if ent, ok := o.c.Get(key); ok { return ent.(*OrgInfo), nil } // check if the org cache is evicted. if yes we need // fetch the record from the contract if o.evicted { // call cache population function to populate from contract orgRec, err := o.populateCacheFunc(orgId) if err != nil { return nil, err } // insert the received record into cache o.UpsertOrgWithSubOrgList(orgRec) //return the record return orgRec, nil } return nil, ErrOrgDoesNotExists } func (o *OrgCache) GetOrgList() []OrgInfo { olist := make([]OrgInfo, len(o.c.Keys())) for i, k := range o.c.Keys() { v, _ := o.c.Get(k) vp := v.(*OrgInfo) olist[i] = *vp } return olist } func (n *NodeCache) UpsertNode(orgId string, url string, status NodeStatus) { key := NodeKey{OrgId: orgId, Url: url} n.c.Add(key, &NodeInfo{orgId, url, status}) } func (n *NodeCache) GetNodeByUrl(url string) (*NodeInfo, error) { for _, k := range n.c.Keys() { ent := k.(NodeKey) if ent.Url == url { v, _ := n.c.Get(ent) return v.(*NodeInfo), nil } } // check if the node cache is evicted. if yes we need // fetch the record from the contract if n.evicted { // call cache population function to populate from contract nodeRec, err := n.populateCacheFunc(url) if err != nil { return nil, err } // insert the received record into cache n.UpsertNode(nodeRec.OrgId, nodeRec.Url, nodeRec.Status) //return the record return nodeRec, err } return nil, ErrNodeDoesNotExists } func (n *NodeCache) GetNodeList() []NodeInfo { olist := make([]NodeInfo, len(n.c.Keys())) for i, k := range n.c.Keys() { v, _ := n.c.Get(k) vp := v.(*NodeInfo) olist[i] = *vp } return olist } func (a *AcctCache) UpsertAccount(orgId string, role string, acct common.Address, orgAdmin bool, status AcctStatus) { key := AccountKey{acct} a.c.Add(key, &AccountInfo{orgId, role, acct, orgAdmin, status}) } func (a *AcctCache) GetAccount(acct common.Address) (*AccountInfo, error) { if v, ok := a.c.Get(AccountKey{acct}); ok { return v.(*AccountInfo), nil } // check if the account cache is evicted. if yes we need // fetch the record from the contract if a.evicted { // call function to populate cache with the record acctRec, err := a.populateCacheFunc(acct) // insert the received record into cache if err != nil { return nil, err } a.UpsertAccount(acctRec.OrgId, acctRec.RoleId, acctRec.AcctId, acctRec.IsOrgAdmin, acctRec.Status) //return the record return acctRec, nil } return nil, nil } func (a *AcctCache) GetAcctList() []AccountInfo { alist := make([]AccountInfo, len(a.c.Keys())) for i, k := range a.c.Keys() { v, _ := a.c.Get(k) vp := v.(*AccountInfo) alist[i] = *vp } return alist } func (a *AcctCache) GetAcctListOrg(orgId string) []AccountInfo { var alist []AccountInfo for _, k := range a.c.Keys() { v, _ := a.c.Get(k) vp := v.(*AccountInfo) if vp.OrgId == orgId { alist = append(alist, *vp) } } return alist } func (a *AcctCache) GetAcctListRole(orgId, roleId string) []AccountInfo { var alist []AccountInfo for _, k := range a.c.Keys() { v, _ := a.c.Get(k) vp := v.(*AccountInfo) orgRec, err := OrgInfoMap.GetOrg(vp.OrgId) if err != nil { return nil } if vp.RoleId == roleId && (vp.OrgId == orgId || (orgRec != nil && orgRec.UltimateParent == orgId)) { alist = append(alist, *vp) } } return alist } func (r *RoleCache) UpsertRole(orgId string, role string, voter bool, admin bool, access AccessType, active bool) { key := RoleKey{orgId, role} r.c.Add(key, &RoleInfo{orgId, role, voter, admin, access, active}) } func (r *RoleCache) GetRole(orgId string, roleId string) (*RoleInfo, error) { key := RoleKey{OrgId: orgId, RoleId: roleId} if ent, ok := r.c.Get(key); ok { return ent.(*RoleInfo), nil } // check if the role cache is evicted. if yes we need // fetch the record from the contract if r.evicted { // call cache population function to populate from contract roleRec, err := r.populateCacheFunc(&RoleKey{RoleId: roleId, OrgId: orgId}) if err != nil { return nil, err } // insert the received record into cache r.UpsertRole(roleRec.OrgId, roleRec.RoleId, roleRec.IsVoter, roleRec.IsAdmin, roleRec.Access, roleRec.Active) //return the record return roleRec, nil } return nil, ErrInvalidRole } func (r *RoleCache) GetRoleList() []RoleInfo { rlist := make([]RoleInfo, len(r.c.Keys())) for i, k := range r.c.Keys() { v, _ := r.c.Get(k) vp := v.(*RoleInfo) rlist[i] = *vp } return rlist } // Returns the access type for an account. If not found returns // default access func GetAcctAccess(acctId common.Address) AccessType { //if we have not reached QIP714 block return default access //which will be full access if !QIP714BlockReached { return DefaultAccess } // check if the org status is fine to do the transaction a, _ := AcctInfoMap.GetAccount(acctId) if a != nil && a.Status == AcctActive { // get the org details and ultimate org details. check org status // if the org is not approved or pending suspension o, _ := OrgInfoMap.GetOrg(a.OrgId) if o != nil && (o.Status == OrgApproved || o.Status == OrgPendingSuspension) { u, _ := OrgInfoMap.GetOrg(o.UltimateParent) if u != nil && (u.Status == OrgApproved || u.Status == OrgPendingSuspension) { if a.RoleId == networkAdminRole || a.RoleId == orgAdminRole { return FullAccess } r, _ := RoleInfoMap.GetRole(a.OrgId, a.RoleId) if r != nil && r.Active { return r.Access } r, _ = RoleInfoMap.GetRole(o.UltimateParent, a.RoleId) if r != nil && r.Active { return r.Access } } } } return DefaultAccess } // validates if the account can transact from the current node func ValidateNodeForTxn(hexnodeId string, from common.Address) bool { if !QIP714BlockReached || hexnodeId == "" { return true } passedEnodeId, err := enode.ParseV4(hexnodeId) if err != nil { return false } ac, _ := AcctInfoMap.GetAccount(from) if ac == nil { return true } acOrgRec, err := OrgInfoMap.GetOrg(ac.OrgId) if err != nil { return false } // scan through the node list and validate for _, n := range NodeInfoMap.GetNodeList() { orgRec, err := OrgInfoMap.GetOrg(n.OrgId) if err != nil { return false } if orgRec.UltimateParent == acOrgRec.UltimateParent { recEnodeId, _ := enode.ParseV4(n.Url) if recEnodeId.ID() == passedEnodeId.ID() && n.Status == NodeApproved { return true } } } if NodeInfoMap.evicted { return NodeInfoMap.populateAndValidateFunc(hexnodeId, acOrgRec.UltimateParent) } return false }