-
Notifications
You must be signed in to change notification settings - Fork 0
/
access_functions.go
707 lines (584 loc) · 17.7 KB
/
access_functions.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
package main
import (
"encoding/json"
"fmt"
"log"
"os"
"strings"
)
type PublicAccessUser struct {
Username string
PublicToken []byte
}
type PrivateAccessUser struct {
UserID int
Username string
Password string
Roles []AccessRole
UserPrivateToken []byte
}
type AccessGroup struct {
GroupID int
Name string
UserList []PrivateAccessUser
Roles []AccessRole
GroupPrivateToken []byte
}
type AccessRole struct {
RoleID int
Name string
Scope string
Policies []AccessPolicy
}
type AccessPolicy struct {
PolicyID int
Name string
Permissions []string
}
type SystemDB struct {
Users []PrivateAccessUser
Groups []AccessGroup
Roles []AccessRole
Policies []AccessPolicy
}
// Loads the system databases from file
// / ** This will load Users, Groups, Roles and Policies
func (s *SystemDB) loadSystemDB() error {
systemTables := []string{"users", "groups", "policies", "roles"}
for _, tableName := range systemTables {
content, err := os.ReadFile(fmt.Sprintf("system/%v.dat", tableName))
if err != nil {
// If the system databases cannot be found, create the base policies and roles
if strings.Contains(err.Error(), "cannot find the file") {
// Create base policies
s.createBasePolicies()
// Create base roles
baseRolesError := s.createBaseRoles()
if baseRolesError != nil {
return baseRolesError
}
// Save the database to create the files
// Load the database to check loading works
s.saveSystemDB()
s.loadSystemDB()
} else {
return err
}
}
ekerr := generateEncryptionKey(keyPath)
if ekerr != nil {
return ekerr
}
decryptedData, decryptErr := decryptData([]byte(os.Getenv("EK")), content)
if decryptErr != nil {
return decryptErr
}
switch tableName {
case "users":
err = json.Unmarshal(decryptedData, &s.Users)
if err != nil {
return err
}
case "groups":
err = json.Unmarshal(decryptedData, &s.Groups)
if err != nil {
return err
}
case "roles":
err = json.Unmarshal(decryptedData, &s.Roles)
if err != nil {
return err
}
case "policies":
err = json.Unmarshal(decryptedData, &s.Policies)
if err != nil {
return err
}
default:
return fmt.Errorf("no system table goes by the name specified")
}
}
return nil
}
// Saves the system databases to file
func (s *SystemDB) saveSystemDB() error {
systemTables := []string{"users", "groups", "policies", "roles"}
var content []byte
var err error
for _, tableName := range systemTables {
switch tableName {
case "users":
content, err = json.Marshal(s.Users)
if err != nil {
return err
}
case "groups":
content, err = json.Marshal(s.Groups)
if err != nil {
return err
}
case "policies":
content, err = json.Marshal(s.Policies)
if err != nil {
return err
}
case "roles":
content, err = json.Marshal(s.Roles)
if err != nil {
return err
}
default:
return fmt.Errorf("no system table could be found by that name")
}
file, fileErr := os.OpenFile(fmt.Sprintf("system/%v.dat", tableName), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
if fileErr != nil {
return fileErr
}
ekerr := generateEncryptionKey(keyPath)
if ekerr != nil {
return ekerr
}
encryptedContent, EncryptErr := encrpytData([]byte(os.Getenv("EK")), content)
if EncryptErr != nil {
return EncryptErr
}
_, fileWriteErr := file.Write(encryptedContent)
if fileWriteErr != nil {
return fileWriteErr
}
defer file.Close()
}
return nil
}
// Close the database and remove all data
func (s *SystemDB) close() {
saveErr := s.saveSystemDB()
if saveErr != nil {
log.Fatalf("failed to save the system database")
}
s = nil
}
// Create base policies within the system database
func (s *SystemDB) createBasePolicies() {
readerPolicy := AccessPolicy{
PolicyID: 1,
Name: "Reader",
Permissions: []string{"PULL"},
}
writerPolicy := AccessPolicy{
PolicyID: 2,
Name: "Writer",
Permissions: []string{"PUSH", "PUT"},
}
removerPolicy := AccessPolicy{
PolicyID: 3,
Name: "Remover",
Permissions: []string{"DELETE"},
}
s.Policies = append(s.Policies, readerPolicy, writerPolicy, removerPolicy)
}
// Find policy by name search function
func (s *SystemDB) findPolicyByName(policyName string) (AccessPolicy, error) {
for _, value := range s.Policies {
if value.Name == policyName {
return value, nil
}
}
return AccessPolicy{}, fmt.Errorf("no policy could be found by the name: %v", policyName)
}
// Find policy by ID search function
func (s *SystemDB) findPolicyByID(policyID int) (AccessPolicy, error) {
for _, value := range s.Policies {
if value.PolicyID == policyID {
return value, nil
}
}
return AccessPolicy{}, fmt.Errorf("no policy could be found by the id: %v", policyID)
}
// Create base roles within the system database
func (s *SystemDB) createBaseRoles() error {
// Find the base policies
readerPolicy, readerPolicyErr := s.findPolicyByName("Reader")
writerPolicy, writerPolicyErr := s.findPolicyByName("Writer")
removerPolicy, removerPolicyErr := s.findPolicyByName("Remover")
// Iterate over each of them to send the appropriate error
for _, value := range []error{readerPolicyErr, writerPolicyErr, removerPolicyErr} {
if value != nil {
return value
}
}
// Create each of the roles at a root scope
/// Root admin role
rootAdminRole := AccessRole{
RoleID: 1,
Name: "Root Admin",
Scope: "*",
Policies: []AccessPolicy{
readerPolicy,
writerPolicy,
removerPolicy,
},
}
// Root reader role
rootReaderRole := AccessRole{
RoleID: 2,
Name: "Root Reader",
Scope: "*",
Policies: []AccessPolicy{
readerPolicy,
},
}
// Root writer role
rootWriterRole := AccessRole{
RoleID: 3,
Name: "Root Writer",
Scope: "*",
Policies: []AccessPolicy{
readerPolicy,
writerPolicy,
},
}
// Append the roles to the System DB
s.Roles = append(s.Roles, rootAdminRole, rootReaderRole, rootWriterRole)
return nil
}
// Find role by name search function
func (s *SystemDB) findRoleByName(roleName string) (AccessRole, error) {
for _, value := range s.Roles {
if value.Name == roleName {
return value, nil
}
}
return AccessRole{}, fmt.Errorf("no role could be found matching the name: %v", roleName)
}
// Find role by ID search function
func (s *SystemDB) findRoleByID(roleID int) (AccessRole, error) {
for _, value := range s.Roles {
if value.RoleID == roleID {
return value, nil
}
}
return AccessRole{}, fmt.Errorf("no role could be found matching the ID: %v", roleID)
}
// Confirm that the appropriate permission is applied for the specified scope
func (r *AccessRole) confirmPermission(scope string, permission string) bool {
for _, policy := range r.Policies {
for _, policyPermission := range policy.Permissions {
if policyPermission == permission && scope == r.Scope {
return true
}
}
}
return false
}
// Assign a user to a role
func (s *SystemDB) assignUserToRole(User PublicAccessUser, Role AccessRole) error {
// check user exists
for userIndex, userItem := range s.Users {
if userItem.Username == User.Username {
// check role exists
for _, roleItem := range s.Roles {
if Role.RoleID == roleItem.RoleID {
// assign the role
s.Users[userIndex].Roles = append(s.Users[userIndex].Roles, roleItem)
return nil
}
}
// If it hits here, no matching role was found, return the error
return fmt.Errorf("a registered role could not be found within the system database")
}
}
// If it hits here, no matching user was found, return the error
return fmt.Errorf("a registered user could not be found within the system database")
}
// assign a user to group
func (s *SystemDB) assignUserToGroup(User PublicAccessUser, Group AccessGroup) error {
for _, userItem := range s.Users {
if userItem.Username == User.Username {
// check group exists
for groupIndex, groupItem := range s.Groups {
if Group.GroupID == groupItem.GroupID {
// assign the group
s.Groups[groupIndex].UserList = append(s.Groups[groupIndex].UserList, userItem)
return nil
}
}
// If it hits here, no matching group was found, return the error
return fmt.Errorf("a matching group could not be found within the system database")
}
}
// If it hits here, no matching user was found, return the error
return fmt.Errorf("a matching user could not be found within the system database")
}
// assign a role to the group
func (s *SystemDB) assignGroupToRole(Group AccessGroup, Role AccessRole) error {
// check user exists
for groupIndex, groupItem := range s.Groups {
if groupItem.GroupID == Group.GroupID {
// check role exists
for _, roleItem := range s.Roles {
if Role.RoleID == roleItem.RoleID {
// check for duplicates within the group
for _, groupRoleItem := range groupItem.Roles {
if groupRoleItem.RoleID == Role.RoleID {
return fmt.Errorf("%v already has an assigned instance of %v", Group.Name, Role.Name)
}
}
// if it hits here, there are no duplicates assigned to the group - assign this role to the group
s.Groups[groupIndex].Roles = append(s.Groups[groupIndex].Roles, roleItem)
return nil
}
}
// If it hits here, no matching role was found, return the error
return fmt.Errorf("a matching role could not be found within the system database")
}
}
// If it hits here, no matching user was found, return the error
return fmt.Errorf("a matching group could not be found within the system database")
}
// create a new group
func (s *SystemDB) createGroup(groupName string) (AccessGroup, error) {
latestID := 0
// Check for group name overlap
for _, groupItem := range s.Groups {
if groupItem.Name == groupName {
return AccessGroup{}, fmt.Errorf("an existing group already has the name: %v", groupName)
}
// use this to get the latest Group ID to automatically generate a new one
if groupItem.GroupID > latestID {
latestID = groupItem.GroupID
}
}
// Generate private token
privKey, privKeyErr := generatePrivateKey()
if privKeyErr != nil {
return AccessGroup{}, privKeyErr
}
// create and append the new group
newGroup := AccessGroup{
GroupID: (latestID + 1),
Name: groupName,
UserList: []PrivateAccessUser{},
Roles: []AccessRole{},
GroupPrivateToken: privKey,
}
s.Groups = append(s.Groups, newGroup)
return newGroup, nil
}
// create a new user
func (s *SystemDB) createUser(Username string, Password string) (PublicAccessUser, error) {
latestID := 0
// Check if the user already exists, get the latest ID
for _, userItem := range s.Users {
if userItem.Username == Username {
return PublicAccessUser{}, fmt.Errorf("username already exists: %v", Username)
}
if userItem.UserID > latestID {
latestID = userItem.UserID
}
}
// create a private key for the user
privKey, privKeyErr := generatePrivateKey()
if privKeyErr != nil {
return PublicAccessUser{}, privKeyErr
}
// create the new user object and append it to the system table
s.Users = append(s.Users, PrivateAccessUser{
UserID: (latestID + 1),
Username: Username,
Password: Password,
Roles: []AccessRole{},
UserPrivateToken: privKey,
})
// create the public key for the user using the new private key
pubKey, pubKeyErr := generatePublicKey(privKey)
if pubKeyErr != nil {
return PublicAccessUser{}, pubKeyErr
}
// return the public user object
return PublicAccessUser{
Username: Username,
PublicToken: pubKey,
}, nil
}
// create a new role
// ** - Still need to confirm scope
func (s *SystemDB) createRole(roleName string, scope string, policies []AccessPolicy) error {
latestID := 0
// check for role duplicates with the same name and get the latest id
for _, roleItem := range s.Roles {
if roleItem.Name == roleName {
return fmt.Errorf("an existing role is already using the name: %v", roleName)
}
if roleItem.RoleID > latestID {
latestID = roleItem.RoleID
}
}
// check policies for existence
for _, specifiedPolicyItem := range policies {
matchingPolicyFound := false
for _, policyItem := range s.Policies {
if policyItem.PolicyID == specifiedPolicyItem.PolicyID {
matchingPolicyFound = true
break
}
}
if !matchingPolicyFound {
return fmt.Errorf("no matching policy could be found to match: %v", specifiedPolicyItem.Name)
}
}
// create and add the new role to the system db
s.Roles = append(s.Roles, AccessRole{
RoleID: (latestID + 1),
Name: roleName,
Scope: scope,
Policies: policies,
})
return nil
}
// create a new policy for a set of permissions
func (s *SystemDB) createPolicy(policyName string, perms []string) error {
// specify the permissions that will actually be accepted for the creation of a policy
acceptedPerms := []string{"PULL", "PUSH", "PUT", "DELETE"}
latestID := 0
// check for policy duplicates
for _, policyItem := range s.Policies {
if policyItem.Name == policyName {
return fmt.Errorf("an existing policy already has the name: %v", policyName)
}
if policyItem.PolicyID > latestID {
latestID = policyItem.PolicyID
}
}
// check the perm strings for the allowed actions
for _, permItem := range perms {
permAllowed := false
for _, acceptedItem := range acceptedPerms {
if permItem == acceptedItem {
permAllowed = true
break
}
}
if !permAllowed {
return fmt.Errorf("permission string not recognised: %v", permItem)
}
}
// create the policy and append it to the system db
s.Policies = append(s.Policies, AccessPolicy{
PolicyID: (latestID + 1),
Name: policyName,
Permissions: perms,
})
return nil
}
// handle a user login, and generate a public access user object
func (s *SystemDB) userLogin(username string, password string) (PublicAccessUser, error) {
for _, userItem := range s.Users {
if userItem.Username == username && userItem.Password == password {
pubKey, pubKeyErr := generatePublicKey(userItem.UserPrivateToken)
if pubKeyErr != nil {
return PublicAccessUser{}, pubKeyErr
}
return PublicAccessUser{
Username: username,
PublicToken: pubKey,
}, nil
}
}
return PublicAccessUser{}, fmt.Errorf("the username or password was incorrect, please try again")
}
// search for a group by its name
func (s *SystemDB) findGroupByName(groupName string) (AccessGroup, error) {
for _, groupItem := range s.Groups {
if groupItem.Name == groupName {
return groupItem, nil
}
}
return AccessGroup{}, fmt.Errorf("no group could be found with the name: %v", groupName)
}
// search for a group by its ID
func (s *SystemDB) findGroupByID(groupID int) (AccessGroup, error) {
for _, groupItem := range s.Groups {
if groupItem.GroupID == groupID {
return groupItem, nil
}
}
return AccessGroup{}, fmt.Errorf("no group could be found with the id: %v", groupID)
}
// delete a user based on user ID
// ** - need to add functionality unassign users from groups
func (s *SystemDB) deleteUser(username string) error {
for userIndex, userItem := range s.Users {
if userItem.Username == username {
s.Users = append(s.Users[:userIndex], s.Users[(userIndex+1):]...)
return nil
}
}
return fmt.Errorf("no user exists with the username: %v", username)
}
// delete a role based on its ID
// ** - need to add functionality to unassign groups and users from roles
func (s *SystemDB) deleteRole(roleID int) error {
for roleIndex, roleItem := range s.Roles {
if roleItem.RoleID == roleID {
s.Roles = append(s.Roles[:roleIndex], s.Roles[(roleIndex+1):]...)
return nil
}
}
return fmt.Errorf("no role exists with the id: %v", roleID)
}
// delete a group based on its ID
func (s *SystemDB) deleteGroup(groupID int) error {
for groupIndex, groupItem := range s.Groups {
if groupItem.GroupID == groupID {
s.Groups = append(s.Groups[:groupIndex], s.Groups[(groupIndex+1):]...)
return nil
}
}
return fmt.Errorf("no group exists with the id: %v", groupID)
}
// remove a user from group membership
func (s *SystemDB) removeUserFromGroup(groupID int, username string) error {
for groupIndex, groupItem := range s.Groups {
if groupItem.GroupID == groupID {
for userIndex, userItem := range groupItem.UserList {
if userItem.Username == username {
s.Groups[groupIndex].UserList = append(s.Groups[groupIndex].UserList[:userIndex], s.Groups[groupIndex].UserList[(userIndex+1):]...)
return nil
}
}
return fmt.Errorf("no user could be found in the specified group with the username: %v", username)
}
}
return fmt.Errorf("no group could be found with the ID: %v", groupID)
}
// remove a user from a role
func (s *SystemDB) removeUserFromRole(roleID int, username string) error {
for userIndex, userItem := range s.Users {
if userItem.Username == username {
for roleIndex, roleItem := range userItem.Roles {
if roleItem.RoleID == roleID {
s.Users[userIndex].Roles = append(s.Users[userIndex].Roles[:roleIndex], s.Users[userIndex].Roles[(roleIndex+1):]...)
return nil
}
}
return fmt.Errorf("no role could be found with an ID matching: %v", roleID)
}
}
return fmt.Errorf("no user could be found with a username matching: %v", username)
}
// remove a group from a role
func (s *SystemDB) removeGroupFromRole(roleID int, groupID int) error {
for groupIndex, groupItem := range s.Groups {
if groupItem.GroupID == groupID {
for roleIndex, roleItem := range groupItem.Roles {
if roleItem.RoleID == roleID {
s.Groups[groupIndex].Roles = append(s.Groups[groupIndex].Roles[:roleIndex], s.Groups[groupIndex].Roles[(roleIndex+1):]...)
return nil
}
}
return fmt.Errorf("no role could be found with a matching id to: %v", roleID)
}
}
return fmt.Errorf("no group could be found with an ID that matched: %v", groupID)
}