• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

vocdoni / saas-backend / 16341243622

17 Jul 2025 09:20AM UTC coverage: 56.89% (+0.8%) from 56.107%
16341243622

push

github

altergui
add TestStripeAPI and ZeroAddressValidation tests

5008 of 8803 relevant lines covered (56.89%)

25.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

59.38
/subscriptions/subscriptions.go
1
// Package subscriptions provides functionality for managing organization subscriptions
2
// and enforcing permissions based on subscription plans.
3
package subscriptions
4

5
import (
6
        "fmt"
7

8
        "github.com/ethereum/go-ethereum/common"
9
        "github.com/vocdoni/saas-backend/db"
10
        "go.vocdoni.io/proto/build/go/models"
11
)
12

13
// Config holds the configuration for the subscriptions service.
14
// It includes a reference to the MongoDB storage used by the service.
15
type Config struct {
16
        DB *db.MongoStorage
17
}
18

19
// DBPermission represents the permissions that an organization can have based on its subscription.
20
type DBPermission int
21

22
const (
23
        // InviteUser represents the permission to invite new users to an organization.
24
        InviteUser DBPermission = iota
25
        // DeleteUser represents the permission to remove users from an organization.
26
        DeleteUser
27
        // CreateSubOrg represents the permission to create sub-organizations.
28
        CreateSubOrg
29
        // CreateDraft represents the permission to create draft processes.
30
        CreateDraft
31
)
32

33
// String returns the string representation of the DBPermission.
34
func (p DBPermission) String() string {
×
35
        switch p {
×
36
        case InviteUser:
×
37
                return "InviteUser"
×
38
        case DeleteUser:
×
39
                return "DeleteUser"
×
40
        case CreateSubOrg:
×
41
                return "CreateSubOrg"
×
42
        case CreateDraft:
×
43
                return "CreateDraft"
×
44
        default:
×
45
                return "Unknown"
×
46
        }
47
}
48

49
// DBInterface defines the database methods required by the Subscriptions service
50
type DBInterface interface {
51
        Plan(id uint64) (*db.Plan, error)
52
        UserByEmail(email string) (*db.User, error)
53
        Organization(address common.Address) (*db.Organization, error)
54
        OrganizationWithParent(address common.Address) (*db.Organization, *db.Organization, error)
55
}
56

57
// Subscriptions is the service that manages the organization permissions based on
58
// the subscription plans.
59
type Subscriptions struct {
60
        db DBInterface
61
}
62

63
// New creates a new Subscriptions service with the given configuration.
64
func New(conf *Config) *Subscriptions {
1✔
65
        if conf == nil {
1✔
66
                return nil
×
67
        }
×
68
        return &Subscriptions{
1✔
69
                db: conf.DB,
1✔
70
        }
1✔
71
}
72

73
// hasElectionMetadataPermissions checks if the organization has permission to create an election with the given metadata.
74
func hasElectionMetadataPermissions(process *models.NewProcessTx, plan *db.Plan) (bool, error) {
1✔
75
        // check ANONYMOUS
1✔
76
        if process.Process.EnvelopeType.Anonymous && !plan.Features.Anonymous {
1✔
77
                return false, fmt.Errorf("anonymous elections are not allowed")
×
78
        }
×
79

80
        // check WEIGHTED
81
        if process.Process.EnvelopeType.CostFromWeight && !plan.VotingTypes.Weighted {
1✔
82
                return false, fmt.Errorf("weighted elections are not allowed")
×
83
        }
×
84

85
        // check VOTE OVERWRITE
86
        if process.Process.VoteOptions.MaxVoteOverwrites > 0 && !plan.Features.Overwrite {
1✔
87
                return false, fmt.Errorf("vote overwrites are not allowed")
×
88
        }
×
89

90
        // check PROCESS DURATION
91
        duration := plan.Organization.MaxDuration * 24 * 60 * 60
1✔
92
        if process.Process.Duration > uint32(duration) {
1✔
93
                return false, fmt.Errorf("duration is greater than the allowed")
×
94
        }
×
95

96
        // TODO:future check if the election voting type is supported by the plan
97
        // TODO:future check if the streamURL is used and allowed by the plan
98

99
        return true, nil
1✔
100
}
101

