Skip to content
This repository has been archived by the owner on May 10, 2024. It is now read-only.

Commit

Permalink
feat: #164 Partially (WIP) implement validation for the upload endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
blackandred committed Feb 15, 2022
1 parent f0451a2 commit d5c9328
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 4 deletions.
73 changes: 73 additions & 0 deletions server-go/collections/entity.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
package collections

import (
"encoding/json"
"errors"
"fmt"
"github.com/riotkit-org/backup-repository/config"
"github.com/riotkit-org/backup-repository/security"
"github.com/riotkit-org/backup-repository/users"
cron "github.com/robfig/cron/v3"
"time"
)

type StrategySpec struct {
Expand All @@ -13,6 +19,58 @@ type StrategySpec struct {
type BackupWindow struct {
From string `json:"from"`
Duration string `json:"duration"`

parsed cron.Schedule
parsedDuration time.Duration
}

// UnmarshalJSON performs a validation when decoding a JSON
func (b *BackupWindow) UnmarshalJSON(in []byte) error {
v := struct {
From string `json:"from"`
Duration string `json:"duration"`
}{}

if unmarshalErr := json.Unmarshal(in, &v); unmarshalErr != nil {
return unmarshalErr
}

parser := cron.NewParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.DowOptional)
err := errors.New("")
b.parsed, err = parser.Parse(b.From)

if err != nil {
return errors.New(fmt.Sprintf("cannot parse Backup Window: %v. Error: %v", b.From, err))
}

return nil
}

func (b *BackupWindow) IsInWindowNow(current time.Time) (bool, error) {
nextRun := b.parsed.Next(current)
var startDate time.Time
retries := 0

// First calculate startDate run to get "START DATE" and calculate "END DATE"
// because the library does not provide a "Previous" method unfortunately
for true {
retries = retries + 1
startDate = current.Add(time.Minute * time.Duration(-1))

if startDate.Format(time.RFC822) != nextRun.Format(time.RFC822) {
break
}

// six months
if retries > 60*24*30*6 {
return false, errors.New("cannot find a previous date in the backup window")
}
}

endDate := startDate.Add(b.parsedDuration)

// previous run -> previous run + duration
return current.After(startDate) && current.Before(endDate), nil
}

type BackupWindows []BackupWindow
Expand All @@ -31,4 +89,19 @@ type Spec struct {

type Collection struct {
Metadata config.ObjectMetadata `json:"metadata"`
Spec Spec `json:"spec"`
}

func (c Collection) CanUploadToMe(user *users.User) bool {
if user.Spec.Roles.HasRole(security.RoleBackupUploader) {
return true
}

for _, permitted := range c.Spec.AccessControl {
if permitted.UserName == user.Metadata.Name && permitted.Roles.HasRole(security.RoleBackupUploader) {
return true
}
}

return false
}
28 changes: 27 additions & 1 deletion server-go/collections/service.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package collections

import "github.com/riotkit-org/backup-repository/config"
import (
"fmt"
"github.com/riotkit-org/backup-repository/config"
"github.com/sirupsen/logrus"
"time"
)

type Service struct {
repository collectionRepository
Expand All @@ -10,6 +15,27 @@ func (s *Service) GetCollectionById(id string) (*Collection, error) {
return s.repository.getById(id)
}

func (s *Service) ValidateIsBackupWindowAllowingToUpload(collection *Collection, contextTime time.Time) bool {
// no defined Backup Windows = no limits, ITS OPTIONAL
if len(collection.Spec.Windows) == 0 {
return true
}

for _, window := range collection.Spec.Windows {
result, err := window.IsInWindowNow(contextTime)

if err != nil {
logrus.Error(fmt.Sprintf("Backup Window validation error (collection id=%v): %v", collection.Metadata.Name, err))
}

if result {
return true
}
}

return false
}

func NewService(config config.ConfigurationProvider) Service {
return Service{
repository: collectionRepository{
Expand Down
1 change: 1 addition & 0 deletions server-go/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
Expand Down
21 changes: 19 additions & 2 deletions server-go/http/collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,42 @@ import (
"errors"
"github.com/gin-gonic/gin"
"github.com/riotkit-org/backup-repository/core"
"github.com/riotkit-org/backup-repository/security"
"time"
)

func addUploadRoute(r *gin.RouterGroup, ctx *core.ApplicationContainer) {
r.POST("/repository/collection/:collectionId/version", func(c *gin.Context) {
// todo: check if collection exists
// todo: check if backup window is OK
// todo: check if rotation strategy allows uploading
// todo: deactivate token if temporary token is used
// todo: handle upload
// todo: check uploaded file size, respect quotas and additional space
// todo: check if there are gpg header and footer
// todo: handle upload interruptions

ctxUser, _ := GetContextUser(ctx, c)

// Check if Colection exists
collection, err := ctx.Collections.GetCollectionById(c.Param("collectionId"))
if err != nil {
NotFoundResponse(c, errors.New("cannot find specified collection"))
return
}

// Check permissions
if !collection.CanUploadToMe(ctxUser) {
UnauthorizedResponse(c, errors.New("not authorized to upload versions to this collection"))
}

// Backup Windows support
if !ctx.Collections.ValidateIsBackupWindowAllowingToUpload(collection, time.Now()) &&
!ctxUser.Spec.Roles.HasRole(security.RoleUploadsAnytime) {

UnauthorizedResponse(c, errors.New("backup window does not allow you to send a backup at this time. "+
"You need a token from a user that has a special permission 'uploadsAnytime'"))
return
}

println(collection)
})
}
5 changes: 4 additions & 1 deletion server-go/security/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@ const (
RoleCollectionManager = "collectionManager"
RoleBackupDownloader = "backupDownloader"
RoleBackupUploader = "backupUploader"
RoleSysAdmin = "systemAdmin"

// RoleUploadsAnytime allows uploading versions regardless of Backup Windows
RoleUploadsAnytime = "uploadsAnytime"
RoleSysAdmin = "systemAdmin"
)

0 comments on commit d5c9328

Please sign in to comment.