eintopf.info@v0.13.16/service/event/authorizer.go (about) 1 // Copyright (C) 2022 The Eintopf authors 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <https://www.gnu.org/licenses/>. 15 16 package event 17 18 import ( 19 "context" 20 "fmt" 21 "strings" 22 23 "eintopf.info/internal/crud" 24 "eintopf.info/service/auth" 25 ) 26 27 // Authorizer wraps a Storer. It checks the authorization before propagating 28 // the call to the wrapped storer. 29 type Authorizer struct { 30 store Storer 31 groupAuthorizer authorizer 32 } 33 34 // NewAuthorizer returns a new authorizer limiting access to the provided 35 // store. 36 func NewAuthorizer(store Storer, groupAuthorizer authorizer) Storer { 37 return &Authorizer{ 38 store: store, 39 groupAuthorizer: groupAuthorizer, 40 } 41 } 42 43 type authorizer interface { 44 IsOwned(ctx context.Context, id string, userID string) bool 45 } 46 47 // Create can be called by all users. 48 func (a *Authorizer) Create(ctx context.Context, newEvent *NewEvent) (*Event, error) { 49 _, err := auth.UserIDFromContext(ctx) 50 if err != nil { 51 return nil, auth.ErrUnauthorized 52 } 53 return a.store.Create(ctx, newEvent) 54 } 55 56 // Update can be called by an admin user or the event author. 57 func (a *Authorizer) Update(ctx context.Context, event *Event) (*Event, error) { 58 if err := a.adminOrOwned(ctx, event.ID); err != nil { 59 return nil, err 60 } 61 oldEvent, err := a.store.FindByID(ctx, event.ID) 62 if err != nil { 63 return nil, err 64 } 65 if oldEvent == nil { 66 return nil, fmt.Errorf("cant find event with id: %s", event.ID) 67 } 68 if oldEvent.Deactivated != event.Deactivated { 69 role, err := auth.RoleFromContext(ctx) 70 if err != nil { 71 return nil, auth.ErrUnauthorized 72 } 73 if role == auth.RoleNormal { 74 return nil, auth.ErrUnauthorized 75 } 76 } 77 return a.store.Update(ctx, event) 78 } 79 80 // Delete can be called by an admin user or the event author. 81 func (a *Authorizer) Delete(ctx context.Context, id string) error { 82 if err := a.adminOrOwned(ctx, id); err != nil { 83 return err 84 } 85 return a.store.Delete(ctx, id) 86 } 87 88 // FindByID can be called by everyone. 89 func (a *Authorizer) FindByID(ctx context.Context, id string) (*Event, error) { 90 return a.store.FindByID(ctx, id) 91 } 92 93 // Find can be called by everyone. 94 func (a *Authorizer) Find(ctx context.Context, params *crud.FindParams[FindFilters]) ([]*Event, int, error) { 95 return a.store.Find(ctx, params) 96 } 97 98 func (a *Authorizer) adminOrOwned(ctx context.Context, eventID string) error { 99 role, err := auth.RoleFromContext(ctx) 100 if err != nil { 101 return auth.ErrUnauthorized 102 } 103 if role == auth.RoleAdmin || role == auth.RoleInternal { 104 return nil 105 } 106 107 userID, err := auth.UserIDFromContext(ctx) 108 if err != nil { 109 return auth.ErrUnauthorized 110 } 111 event, err := a.FindByID(ctx, eventID) 112 if err != nil { 113 return err 114 } 115 if a.ownesEvent(ctx, userID, event) { 116 return nil 117 } 118 // Check if the user ownes the parent event. 119 if event.Parent != "" { 120 parentID := strings.TrimPrefix(event.Parent, "id:") 121 parentEvent, err := a.FindByID(ctx, parentID) 122 if err != nil { 123 return err 124 } 125 126 if a.ownesEvent(ctx, userID, parentEvent) { 127 return nil 128 } 129 } 130 131 return auth.ErrUnauthorized 132 } 133 134 func (a *Authorizer) ownesEvent(ctx context.Context, userID string, event *Event) bool { 135 if event.IsOwned(userID) { 136 return true 137 } 138 // When an organizer is a Group, check if the user ownes it. 139 // They then get permissions for this event. 140 for _, organizer := range event.Organizers { 141 if strings.Contains(organizer, "id:") { 142 groupID := strings.TrimPrefix(organizer, "id:") 143 if a.groupAuthorizer.IsOwned(ctx, groupID, userID) { 144 return true 145 } 146 } 147 } 148 return false 149 }