Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: permission issues and sidebar item defaults #211

Open
wants to merge 1 commit into
base: staging
Choose a base branch
from
Open
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
12 changes: 10 additions & 2 deletions modules/core/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,24 @@ func (m *Module) Register(app application.Application) error {
userRepo := persistence.NewUserRepository(uploadRepo)
roleRepo := persistence.NewRoleRepository()

// Create services
userService := services.NewUserService(userRepo, app.EventPublisher())
tabService := services.NewTabService(persistence.NewTabRepository())

// Set up dependencies
userService.SetTabService(tabService)
userService.SetApplication(app)

app.RegisterServices(
services.NewUploadService(uploadRepo, fsStorage, app.EventPublisher()),
services.NewUserService(userRepo, app.EventPublisher()),
userService,
services.NewSessionService(persistence.NewSessionRepository(), app.EventPublisher()),
)
app.RegisterServices(
services.NewAuthService(app),
services.NewCurrencyService(persistence.NewCurrencyRepository(), app.EventPublisher()),
services.NewRoleService(roleRepo, app.EventPublisher()),
services.NewTabService(persistence.NewTabRepository()),
tabService,
services.NewGroupService(persistence.NewGroupRepository(userRepo, roleRepo), app.EventPublisher()),
)
app.RegisterControllers(
Expand Down
87 changes: 85 additions & 2 deletions modules/core/services/user_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,21 @@ import (
"context"

"github.com/iota-uz/iota-sdk/modules/core/domain/aggregates/user"
"github.com/iota-uz/iota-sdk/modules/core/domain/entities/tab"
"github.com/iota-uz/iota-sdk/modules/core/permissions"
"github.com/iota-uz/iota-sdk/pkg/application"
"github.com/iota-uz/iota-sdk/pkg/composables"
"github.com/iota-uz/iota-sdk/pkg/constants"
"github.com/iota-uz/iota-sdk/pkg/eventbus"
"github.com/iota-uz/iota-sdk/pkg/types"
"github.com/nicksnyder/go-i18n/v2/i18n"
)

type UserService struct {
repo user.Repository
publisher eventbus.EventBus
repo user.Repository
publisher eventbus.EventBus
app application.Application
tabService *TabService
}

func NewUserService(repo user.Repository, publisher eventbus.EventBus) *UserService {
Expand All @@ -20,6 +28,58 @@ func NewUserService(repo user.Repository, publisher eventbus.EventBus) *UserServ
}
}

func (s *UserService) SetTabService(tabService *TabService) {
s.tabService = tabService
}

func (s *UserService) SetApplication(app application.Application) {
s.app = app
}

func (s *UserService) getAccessibleNavItems(items []types.NavigationItem, user user.User) []string {
var result []string

for _, item := range items {
if item.HasPermission(user) {
if item.Href != "" {
result = append(result, item.Href)
}

if len(item.Children) > 0 {
childItems := s.getAccessibleNavItems(item.Children, user)
result = append(result, childItems...)
}
}
}

return result
}

func (s *UserService) createUserTabs(ctx context.Context, user user.User) error {
if s.app == nil || s.tabService == nil {
return nil
}

items := s.app.NavItems(i18n.NewLocalizer(s.app.Bundle(), string(user.UILanguage())))
hrefs := s.getAccessibleNavItems(items, user)

tabs := make([]*tab.CreateDTO, 0, len(hrefs))
for i, href := range hrefs {
tabs = append(tabs, &tab.CreateDTO{
Href: href,
UserID: user.ID(),
Position: uint(i),
})
}

if len(tabs) > 0 {
ctxWithUser := context.WithValue(ctx, constants.UserKey, user)
_, err := s.tabService.CreateManyUserTabs(ctxWithUser, user.ID(), tabs)
return err
}
return nil
}

func (s *UserService) GetByEmail(ctx context.Context, email string) (user.User, error) {
return s.repo.GetByEmail(ctx, email)
}
Expand All @@ -33,14 +93,23 @@ func (s *UserService) GetAll(ctx context.Context) ([]user.User, error) {
}

func (s *UserService) GetByID(ctx context.Context, id uint) (user.User, error) {
if err := composables.CanUser(ctx, permissions.UserRead); err != nil {
return nil, err
}
return s.repo.GetByID(ctx, id)
}

func (s *UserService) GetPaginated(ctx context.Context, params *user.FindParams) ([]user.User, error) {
if err := composables.CanUser(ctx, permissions.UserRead); err != nil {
return nil, err
}
return s.repo.GetPaginated(ctx, params)
}

func (s *UserService) GetPaginatedWithTotal(ctx context.Context, params *user.FindParams) ([]user.User, int64, error) {
if err := composables.CanUser(ctx, permissions.UserRead); err != nil {
return nil, 0, err
}
us, err := s.repo.GetPaginated(ctx, params)
if err != nil {
return nil, 0, err
Expand All @@ -53,6 +122,9 @@ func (s *UserService) GetPaginatedWithTotal(ctx context.Context, params *user.Fi
}

func (s *UserService) Create(ctx context.Context, data user.User) error {
if err := composables.CanUser(ctx, permissions.UserCreate); err != nil {
return err
}
tx, err := composables.BeginTx(ctx)
if err != nil {
return err
Expand All @@ -70,6 +142,11 @@ func (s *UserService) Create(ctx context.Context, data user.User) error {
if err != nil {
return err
}

if err := s.createUserTabs(ctx, created); err != nil {
return err
}

if err := tx.Commit(ctx); err != nil {
return err
}
Expand All @@ -87,6 +164,9 @@ func (s *UserService) UpdateLastLogin(ctx context.Context, id uint) error {
}

func (s *UserService) Update(ctx context.Context, data user.User) error {
if err := composables.CanUser(ctx, permissions.UserUpdate); err != nil {
return err
}
tx, err := composables.BeginTx(ctx)
if err != nil {
return err
Expand Down Expand Up @@ -114,6 +194,9 @@ func (s *UserService) Update(ctx context.Context, data user.User) error {
}

func (s *UserService) Delete(ctx context.Context, id uint) (user.User, error) {
if err := composables.CanUser(ctx, permissions.UserDelete); err != nil {
return nil, err
}
tx, err := composables.BeginTx(ctx)
if err != nil {
return nil, err
Expand Down
17 changes: 14 additions & 3 deletions pkg/composables/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/iota-uz/iota-sdk/modules/core/domain/entities/permission"
"github.com/iota-uz/iota-sdk/modules/core/domain/entities/session"
"github.com/iota-uz/iota-sdk/pkg/constants"
"github.com/iota-uz/iota-sdk/pkg/rbac"
)

var (
Expand Down Expand Up @@ -41,11 +42,21 @@ func MustUseUser(ctx context.Context) user.User {
func CanUser(ctx context.Context, permission *permission.Permission) error {
u, err := UseUser(ctx)
if err != nil {
return err
return nil
}
if !u.Can(permission) {
return nil
// return service.ErrForbidden
return ErrForbidden
}
return nil
}

func CanUserAll(ctx context.Context, perms ...rbac.Permission) error {
u, err := UseUser(ctx)
if err != nil || len(perms) == 0 {
return nil // don't check if the user isn't in the context
}
if !rbac.And(perms...).Can(u) {
return ErrForbidden
}
return nil
}
Expand Down
62 changes: 62 additions & 0 deletions pkg/rbac/rbac.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package rbac

import (
"github.com/iota-uz/iota-sdk/modules/core/domain/aggregates/user"
"github.com/iota-uz/iota-sdk/modules/core/domain/entities/permission"
)

type Permission interface {
Can(u user.User) bool
}

type rbacPermission struct {
*permission.Permission
}

var _ Permission = (*rbacPermission)(nil)

func (p rbacPermission) Can(u user.User) bool {
return u.Can(p.Permission)
}

type or struct {
permissions []Permission
}

var _ Permission = (*or)(nil)

func (o or) Can(u user.User) bool {
for _, p := range o.permissions {
if p.Can(u) {
return true
}
}
return false
}

type and struct {
permissions []Permission
}

var _ Permission = (*and)(nil)

func (a and) Can(u user.User) bool {
for _, p := range a.permissions {
if !p.Can(u) {
return false
}
}
return true
}

func Or(perms ...Permission) Permission {
return or{permissions: perms}
}

func And(perms ...Permission) Permission {
return and{permissions: perms}
}

func Perm(p *permission.Permission) Permission {
return rbacPermission{Permission: p}
}