eintopf.info@v0.13.16/service/revent/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 revent
    17  
    18  import (
    19  	"context"
    20  	"fmt"
    21  	"time"
    22  
    23  	"eintopf.info/internal/crud"
    24  	"eintopf.info/service/auth"
    25  	"eintopf.info/service/event"
    26  )
    27  
    28  // authorizer wraps a Storer. It checks the authorization before propagating
    29  // the call to the wrapped storer.
    30  type authorizer struct {
    31  	service Service
    32  }
    33  
    34  // NewAuthorizer returns a new role manager limiting access to the provided
    35  // store.
    36  func NewAuthorizer(service Service) Service {
    37  	return &authorizer{service}
    38  }
    39  
    40  // Create can be called by all users.
    41  func (r *authorizer) Create(ctx context.Context, newRepeatingEvent *NewRepeatingEvent) (*RepeatingEvent, error) {
    42  	_, err := auth.UserIDFromContext(ctx)
    43  	if err != nil {
    44  		return nil, auth.ErrUnauthorized
    45  	}
    46  	return r.service.Create(ctx, newRepeatingEvent)
    47  }
    48  
    49  // Update can be called by an admin user or the repeatingEvent author.
    50  func (r *authorizer) Update(ctx context.Context, repeatingEvent *RepeatingEvent) (*RepeatingEvent, error) {
    51  	if err := r.adminOrOwned(ctx, repeatingEvent.ID); err != nil {
    52  		return nil, err
    53  	}
    54  	oldRepeatingEvent, err := r.service.FindByID(ctx, repeatingEvent.ID)
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  	if oldRepeatingEvent == nil {
    59  		return nil, fmt.Errorf("cant find repeatingEvent with id: %s", repeatingEvent.ID)
    60  	}
    61  	if oldRepeatingEvent.Deactivated != repeatingEvent.Deactivated {
    62  		role, err := auth.RoleFromContext(ctx)
    63  		if err != nil {
    64  			return nil, auth.ErrUnauthorized
    65  		}
    66  		if role == auth.RoleNormal {
    67  			return nil, auth.ErrUnauthorized
    68  		}
    69  	}
    70  	return r.service.Update(ctx, repeatingEvent)
    71  }
    72  
    73  // Delete can be called by an admin user or the repeatingEvent author.
    74  func (r *authorizer) Delete(ctx context.Context, id string) error {
    75  	if err := r.adminOrOwned(ctx, id); err != nil {
    76  		return err
    77  	}
    78  	return r.service.Delete(ctx, id)
    79  }
    80  
    81  // FindByID can be called by everyone.
    82  func (r *authorizer) FindByID(ctx context.Context, id string) (*RepeatingEvent, error) {
    83  	return r.service.FindByID(ctx, id)
    84  }
    85  
    86  // Find can be called by everyone.
    87  func (r *authorizer) Find(ctx context.Context, params *crud.FindParams[FindFilters]) ([]*RepeatingEvent, int, error) {
    88  	return r.service.Find(ctx, params)
    89  }
    90  
    91  func (a *authorizer) GenerateEvents(ctx context.Context, id string, start, end time.Time) ([]*event.Event, error) {
    92  	if err := a.adminOrOwned(ctx, id); err != nil {
    93  		return nil, err
    94  	}
    95  	return a.service.GenerateEvents(ctx, id, start, end)
    96  }
    97  
    98  func (r *authorizer) adminOrOwned(ctx context.Context, repeatingEventID 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  	repeatingEvent, err := r.FindByID(ctx, repeatingEventID)
   108  	if err != nil {
   109  		return err
   110  	}
   111  	userID, err := auth.UserIDFromContext(ctx)
   112  	if err != nil {
   113  		return auth.ErrUnauthorized
   114  	}
   115  	if !repeatingEvent.IsOwned(userID) {
   116  		return auth.ErrUnauthorized
   117  	}
   118  
   119  	return nil
   120  }