eintopf.info@v0.13.16/service/notification/authorizer.go (about) 1 // Copyright (C) 2024 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 notification 17 18 import ( 19 "context" 20 "fmt" 21 22 "eintopf.info/internal/crud" 23 "eintopf.info/service/auth" 24 ) 25 26 type Authorizer struct { 27 store Storer 28 } 29 30 // NewAuthorizer wraps the given store with authorization methods. 31 func NewAuthorizer(store Storer) *Authorizer { 32 return &Authorizer{store: store} 33 } 34 35 // Create is allowed by 36 // - an admin 37 // - a moderator 38 // - internally 39 func (a *Authorizer) Create(ctx context.Context, notification *NewNotification) (*Notification, error) { 40 role, err := auth.RoleFromContext(ctx) 41 if err != nil { 42 return nil, auth.ErrUnauthorized 43 } 44 if role == auth.RoleNormal { 45 return nil, auth.ErrUnauthorized 46 } 47 return a.store.Create(ctx, notification) 48 } 49 50 // Update is allowed by 51 // - the user owning the notification 52 // - an admin 53 // - internally 54 func (a *Authorizer) Update(ctx context.Context, notification *Notification) (*Notification, error) { 55 if err := a.adminOrOwned(ctx, notification.ID); err != nil { 56 return nil, err 57 } 58 59 return a.store.Update(ctx, notification) 60 } 61 62 // Delete is allowed by 63 // - the user owning the notification 64 // - an admin 65 // - internally 66 func (a *Authorizer) Delete(ctx context.Context, id string) error { 67 if err := a.adminOrOwned(ctx, id); err != nil { 68 return err 69 } 70 71 return a.store.Delete(ctx, id) 72 } 73 74 // FindByID is allowed by 75 // - the user owning the notification 76 // - an admin 77 // - internally 78 func (a *Authorizer) FindByID(ctx context.Context, id string) (*Notification, error) { 79 if err := a.adminOrOwned(ctx, id); err != nil { 80 return nil, err 81 } 82 83 return a.store.FindByID(ctx, id) 84 } 85 86 // Find is allowed by 87 // - normal and moderator for notifications that are owned 88 // - an admin 89 // - internally 90 func (a *Authorizer) Find(ctx context.Context, params *crud.FindParams[Filters]) ([]*Notification, int, error) { 91 notifications, total, err := a.store.Find(ctx, params) 92 if err != nil { 93 return nil, 0, err 94 } 95 96 role, err := auth.RoleFromContext(ctx) 97 if err != nil { 98 return nil, 0, auth.ErrUnauthorized 99 } 100 if role == auth.RoleAdmin || role == auth.RoleInternal { 101 return notifications, total, nil 102 } 103 104 userID, err := auth.UserIDFromContext(ctx) 105 if err != nil { 106 return nil, 0, auth.ErrUnauthorized 107 } 108 109 filtered := []*Notification{} 110 for _, n := range notifications { 111 if n.UserID == userID { 112 filtered = append(filtered, n) 113 } else { 114 total-- 115 } 116 } 117 return filtered, total, nil 118 } 119 120 func (a *Authorizer) adminOrOwned(ctx context.Context, id string) error { 121 role, err := auth.RoleFromContext(ctx) 122 if err != nil { 123 return auth.ErrUnauthorized 124 } 125 if role == auth.RoleNormal || role == auth.RoleModerator { 126 userID, err := auth.UserIDFromContext(ctx) 127 if err != nil { 128 return auth.ErrUnauthorized 129 } 130 131 n, err := a.store.FindByID(auth.ContextWithRole(ctx, auth.RoleInternal), id) 132 if err != nil { 133 return err 134 } 135 if n == nil { 136 return fmt.Errorf("could not find existing notification") 137 } 138 139 if n.UserID != userID { 140 return auth.ErrUnauthorized 141 } 142 } 143 return nil 144 } 145 146 type SettingsAuthorizer struct { 147 store SettingsStorer 148 } 149 150 // NewSettingsAuthorizer wraps the given store with authorization methods. 151 func NewSettingsAuthorizer(store SettingsStorer) *SettingsAuthorizer { 152 return &SettingsAuthorizer{store: store} 153 } 154 155 // Create is allowed by 156 // - the user owning the settings 157 // - an admin 158 // - internally 159 func (a *SettingsAuthorizer) Create(ctx context.Context, settings *NewSettings) (*Settings, error) { 160 if err := a.adminOrOwned(ctx, settings.UserID); err != nil { 161 return nil, err 162 } 163 return a.store.Create(ctx, settings) 164 } 165 166 // Update is allowed by 167 // - the user owning the settings 168 // - an admin 169 // - internally 170 func (a *SettingsAuthorizer) Update(ctx context.Context, settings *Settings) (*Settings, error) { 171 if err := a.adminOrOwned(ctx, settings.UserID); err != nil { 172 return nil, err 173 } 174 175 return a.store.Update(ctx, settings) 176 } 177 178 // Delete is allowed by 179 // - the user owning the settings 180 // - an admin 181 // - internally 182 func (a *SettingsAuthorizer) Delete(ctx context.Context, id string) error { 183 if err := a.adminOrOwned(ctx, id); err != nil { 184 return err 185 } 186 187 return a.store.Delete(ctx, id) 188 } 189 190 // FindByID is allowed by 191 // - the user owning the settings 192 // - an admin 193 // - internally 194 func (a *SettingsAuthorizer) FindByID(ctx context.Context, id string) (*Settings, error) { 195 if err := a.adminOrOwned(ctx, id); err != nil { 196 return nil, err 197 } 198 199 return a.store.FindByID(ctx, id) 200 } 201 202 // Find is allowed by 203 // - normal and moderator can only retrieve their own settings 204 // - an admin 205 // - internally 206 func (a *SettingsAuthorizer) Find(ctx context.Context, params *crud.FindParams[SettingsFilters]) ([]*Settings, int, error) { 207 role, err := auth.RoleFromContext(ctx) 208 if err != nil { 209 return nil, 0, auth.ErrUnauthorized 210 } 211 if role == auth.RoleNormal || role == auth.RoleModerator { 212 if params == nil { 213 params = &crud.FindParams[SettingsFilters]{} 214 } 215 if params.Filters == nil { 216 params.Filters = &SettingsFilters{} 217 } 218 id, err := auth.UserIDFromContext(ctx) 219 if err != nil { 220 return nil, 0, auth.ErrUnauthorized 221 } 222 params.Filters.UserID = &id 223 } 224 225 return a.store.Find(ctx, params) 226 } 227 228 func (a *SettingsAuthorizer) adminOrOwned(ctx context.Context, id string) error { 229 role, err := auth.RoleFromContext(ctx) 230 if err != nil { 231 return auth.ErrUnauthorized 232 } 233 if role == auth.RoleNormal || role == auth.RoleModerator { 234 userID, err := auth.UserIDFromContext(ctx) 235 if err != nil { 236 return auth.ErrUnauthorized 237 } 238 239 if id != userID { 240 return auth.ErrUnauthorized 241 } 242 } 243 return nil 244 }