package lib import ( "testing" "time" "github.com/google/uuid" ) func TestCheckAccess(t *testing.T) { db, cfg := testDB(t) // Create owner ownerID := uuid.New().String() now := time.Now().UnixMilli() owner := &User{ UserID: ownerID, Email: "owner@test.com", Name: "Owner", Password: "$2a$10$test", Active: true, CreatedAt: now, UpdatedAt: now, } UserCreate(db, owner) projectID := testProject(t, db, cfg, ownerID) // IB admin can read err := CheckAccessRead(db, ownerID, projectID, "") if err != nil { t.Errorf("IB admin should have read access: %v", err) } // IB admin can write err = CheckAccessWrite(db, ownerID, projectID, "") if err != nil { t.Errorf("IB admin should have write access: %v", err) } // IB admin can delete err = CheckAccessDelete(db, ownerID, projectID, "") if err != nil { t.Errorf("IB admin should have delete access: %v", err) } // Create seller user sellerID := testUser(t, db, cfg, projectID, RoleSellerMember) // Seller can read err = CheckAccessRead(db, sellerID, projectID, "") if err != nil { t.Errorf("seller should have read access: %v", err) } // Seller can write err = CheckAccessWrite(db, sellerID, projectID, "") if err != nil { t.Errorf("seller should have write access: %v", err) } // Create buyer user (read-only) buyerID := testUser(t, db, cfg, projectID, RoleBuyerMember) // Buyer can read err = CheckAccessRead(db, buyerID, projectID, "") if err != nil { t.Errorf("buyer should have read access: %v", err) } // Buyer cannot write err = CheckAccessWrite(db, buyerID, projectID, "") if err != ErrAccessDenied { t.Errorf("buyer should NOT have write access, got: %v", err) } } func TestRoleHierarchy(t *testing.T) { // Verify hierarchy levels: buyer < seller_member < seller_admin < ib_analyst < ib_member < ib_admin expected := []struct { role string level int }{ {RoleObserver, 10}, {RoleBuyerMember, 30}, {RoleBuyerAdmin, 40}, {RoleSellerMember, 50}, {RoleSellerAdmin, 70}, {RoleIBMember, 80}, {RoleIBAdmin, 100}, } for _, tc := range expected { level, ok := RoleHierarchy[tc.role] if !ok { t.Errorf("role %s not in hierarchy", tc.role) continue } if level != tc.level { t.Errorf("role %s: expected level %d, got %d", tc.role, tc.level, level) } } // Verify ordering if RoleHierarchy[RoleBuyerMember] >= RoleHierarchy[RoleSellerMember] { t.Error("buyer should be lower than seller_member") } if RoleHierarchy[RoleSellerMember] >= RoleHierarchy[RoleSellerAdmin] { t.Error("seller_member should be lower than seller_admin") } if RoleHierarchy[RoleSellerAdmin] >= RoleHierarchy[RoleIBMember] { t.Error("seller_admin should be lower than ib_member") } if RoleHierarchy[RoleIBMember] >= RoleHierarchy[RoleIBAdmin] { t.Error("ib_member should be lower than ib_admin") } } func TestCanGrantRole(t *testing.T) { tests := []struct { granter string target string canDo bool }{ // IB admin can grant anything {RoleIBAdmin, RoleIBAdmin, true}, {RoleIBAdmin, RoleIBMember, true}, {RoleIBAdmin, RoleSellerAdmin, true}, {RoleIBAdmin, RoleBuyerMember, true}, // IB member can grant lower roles {RoleIBMember, RoleIBAdmin, false}, {RoleIBMember, RoleIBMember, true}, {RoleIBMember, RoleSellerAdmin, true}, // Seller admin can grant seller and buyer roles {RoleSellerAdmin, RoleIBMember, false}, {RoleSellerAdmin, RoleSellerAdmin, true}, {RoleSellerAdmin, RoleSellerMember, true}, {RoleSellerAdmin, RoleBuyerMember, true}, // Buyer cannot grant higher roles {RoleBuyerAdmin, RoleSellerMember, false}, {RoleBuyerAdmin, RoleBuyerAdmin, true}, {RoleBuyerAdmin, RoleBuyerMember, true}, } for _, tc := range tests { result := CanGrantRole(tc.granter, tc.target) if result != tc.canDo { t.Errorf("CanGrantRole(%s, %s) = %v, want %v", tc.granter, tc.target, result, tc.canDo) } } } func TestGrantRevoke(t *testing.T) { db, cfg := testDB(t) // Create admin adminID := uuid.New().String() now := time.Now().UnixMilli() admin := &User{ UserID: adminID, Email: "admin@test.com", Name: "Admin", Password: "$2a$10$test", Active: true, CreatedAt: now, UpdatedAt: now, } UserCreate(db, admin) projectID := testProject(t, db, cfg, adminID) // Create user with no access userID := uuid.New().String() user := &User{ UserID: userID, Email: "user@test.com", Name: "User", Password: "$2a$10$test", Active: true, CreatedAt: now, UpdatedAt: now, } UserCreate(db, user) // Verify no access err := CheckAccessRead(db, userID, projectID, "") if err != ErrAccessDenied { t.Error("user should have no access initially") } // Grant access accessID := uuid.New().String() access := &Access{ ID: accessID, ProjectID: projectID, UserID: userID, Role: RoleBuyerMember, Ops: "r", CanGrant: false, GrantedBy: adminID, GrantedAt: now, } if err := AccessGrant(db, access); err != nil { t.Fatalf("AccessGrant: %v", err) } // Verify access granted err = CheckAccessRead(db, userID, projectID, "") if err != nil { t.Errorf("user should have read access after grant: %v", err) } // Revoke access if err := AccessRevoke(db, accessID, adminID); err != nil { t.Fatalf("AccessRevoke: %v", err) } // Verify access revoked err = CheckAccessRead(db, userID, projectID, "") if err != ErrAccessDenied { t.Error("user should have no access after revoke") } } func TestIsBuyerRole(t *testing.T) { buyers := []string{RoleBuyerAdmin, RoleBuyerMember} nonBuyers := []string{RoleIBAdmin, RoleIBMember, RoleSellerAdmin, RoleSellerMember, RoleObserver} for _, role := range buyers { if !IsBuyerRole(role) { t.Errorf("%s should be buyer role", role) } } for _, role := range nonBuyers { if IsBuyerRole(role) { t.Errorf("%s should NOT be buyer role", role) } } } func TestGetUserHighestRole(t *testing.T) { db, cfg := testDB(t) // Create admin and project adminID := uuid.New().String() now := time.Now().UnixMilli() admin := &User{ UserID: adminID, Email: "admin@test.com", Name: "Admin", Password: "$2a$10$test", Active: true, CreatedAt: now, UpdatedAt: now, } UserCreate(db, admin) projectID := testProject(t, db, cfg, adminID) // Admin's highest role should be ib_admin role, err := GetUserHighestRole(db, adminID, projectID) if err != nil { t.Fatalf("GetUserHighestRole: %v", err) } if role != RoleIBAdmin { t.Errorf("expected ib_admin, got %s", role) } // Create user with multiple roles userID := uuid.New().String() user := &User{ UserID: userID, Email: "multi@test.com", Name: "Multi Role User", Password: "$2a$10$test", Active: true, CreatedAt: now, UpdatedAt: now, } UserCreate(db, user) // Grant buyer role AccessGrant(db, &Access{ ID: uuid.New().String(), ProjectID: projectID, UserID: userID, Role: RoleBuyerMember, Ops: "r", GrantedBy: adminID, GrantedAt: now, }) // Grant seller role (higher) AccessGrant(db, &Access{ ID: uuid.New().String(), ProjectID: projectID, UserID: userID, Role: RoleSellerMember, Ops: "rw", GrantedBy: adminID, GrantedAt: now, }) // Highest should be seller_member role, err = GetUserHighestRole(db, userID, projectID) if err != nil { t.Fatalf("GetUserHighestRole: %v", err) } if role != RoleSellerMember { t.Errorf("expected seller_member (highest), got %s", role) } // User with no access noAccessID := uuid.New().String() noAccessUser := &User{ UserID: noAccessID, Email: "noaccess@test.com", Name: "No Access", Password: "$2a$10$test", Active: true, CreatedAt: now, UpdatedAt: now, } UserCreate(db, noAccessUser) _, err = GetUserHighestRole(db, noAccessID, projectID) if err != ErrAccessDenied { t.Errorf("expected ErrAccessDenied for user with no access, got %v", err) } } func TestWorkstreamAccess(t *testing.T) { db, cfg := testDB(t) // Create admin and project adminID := uuid.New().String() now := time.Now().UnixMilli() admin := &User{ UserID: adminID, Email: "admin@test.com", Name: "Admin", Password: "$2a$10$test", Active: true, CreatedAt: now, UpdatedAt: now, } UserCreate(db, admin) projectID := testProject(t, db, cfg, adminID) // Create user with access to specific workstream only userID := uuid.New().String() user := &User{ UserID: userID, Email: "ws@test.com", Name: "Workstream User", Password: "$2a$10$test", Active: true, CreatedAt: now, UpdatedAt: now, } UserCreate(db, user) workstreamID := "workstream-1" AccessGrant(db, &Access{ ID: uuid.New().String(), ProjectID: projectID, WorkstreamID: workstreamID, UserID: userID, Role: RoleSellerMember, Ops: "rw", GrantedBy: adminID, GrantedAt: now, }) // User has access to their workstream _, err := CheckAccess(db, userID, projectID, workstreamID, "r") if err != nil { t.Errorf("user should have access to their workstream: %v", err) } // User should NOT have access to different workstream _, err = CheckAccess(db, userID, projectID, "different-workstream", "r") if err != ErrAccessDenied { t.Error("user should NOT have access to different workstream") } } // ---- Additional tests for super_admin, IsSuperAdmin, and ValidateOrgDomain ---- func TestSuperAdminBypassesCheckAccess(t *testing.T) { db, cfg := testDB(t) // Create a regular user who owns a project ownerID := uuid.New().String() now := time.Now().UnixMilli() owner := &User{ UserID: ownerID, Email: "owner@test.com", Name: "Owner", Password: "$2a$10$test", Active: true, CreatedAt: now, UpdatedAt: now, } UserCreate(db, owner) projectID := testProject(t, db, cfg, ownerID) // Create super_admin user (no explicit project access) superAdminID := uuid.New().String() superAdmin := &User{ UserID: superAdminID, Email: "superadmin@test.com", Name: "Super Admin", Password: "$2a$10$test", Active: true, CreatedAt: now, UpdatedAt: now, } UserCreate(db, superAdmin) // Grant super_admin role on a dummy project (super_admin is global but stored per project) dummyProjectID := uuid.New().String() AccessGrant(db, &Access{ ID: uuid.New().String(), ProjectID: dummyProjectID, UserID: superAdminID, Role: RoleSuperAdmin, Ops: "rwdm", CanGrant: true, GrantedBy: "system", GrantedAt: now, }) // Verify super_admin is recognized isSA, err := IsSuperAdmin(db, superAdminID) if err != nil { t.Fatalf("IsSuperAdmin failed: %v", err) } if !isSA { t.Error("User with super_admin role should be recognized as super admin") } // super_admin should have full access to any project without explicit grant access, err := CheckAccess(db, superAdminID, projectID, "", "r") if err != nil { t.Errorf("super_admin should have read access: %v", err) } if access == nil { t.Fatal("CheckAccess should return an Access object for super_admin") } if access.Role != RoleSuperAdmin { t.Errorf("Access role should be super_admin, got %s", access.Role) } if access.Ops != "rwdm" { t.Errorf("super_admin should have rwdm ops, got %s", access.Ops) } // Test all operations _, err = CheckAccess(db, superAdminID, projectID, "", "w") if err != nil { t.Errorf("super_admin should have write access: %v", err) } _, err = CheckAccess(db, superAdminID, projectID, "", "d") if err != nil { t.Errorf("super_admin should have delete access: %v", err) } _, err = CheckAccess(db, superAdminID, projectID, "", "m") if err != nil { t.Errorf("super_admin should have manage access: %v", err) } // Test convenience functions err = CheckAccessRead(db, superAdminID, projectID, "") if err != nil { t.Errorf("super_admin CheckAccessRead should pass: %v", err) } err = CheckAccessWrite(db, superAdminID, projectID, "") if err != nil { t.Errorf("super_admin CheckAccessWrite should pass: %v", err) } err = CheckAccessDelete(db, superAdminID, projectID, "") if err != nil { t.Errorf("super_admin CheckAccessDelete should pass: %v", err) } } func TestIsSuperAdmin(t *testing.T) { db, _ := testDB(t) now := time.Now().UnixMilli() // Create regular user regularID := uuid.New().String() regular := &User{ UserID: regularID, Email: "regular@test.com", Name: "Regular User", Password: "$2a$10$test", Active: true, CreatedAt: now, UpdatedAt: now, } UserCreate(db, regular) // Create super admin user superID := uuid.New().String() superUser := &User{ UserID: superID, Email: "super@test.com", Name: "Super User", Password: "$2a$10$test", Active: true, CreatedAt: now, UpdatedAt: now, } UserCreate(db, superUser) // Grant super_admin role AccessGrant(db, &Access{ ID: uuid.New().String(), ProjectID: "any-project", UserID: superID, Role: RoleSuperAdmin, Ops: "rwdm", CanGrant: true, GrantedBy: "system", GrantedAt: now, }) // Test IsSuperAdmin isSA, err := IsSuperAdmin(db, superID) if err != nil { t.Fatalf("IsSuperAdmin failed: %v", err) } if !isSA { t.Error("User with super_admin role should return true") } isRegularSA, err := IsSuperAdmin(db, regularID) if err != nil { t.Fatalf("IsSuperAdmin for regular user failed: %v", err) } if isRegularSA { t.Error("Regular user should not be super admin") } // Test with ib_admin (should NOT be super admin) ibAdminID := uuid.New().String() ibAdmin := &User{ UserID: ibAdminID, Email: "ibadmin@test.com", Name: "IB Admin", Password: "$2a$10$test", Active: true, CreatedAt: now, UpdatedAt: now, } UserCreate(db, ibAdmin) AccessGrant(db, &Access{ ID: uuid.New().String(), ProjectID: "test-project", UserID: ibAdminID, Role: RoleIBAdmin, Ops: "rwdm", CanGrant: true, GrantedBy: "system", GrantedAt: now, }) isIBSA, err := IsSuperAdmin(db, ibAdminID) if err != nil { t.Fatalf("IsSuperAdmin for ib_admin failed: %v", err) } if isIBSA { t.Error("ib_admin should NOT be super admin") } } func TestIsSuperAdminRevokedGrant(t *testing.T) { db, _ := testDB(t) now := time.Now().UnixMilli() // Create user userID := uuid.New().String() user := &User{ UserID: userID, Email: "revoke-test@test.com", Name: "Revoke Test", Password: "$2a$10$test", Active: true, CreatedAt: now, UpdatedAt: now, } UserCreate(db, user) // Grant and then revoke super_admin accessID := uuid.New().String() AccessGrant(db, &Access{ ID: accessID, ProjectID: "any-project", UserID: userID, Role: RoleSuperAdmin, Ops: "rwdm", CanGrant: true, GrantedBy: "system", GrantedAt: now, }) // Should be super admin isSA, _ := IsSuperAdmin(db, userID) if !isSA { t.Error("Should be super admin before revocation") } // Revoke the grant AccessRevoke(db, accessID, "system") // Should no longer be super admin isSA, _ = IsSuperAdmin(db, userID) if isSA { t.Error("Should NOT be super admin after revocation") } } func TestSuperAdminCanGrantAnyRole(t *testing.T) { // super_admin (level 200) should be able to grant any role tests := []struct { target string canDo bool }{ {RoleSuperAdmin, true}, {RoleIBAdmin, true}, {RoleIBMember, true}, {RoleSellerAdmin, true}, {RoleSellerMember, true}, {RoleBuyerAdmin, true}, {RoleBuyerMember, true}, {RoleObserver, true}, } for _, tc := range tests { result := CanGrantRole(RoleSuperAdmin, tc.target) if result != tc.canDo { t.Errorf("CanGrantRole(super_admin, %s) = %v, want %v", tc.target, result, tc.canDo) } } }