102
// HasTxPermission checks if the organization has permission to perform the given transaction.
103
func (p *Subscriptions) HasTxPermission(
104
        tx *models.Tx,
105
        txType models.TxType,
106
        org *db.Organization,
107
        user *db.User,
108
) (bool, error) {
6✔
109
        if org == nil {
7✔
110
                return false, fmt.Errorf("organization is nil")
1✔
111
        }
1✔
112

113
        // Check if the organization has a subscription
114
        if org.Subscription.PlanID == 0 {
6✔
115
                return false, fmt.Errorf("organization has no subscription plan")
1✔
116
        }
1✔
117

118
        plan, err := p.db.Plan(org.Subscription.PlanID)
4✔
119
        if err != nil {
4✔
120
                return false, fmt.Errorf("could not get organization plan: %v", err)
×
121
        }
×
122

123
        switch txType {
4✔
124
        // check UPDATE ACCOUNT INFO
125
        case models.TxType_SET_ACCOUNT_INFO_URI:
1✔
126
                // check if the user has the admin role for the organization
1✔
127
                if !user.HasRoleFor(org.Address, db.AdminRole) {
1✔
128
                        return false, fmt.Errorf("user does not have admin role")
×
129
                }
×
130
        // check CREATE PROCESS
131
        case models.TxType_NEW_PROCESS, models.TxType_SET_PROCESS_CENSUS:
1✔
132
                // check if the user has the admin role for the organization
1✔
133
                if !user.HasRoleFor(org.Address, db.AdminRole) {
1✔
134
                        return false, fmt.Errorf("user does not have admin role")
×
135
                }
×
136
                newProcess := tx.GetNewProcess()
1✔
137
                if newProcess.Process.MaxCensusSize > uint64(org.Subscription.MaxCensusSize) {
1✔
138
                        return false, fmt.Errorf("MaxCensusSize is greater than the allowed")
×
139
                }
×
140
                if org.Counters.Processes >= plan.Organization.MaxProcesses {
1✔
141
                        return false, fmt.Errorf("max processes reached")
×
142
                }
×
143
                return hasElectionMetadataPermissions(newProcess, plan)
1✔
144

145
        // check SET_PROCESS
146
        case models.TxType_SET_PROCESS_STATUS:
×
147
                // check if the user has the admin role for the organization
×
148
                if !user.HasRoleFor(org.Address, db.AdminRole) && !user.HasRoleFor(org.Address, db.ManagerRole) {
×
149
                        return false, fmt.Errorf("user does not have admin role")
×
150
                }
×
151
        }
152
        return true, nil
3✔
153
}
154

155
// HasDBPermission checks if the user has permission to perform the given action in the organization stored in the DB
156
func (p *Subscriptions) HasDBPermission(userEmail string, orgAddress common.Address, permission DBPermission) (bool, error) {
28✔
157
        user, err := p.db.UserByEmail(userEmail)
28✔
158
        if err != nil {
29✔
159
                return false, fmt.Errorf("could not get user: %v", err)
1✔
160
        }
1✔
161
        switch permission {
27✔
162
        case InviteUser:
24✔
163
                // check if the user has permission to invite users
24✔
164
                if !user.HasRoleFor(orgAddress, db.AdminRole) {
25✔
165
                        return false, fmt.Errorf("user does not have admin role")
1✔
166
                }
1✔
167
                return true, nil
23✔
168
        case DeleteUser:
1✔
169
                // check if the user has permission to delete users
1✔
170
                if !user.HasRoleFor(orgAddress, db.AdminRole) {
2✔
171
                        return false, fmt.Errorf("user does not have admin role")
1✔
172
                }
1✔
173
                return true, nil
×
174
        case CreateSubOrg:
2✔
175
                // check if the user has permission to create sub organizations
2✔
176
                if !user.HasRoleFor(orgAddress, db.AdminRole) {
3✔
177
                        return false, fmt.Errorf("user does not have admin role")
1✔
178
                }
1✔
179
                return true, nil
1✔
180
        }
181
        return false, fmt.Errorf("permission not found")
×
182
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc