Add HighestRole & ordered roles to permission response
This commit adds HighestRole & a list of permissions in order of importance to the CheckPermissionToJoinResponse. This simplify client code so that it doesn't need to be calculated on the client.
This commit is contained in:
parent
8fa31df498
commit
daef5c56e2
|
@ -0,0 +1,151 @@
|
||||||
|
package communities
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
gethcommon "github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/status-im/status-go/protocol/protobuf"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CheckPermissionsResponse struct {
|
||||||
|
Satisfied bool `json:"satisfied"`
|
||||||
|
Permissions map[string]*PermissionTokenCriteriaResult `json:"permissions"`
|
||||||
|
ValidCombinations []*AccountChainIDsCombination `json:"validCombinations"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CheckPermissionToJoinResponse = CheckPermissionsResponse
|
||||||
|
|
||||||
|
type HighestRoleResponse struct {
|
||||||
|
Role protobuf.CommunityTokenPermission_Type `json:"type"`
|
||||||
|
Satisfied bool `json:"satisfied"`
|
||||||
|
Criteria []*PermissionTokenCriteriaResult `json:"criteria"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var joiningRoleOrders = map[protobuf.CommunityTokenPermission_Type]int{
|
||||||
|
protobuf.CommunityTokenPermission_BECOME_MEMBER: 1,
|
||||||
|
protobuf.CommunityTokenPermission_BECOME_ADMIN: 2,
|
||||||
|
protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER: 3,
|
||||||
|
protobuf.CommunityTokenPermission_BECOME_TOKEN_OWNER: 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
type ByRoleDesc []*HighestRoleResponse
|
||||||
|
|
||||||
|
func (a ByRoleDesc) Len() int { return len(a) }
|
||||||
|
func (a ByRoleDesc) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||||
|
func (a ByRoleDesc) Less(i, j int) bool {
|
||||||
|
return joiningRoleOrders[a[i].Role] > joiningRoleOrders[a[j].Role]
|
||||||
|
}
|
||||||
|
|
||||||
|
type rolesAndHighestRole struct {
|
||||||
|
Roles []*HighestRoleResponse
|
||||||
|
HighestRole *HighestRoleResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
func calculateRolesAndHighestRole(permissions map[string]*PermissionTokenCriteriaResult) *rolesAndHighestRole {
|
||||||
|
item := &rolesAndHighestRole{}
|
||||||
|
byRoleMap := make(map[protobuf.CommunityTokenPermission_Type]*HighestRoleResponse)
|
||||||
|
for _, p := range permissions {
|
||||||
|
if joiningRoleOrders[p.Role] == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if byRoleMap[p.Role] == nil {
|
||||||
|
byRoleMap[p.Role] = &HighestRoleResponse{
|
||||||
|
Role: p.Role,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
satisfied := true
|
||||||
|
for _, tr := range p.TokenRequirements {
|
||||||
|
if !tr.Satisfied {
|
||||||
|
satisfied = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if satisfied {
|
||||||
|
byRoleMap[p.Role].Satisfied = true
|
||||||
|
// we prepend
|
||||||
|
byRoleMap[p.Role].Criteria = append([]*PermissionTokenCriteriaResult{p}, byRoleMap[p.Role].Criteria...)
|
||||||
|
} else {
|
||||||
|
// we append then
|
||||||
|
byRoleMap[p.Role].Criteria = append(byRoleMap[p.Role].Criteria, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if byRoleMap[protobuf.CommunityTokenPermission_BECOME_MEMBER] == nil {
|
||||||
|
byRoleMap[protobuf.CommunityTokenPermission_BECOME_MEMBER] = &HighestRoleResponse{Satisfied: true, Role: protobuf.CommunityTokenPermission_BECOME_MEMBER}
|
||||||
|
}
|
||||||
|
for _, p := range byRoleMap {
|
||||||
|
item.Roles = append(item.Roles, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Sort(ByRoleDesc(item.Roles))
|
||||||
|
for _, r := range item.Roles {
|
||||||
|
if r.Satisfied {
|
||||||
|
item.HighestRole = r
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CheckPermissionsResponse) MarshalJSON() ([]byte, error) {
|
||||||
|
type CheckPermissionsTypeAlias struct {
|
||||||
|
Satisfied bool `json:"satisfied"`
|
||||||
|
Permissions map[string]*PermissionTokenCriteriaResult `json:"permissions"`
|
||||||
|
ValidCombinations []*AccountChainIDsCombination `json:"validCombinations"`
|
||||||
|
Roles []*HighestRoleResponse `json:"roles"`
|
||||||
|
HighestRole *HighestRoleResponse `json:"highestRole"`
|
||||||
|
}
|
||||||
|
c.calculateSatisfied()
|
||||||
|
item := &CheckPermissionsTypeAlias{
|
||||||
|
Satisfied: c.Satisfied,
|
||||||
|
Permissions: c.Permissions,
|
||||||
|
ValidCombinations: c.ValidCombinations,
|
||||||
|
}
|
||||||
|
rolesAndHighestRole := calculateRolesAndHighestRole(c.Permissions)
|
||||||
|
|
||||||
|
item.Roles = rolesAndHighestRole.Roles
|
||||||
|
item.HighestRole = rolesAndHighestRole.HighestRole
|
||||||
|
return json.Marshal(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
type TokenRequirementResponse struct {
|
||||||
|
Satisfied bool `json:"satisfied"`
|
||||||
|
TokenCriteria *protobuf.TokenCriteria `json:"criteria"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PermissionTokenCriteriaResult struct {
|
||||||
|
Role protobuf.CommunityTokenPermission_Type `json:"roles"`
|
||||||
|
TokenRequirements []TokenRequirementResponse `json:"tokenRequirement"`
|
||||||
|
Criteria []bool `json:"criteria"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AccountChainIDsCombination struct {
|
||||||
|
Address gethcommon.Address `json:"address"`
|
||||||
|
ChainIDs []uint64 `json:"chainIds"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CheckPermissionsResponse) calculateSatisfied() {
|
||||||
|
if len(c.Permissions) == 0 {
|
||||||
|
c.Satisfied = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Satisfied = false
|
||||||
|
for _, p := range c.Permissions {
|
||||||
|
satisfied := true
|
||||||
|
for _, criteria := range p.Criteria {
|
||||||
|
if !criteria {
|
||||||
|
satisfied = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if satisfied {
|
||||||
|
c.Satisfied = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
package communities
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/protocol/protobuf"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCalculateRolesAndHighestRole(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
permissions map[string]*PermissionTokenCriteriaResult
|
||||||
|
expectedRolesOrder []protobuf.CommunityTokenPermission_Type
|
||||||
|
expectedHighestRole protobuf.CommunityTokenPermission_Type
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Basic scenario with multiple permissions",
|
||||||
|
permissions: map[string]*PermissionTokenCriteriaResult{
|
||||||
|
"1": {
|
||||||
|
Role: protobuf.CommunityTokenPermission_BECOME_MEMBER,
|
||||||
|
TokenRequirements: []TokenRequirementResponse{
|
||||||
|
{Satisfied: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"2": {
|
||||||
|
Role: protobuf.CommunityTokenPermission_BECOME_ADMIN,
|
||||||
|
TokenRequirements: []TokenRequirementResponse{
|
||||||
|
{Satisfied: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"3": {
|
||||||
|
Role: protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER,
|
||||||
|
TokenRequirements: []TokenRequirementResponse{
|
||||||
|
{Satisfied: false},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"4": {
|
||||||
|
Role: protobuf.CommunityTokenPermission_BECOME_TOKEN_OWNER,
|
||||||
|
TokenRequirements: []TokenRequirementResponse{
|
||||||
|
{Satisfied: false},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedRolesOrder: []protobuf.CommunityTokenPermission_Type{protobuf.CommunityTokenPermission_BECOME_TOKEN_OWNER, protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER, protobuf.CommunityTokenPermission_BECOME_ADMIN, protobuf.CommunityTokenPermission_BECOME_MEMBER},
|
||||||
|
expectedHighestRole: protobuf.CommunityTokenPermission_BECOME_ADMIN,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "No member permission created",
|
||||||
|
permissions: map[string]*PermissionTokenCriteriaResult{
|
||||||
|
"2": {
|
||||||
|
Role: protobuf.CommunityTokenPermission_BECOME_ADMIN,
|
||||||
|
TokenRequirements: []TokenRequirementResponse{
|
||||||
|
{Satisfied: false},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"3": {
|
||||||
|
Role: protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER,
|
||||||
|
TokenRequirements: []TokenRequirementResponse{
|
||||||
|
{Satisfied: false},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"4": {
|
||||||
|
Role: protobuf.CommunityTokenPermission_BECOME_TOKEN_OWNER,
|
||||||
|
TokenRequirements: []TokenRequirementResponse{
|
||||||
|
{Satisfied: false},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedRolesOrder: []protobuf.CommunityTokenPermission_Type{protobuf.CommunityTokenPermission_BECOME_TOKEN_OWNER, protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER, protobuf.CommunityTokenPermission_BECOME_ADMIN, protobuf.CommunityTokenPermission_BECOME_MEMBER},
|
||||||
|
expectedHighestRole: protobuf.CommunityTokenPermission_BECOME_MEMBER,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no permission satisfied",
|
||||||
|
permissions: map[string]*PermissionTokenCriteriaResult{
|
||||||
|
"1": {
|
||||||
|
Role: protobuf.CommunityTokenPermission_BECOME_MEMBER,
|
||||||
|
TokenRequirements: []TokenRequirementResponse{
|
||||||
|
{Satisfied: false},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"2": {
|
||||||
|
Role: protobuf.CommunityTokenPermission_BECOME_ADMIN,
|
||||||
|
TokenRequirements: []TokenRequirementResponse{
|
||||||
|
{Satisfied: false},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"3": {
|
||||||
|
Role: protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER,
|
||||||
|
TokenRequirements: []TokenRequirementResponse{
|
||||||
|
{Satisfied: false},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"4": {
|
||||||
|
Role: protobuf.CommunityTokenPermission_BECOME_TOKEN_OWNER,
|
||||||
|
TokenRequirements: []TokenRequirementResponse{
|
||||||
|
{Satisfied: false},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedRolesOrder: []protobuf.CommunityTokenPermission_Type{protobuf.CommunityTokenPermission_BECOME_TOKEN_OWNER, protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER, protobuf.CommunityTokenPermission_BECOME_ADMIN, protobuf.CommunityTokenPermission_BECOME_MEMBER},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
result := calculateRolesAndHighestRole(tc.permissions)
|
||||||
|
var actualOrder []protobuf.CommunityTokenPermission_Type
|
||||||
|
for _, r := range result.Roles {
|
||||||
|
actualOrder = append(actualOrder, r.Role)
|
||||||
|
}
|
||||||
|
if tc.expectedHighestRole == 0 {
|
||||||
|
assert.Nil(t, result.HighestRole)
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, tc.expectedHighestRole, result.HighestRole.Role, "Highest role is not calculated as expected")
|
||||||
|
}
|
||||||
|
assert.Equal(t, tc.expectedRolesOrder, actualOrder, "Roles are not calculated as expected")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -2602,45 +2602,6 @@ func (m *Manager) HandleCommunityEditSharedAddresses(signer *ecdsa.PublicKey, re
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type CheckPermissionsResponse struct {
|
|
||||||
Satisfied bool `json:"satisfied"`
|
|
||||||
Permissions map[string]*PermissionTokenCriteriaResult `json:"permissions"`
|
|
||||||
ValidCombinations []*AccountChainIDsCombination `json:"validCombinations"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CheckPermissionToJoinResponse = CheckPermissionsResponse
|
|
||||||
|
|
||||||
type PermissionTokenCriteriaResult struct {
|
|
||||||
Criteria []bool `json:"criteria"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type AccountChainIDsCombination struct {
|
|
||||||
Address gethcommon.Address `json:"address"`
|
|
||||||
ChainIDs []uint64 `json:"chainIds"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CheckPermissionsResponse) calculateSatisfied() {
|
|
||||||
if len(c.Permissions) == 0 {
|
|
||||||
c.Satisfied = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Satisfied = false
|
|
||||||
for _, p := range c.Permissions {
|
|
||||||
satisfied := true
|
|
||||||
for _, criteria := range p.Criteria {
|
|
||||||
if !criteria {
|
|
||||||
satisfied = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if satisfied {
|
|
||||||
c.Satisfied = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func calculateChainIDsSet(accountsAndChainIDs []*AccountChainIDsCombination, requirementsChainIDs map[uint64]bool) []uint64 {
|
func calculateChainIDsSet(accountsAndChainIDs []*AccountChainIDsCombination, requirementsChainIDs map[uint64]bool) []uint64 {
|
||||||
|
|
||||||
revealedAccountsChainIDs := make([]uint64, 0)
|
revealedAccountsChainIDs := make([]uint64, 0)
|
||||||
|
|
|
@ -263,7 +263,7 @@ func (p *DefaultPermissionChecker) CheckPermissions(permissions []*CommunityToke
|
||||||
for _, tokenPermission := range permissions {
|
for _, tokenPermission := range permissions {
|
||||||
|
|
||||||
permissionRequirementsMet := true
|
permissionRequirementsMet := true
|
||||||
response.Permissions[tokenPermission.Id] = &PermissionTokenCriteriaResult{}
|
response.Permissions[tokenPermission.Id] = &PermissionTokenCriteriaResult{Role: tokenPermission.Type}
|
||||||
|
|
||||||
// There can be multiple token requirements per permission.
|
// There can be multiple token requirements per permission.
|
||||||
// If only one is not met, the entire permission is marked
|
// If only one is not met, the entire permission is marked
|
||||||
|
@ -271,9 +271,12 @@ func (p *DefaultPermissionChecker) CheckPermissions(permissions []*CommunityToke
|
||||||
for _, tokenRequirement := range tokenPermission.TokenCriteria {
|
for _, tokenRequirement := range tokenPermission.TokenCriteria {
|
||||||
|
|
||||||
tokenRequirementMet := false
|
tokenRequirementMet := false
|
||||||
|
tokenRequirementResponse := TokenRequirementResponse{TokenCriteria: tokenRequirement}
|
||||||
|
|
||||||
if tokenRequirement.Type == protobuf.CommunityTokenType_ERC721 {
|
if tokenRequirement.Type == protobuf.CommunityTokenType_ERC721 {
|
||||||
if len(ownedERC721Tokens) == 0 {
|
if len(ownedERC721Tokens) == 0 {
|
||||||
|
|
||||||
|
response.Permissions[tokenPermission.Id].TokenRequirements = append(response.Permissions[tokenPermission.Id].TokenRequirements, tokenRequirementResponse)
|
||||||
response.Permissions[tokenPermission.Id].Criteria = append(response.Permissions[tokenPermission.Id].Criteria, false)
|
response.Permissions[tokenPermission.Id].Criteria = append(response.Permissions[tokenPermission.Id].Criteria, false)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -321,6 +324,7 @@ func (p *DefaultPermissionChecker) CheckPermissions(permissions []*CommunityToke
|
||||||
}
|
}
|
||||||
} else if tokenRequirement.Type == protobuf.CommunityTokenType_ERC20 {
|
} else if tokenRequirement.Type == protobuf.CommunityTokenType_ERC20 {
|
||||||
if len(ownedERC20TokenBalances) == 0 {
|
if len(ownedERC20TokenBalances) == 0 {
|
||||||
|
response.Permissions[tokenPermission.Id].TokenRequirements = append(response.Permissions[tokenPermission.Id].TokenRequirements, tokenRequirementResponse)
|
||||||
response.Permissions[tokenPermission.Id].Criteria = append(response.Permissions[tokenPermission.Id].Criteria, false)
|
response.Permissions[tokenPermission.Id].Criteria = append(response.Permissions[tokenPermission.Id].Criteria, false)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -406,6 +410,9 @@ func (p *DefaultPermissionChecker) CheckPermissions(permissions []*CommunityToke
|
||||||
if !tokenRequirementMet {
|
if !tokenRequirementMet {
|
||||||
permissionRequirementsMet = false
|
permissionRequirementsMet = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tokenRequirementResponse.Satisfied = tokenRequirementMet
|
||||||
|
response.Permissions[tokenPermission.Id].TokenRequirements = append(response.Permissions[tokenPermission.Id].TokenRequirements, tokenRequirementResponse)
|
||||||
response.Permissions[tokenPermission.Id].Criteria = append(response.Permissions[tokenPermission.Id].Criteria, tokenRequirementMet)
|
response.Permissions[tokenPermission.Id].Criteria = append(response.Permissions[tokenPermission.Id].Criteria, tokenRequirementMet)
|
||||||
}
|
}
|
||||||
// multiple permissions are treated as logical OR, meaning
|
// multiple permissions are treated as logical OR, meaning
|
||||||
|
|
Loading…
Reference in New Issue