Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions backend/.mockery.private.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ packages:
dir: internal/authn
structname: '{{.InterfaceName}}Mock'
pkgname: authn

github.com/asgardeo/thunder/internal/role:
config:
all: true
dir: internal/role
structname: '{{.InterfaceName}}Mock'
pkgname: role
filename: "{{.InterfaceName}}_mock_test.go"

github.com/asgardeo/thunder/internal/flow/flowexec:
Expand Down
8 changes: 8 additions & 0 deletions backend/.mockery.public.yml
Original file line number Diff line number Diff line change
Expand Up @@ -268,3 +268,11 @@ packages:
structname: '{{.InterfaceName}}Mock'
pkgname: userschemamock
filename: "{{.InterfaceName}}_mock.go"

github.com/asgardeo/thunder/internal/group:
config:
all: true
dir: tests/mocks/groupmock
structname: '{{.InterfaceName}}Mock'
pkgname: groupmock
filename: "{{.InterfaceName}}_mock.go"
4 changes: 3 additions & 1 deletion backend/cmd/server/servicemanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/asgardeo/thunder/internal/notification"
"github.com/asgardeo/thunder/internal/oauth"
"github.com/asgardeo/thunder/internal/ou"
"github.com/asgardeo/thunder/internal/role"
"github.com/asgardeo/thunder/internal/system/jwt"
"github.com/asgardeo/thunder/internal/system/log"
"github.com/asgardeo/thunder/internal/system/services"
Expand All @@ -51,7 +52,8 @@ func registerServices(mux *http.ServeMux) {
ouService := ou.Initialize(mux)
userSchemaService := userschema.Initialize(mux)
userService := user.Initialize(mux, ouService, userSchemaService)
_ = group.Initialize(mux, ouService, userService)
groupService := group.Initialize(mux, ouService, userService)
_ = role.Initialize(mux, userService, groupService, ouService)

_ = idp.Initialize(mux)
_ = notification.Initialize(mux, jwtService)
Expand Down
44 changes: 44 additions & 0 deletions backend/dbscripts/thunderdb/postgres.sql
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,50 @@ CREATE TABLE GROUP_MEMBER_REFERENCE (
FOREIGN KEY (GROUP_ID) REFERENCES "GROUP" (GROUP_ID) ON DELETE CASCADE
);

-- Table to store Roles
CREATE TABLE "ROLE" (
ID INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
ROLE_ID VARCHAR(36) UNIQUE NOT NULL,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will be better to add a unique constraint on (OU_ID, NAME) at DB level too right ? Could reduce potential race conditions

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added unique constraint on (OU_ID, NAME)

OU_ID VARCHAR(36) NOT NULL,
NAME VARCHAR(50) NOT NULL,
DESCRIPTION VARCHAR(255),
CREATED_AT TIMESTAMPTZ DEFAULT NOW(),
UPDATED_AT TIMESTAMPTZ DEFAULT NOW(),
CONSTRAINT unique_role_ou_name UNIQUE (OU_ID, NAME)
);

-- Table to store Role permissions
CREATE TABLE ROLE_PERMISSION (
ID INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
ROLE_ID VARCHAR(36) NOT NULL,
PERMISSION VARCHAR(100) NOT NULL,
CREATED_AT TIMESTAMPTZ DEFAULT NOW(),
FOREIGN KEY (ROLE_ID) REFERENCES "ROLE" (ROLE_ID) ON DELETE CASCADE,
CONSTRAINT unique_role_permission UNIQUE (ROLE_ID, PERMISSION)
);

-- Table to store Role assignments (to users and groups)
CREATE TABLE ROLE_ASSIGNMENT (
ID INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
ROLE_ID VARCHAR(36) NOT NULL,
ASSIGNEE_TYPE VARCHAR(5) NOT NULL CHECK (ASSIGNEE_TYPE IN ('user', 'group')),
ASSIGNEE_ID VARCHAR(36) NOT NULL,
CREATED_AT TIMESTAMPTZ DEFAULT NOW(),
UPDATED_AT TIMESTAMPTZ DEFAULT NOW(),
FOREIGN KEY (ROLE_ID) REFERENCES "ROLE" (ROLE_ID) ON DELETE CASCADE,
CONSTRAINT unique_role_assignment UNIQUE (ROLE_ID, ASSIGNEE_TYPE, ASSIGNEE_ID)
);

-- Indexes for authorization queries

-- Index for finding all roles assigned to a specific assignee
CREATE INDEX idx_role_assignment_assignee
ON ROLE_ASSIGNMENT (ASSIGNEE_ID, ASSIGNEE_TYPE);

-- Index for finding all permissions for a specific role
CREATE INDEX idx_role_permission_role
ON ROLE_PERMISSION (ROLE_ID);

-- Table to store basic service provider (app) details.
CREATE TABLE SP_APP (
ID INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
Expand Down
44 changes: 44 additions & 0 deletions backend/dbscripts/thunderdb/sqlite.sql
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,50 @@ CREATE TABLE GROUP_MEMBER_REFERENCE (
FOREIGN KEY (GROUP_ID) REFERENCES "GROUP" (GROUP_ID) ON DELETE CASCADE
);

-- Table to store Roles
CREATE TABLE "ROLE" (
ID INTEGER PRIMARY KEY AUTOINCREMENT,
ROLE_ID VARCHAR(36) UNIQUE NOT NULL,
OU_ID VARCHAR(36) NOT NULL,
NAME VARCHAR(50) NOT NULL,
DESCRIPTION VARCHAR(255),
CREATED_AT TEXT DEFAULT (datetime('now')),
UPDATED_AT TEXT DEFAULT (datetime('now')),
CONSTRAINT unique_role_ou_name UNIQUE (OU_ID, NAME)
);

-- Table to store Role permissions
CREATE TABLE ROLE_PERMISSION (
ID INTEGER PRIMARY KEY AUTOINCREMENT,
ROLE_ID VARCHAR(36) NOT NULL,
PERMISSION VARCHAR(100) NOT NULL,
CREATED_AT TEXT DEFAULT (datetime('now')),
FOREIGN KEY (ROLE_ID) REFERENCES "ROLE" (ROLE_ID) ON DELETE CASCADE,
CONSTRAINT unique_role_permission UNIQUE (ROLE_ID, PERMISSION)
);

-- Table to store Role assignments (to users and groups)
CREATE TABLE ROLE_ASSIGNMENT (
ID INTEGER PRIMARY KEY AUTOINCREMENT,
ROLE_ID VARCHAR(36) NOT NULL,
ASSIGNEE_TYPE VARCHAR(5) NOT NULL CHECK (ASSIGNEE_TYPE IN ('user', 'group')),
ASSIGNEE_ID VARCHAR(36) NOT NULL,
CREATED_AT TEXT DEFAULT (datetime('now')),
UPDATED_AT TEXT DEFAULT (datetime('now')),
FOREIGN KEY (ROLE_ID) REFERENCES "ROLE" (ROLE_ID) ON DELETE CASCADE,
CONSTRAINT unique_role_assignment UNIQUE (ROLE_ID, ASSIGNEE_TYPE, ASSIGNEE_ID)
);

-- Indexes for authorization queries

-- Index for finding all roles assigned to a specific assignee
CREATE INDEX idx_role_assignment_assignee
ON ROLE_ASSIGNMENT (ASSIGNEE_ID, ASSIGNEE_TYPE);

-- Index for finding all permissions for a specific role
CREATE INDEX idx_role_permission_role
ON ROLE_PERMISSION (ROLE_ID);

-- Table to store basic service provider (app) details.
CREATE TABLE SP_APP (
ID INTEGER PRIMARY KEY AUTOINCREMENT,
Expand Down
9 changes: 5 additions & 4 deletions backend/internal/group/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ type GroupServiceInterface interface {
UpdateGroup(groupID string, request UpdateGroupRequest) (*Group, *serviceerror.ServiceError)
DeleteGroup(groupID string) *serviceerror.ServiceError
GetGroupMembers(groupID string, limit, offset int) (*MemberListResponse, *serviceerror.ServiceError)
ValidateGroupIDs(groupIDs []string) *serviceerror.ServiceError
}

// groupService is the default implementation of the GroupServiceInterface.
Expand Down Expand Up @@ -183,7 +184,7 @@ func (gs *groupService) CreateGroup(request CreateGroupRequest) (*Group, *servic
return nil, err
}

if err := gs.validateGroupIDs(groupIDs); err != nil {
if err := gs.ValidateGroupIDs(groupIDs); err != nil {
return nil, err
}

Expand Down Expand Up @@ -318,7 +319,7 @@ func (gs *groupService) UpdateGroup(
return nil, err
}

if err := gs.validateGroupIDs(groupIDs); err != nil {
if err := gs.ValidateGroupIDs(groupIDs); err != nil {
return nil, err
}

Expand Down Expand Up @@ -514,8 +515,8 @@ func (gs *groupService) validateUserIDs(userIDs []string) *serviceerror.ServiceE
return nil
}

// validateGroupIDs validates that all provided group IDs exist.
func (gs *groupService) validateGroupIDs(groupIDs []string) *serviceerror.ServiceError {
// ValidateGroupIDs validates that all provided group IDs exist.
func (gs *groupService) ValidateGroupIDs(groupIDs []string) *serviceerror.ServiceError {
logger := log.GetLogger().With(log.String(log.LoggerKeyComponentName, loggerComponentName))

invalidGroupIDs, err := gs.groupStore.ValidateGroupIDs(groupIDs)
Expand Down
Loading
Loading