Skip to content

Commit 93471b5

Browse files
committed
enhancement(graph): check the received bytes content type for profile photos
1 parent 6b7c004 commit 93471b5

2 files changed

Lines changed: 56 additions & 18 deletions

File tree

services/graph/pkg/service/v0/api_users_user_profile_photo.go

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ package svc
33
import (
44
"context"
55
"errors"
6+
"fmt"
67
"io"
78
"net/http"
9+
"strings"
810

911
"github.com/go-chi/render"
1012
"github.com/opencloud-eu/reva/v2/pkg/storage/utils/metadata"
@@ -33,6 +35,12 @@ var (
3335

3436
// ErrNoBytes is returned when no bytes are found
3537
ErrNoBytes = errors.New("no bytes")
38+
39+
// ErrInvalidContentType is returned when the content type is invalid
40+
ErrInvalidContentType = errors.New("invalid content type")
41+
42+
// ErrMissingArgument is returned when a required argument is missing
43+
ErrMissingArgument = errors.New("required argument is missing")
3644
)
3745

3846
// UsersUserProfilePhotoService is the implementation of the UsersUserProfilePhotoProvider interface
@@ -53,12 +61,7 @@ func NewUsersUserProfilePhotoService(storage metadata.Storage) (UsersUserProfile
5361

5462
// GetPhoto retrieves the requested photo
5563
func (s UsersUserProfilePhotoService) GetPhoto(ctx context.Context, id string) ([]byte, error) {
56-
photo, err := s.storage.SimpleDownload(ctx, id)
57-
if err != nil {
58-
return nil, err
59-
}
60-
61-
return photo, nil
64+
return s.storage.SimpleDownload(ctx, id)
6265
}
6366

6467
// DeletePhoto deletes the requested photo
@@ -68,6 +71,10 @@ func (s UsersUserProfilePhotoService) DeletePhoto(ctx context.Context, id string
6871

6972
// UpdatePhoto updates the requested photo
7073
func (s UsersUserProfilePhotoService) UpdatePhoto(ctx context.Context, id string, r io.Reader) error {
74+
if id == "" {
75+
return fmt.Errorf("%w: %s", ErrMissingArgument, "id")
76+
}
77+
7178
photo, err := io.ReadAll(r)
7279
if err != nil {
7380
return err
@@ -77,6 +84,11 @@ func (s UsersUserProfilePhotoService) UpdatePhoto(ctx context.Context, id string
7784
return ErrNoBytes
7885
}
7986

87+
contentType := http.DetectContentType(photo)
88+
if !strings.HasPrefix(contentType, "image/") {
89+
return fmt.Errorf("%w: %s", ErrInvalidContentType, contentType)
90+
}
91+
8092
return s.storage.SimpleUpload(ctx, id, photo)
8193
}
8294

services/graph/pkg/service/v0/api_users_user_profile_photo_test.go

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package svc_test
22

33
import (
4+
"bytes"
45
"context"
56
"errors"
67
"io"
@@ -17,25 +18,50 @@ import (
1718
svc "github.com/opencloud-eu/opencloud/services/graph/pkg/service/v0"
1819
)
1920

21+
func TestNewUsersUserProfilePhotoService(t *testing.T) {
22+
storage := mocks.NewStorage(t)
23+
storage.EXPECT().Init(mock.Anything, mock.Anything).RunAndReturn(func(ctx context.Context, id string) error { return nil })
24+
25+
service, err := svc.NewUsersUserProfilePhotoService(storage)
26+
assert.NoError(t, err)
27+
28+
t.Run("UpdatePhoto", func(t *testing.T) {
29+
t.Run("reports an error if id is empty", func(t *testing.T) {
30+
err := service.UpdatePhoto(context.Background(), "", bytes.NewReader([]byte{}))
31+
assert.ErrorIs(t, err, svc.ErrMissingArgument)
32+
})
33+
34+
t.Run("reports an error if the reader does not contain any bytes", func(t *testing.T) {
35+
err := service.UpdatePhoto(context.Background(), "123", bytes.NewReader([]byte{}))
36+
assert.ErrorIs(t, err, svc.ErrNoBytes)
37+
})
38+
39+
t.Run("reports an error if data is not an image", func(t *testing.T) {
40+
err := service.UpdatePhoto(context.Background(), "234", bytes.NewReader([]byte("not an image")))
41+
assert.ErrorIs(t, err, svc.ErrInvalidContentType)
42+
})
43+
})
44+
}
45+
2046
func TestUsersUserProfilePhotoApi(t *testing.T) {
2147
var (
22-
usersUserProfilePhotoProvider = mocks.NewUsersUserProfilePhotoProvider(t)
23-
dummyDataProvider = func(w http.ResponseWriter, r *http.Request) (string, bool) {
48+
serviceProvider = mocks.NewUsersUserProfilePhotoProvider(t)
49+
dataProvider = func(w http.ResponseWriter, r *http.Request) (string, bool) {
2450
return "123", true
2551
}
2652
)
2753

28-
api, err := svc.NewUsersUserProfilePhotoApi(usersUserProfilePhotoProvider, log.NopLogger())
54+
api, err := svc.NewUsersUserProfilePhotoApi(serviceProvider, log.NopLogger())
2955
assert.NoError(t, err)
3056

3157
t.Run("GetProfilePhoto", func(t *testing.T) {
3258
r := httptest.NewRequest(http.MethodGet, "/", nil)
33-
ep := api.GetProfilePhoto(dummyDataProvider)
59+
ep := api.GetProfilePhoto(dataProvider)
3460

3561
t.Run("fails if photo provider errors", func(t *testing.T) {
3662
w := httptest.NewRecorder()
3763

38-
usersUserProfilePhotoProvider.EXPECT().GetPhoto(mock.Anything, mock.Anything).RunAndReturn(func(ctx context.Context, s string) ([]byte, error) {
64+
serviceProvider.EXPECT().GetPhoto(mock.Anything, mock.Anything).RunAndReturn(func(ctx context.Context, s string) ([]byte, error) {
3965
return nil, errors.New("any")
4066
}).Once()
4167

@@ -47,7 +73,7 @@ func TestUsersUserProfilePhotoApi(t *testing.T) {
4773
t.Run("successfully returns the requested photo", func(t *testing.T) {
4874
w := httptest.NewRecorder()
4975

50-
usersUserProfilePhotoProvider.EXPECT().GetPhoto(mock.Anything, mock.Anything).RunAndReturn(func(ctx context.Context, s string) ([]byte, error) {
76+
serviceProvider.EXPECT().GetPhoto(mock.Anything, mock.Anything).RunAndReturn(func(ctx context.Context, s string) ([]byte, error) {
5177
return []byte("photo"), nil
5278
}).Once()
5379

@@ -60,12 +86,12 @@ func TestUsersUserProfilePhotoApi(t *testing.T) {
6086

6187
t.Run("DeleteProfilePhoto", func(t *testing.T) {
6288
r := httptest.NewRequest(http.MethodDelete, "/", nil)
63-
ep := api.DeleteProfilePhoto(dummyDataProvider)
89+
ep := api.DeleteProfilePhoto(dataProvider)
6490

6591
t.Run("fails if photo provider errors", func(t *testing.T) {
6692
w := httptest.NewRecorder()
6793

68-
usersUserProfilePhotoProvider.EXPECT().DeletePhoto(mock.Anything, mock.Anything).RunAndReturn(func(ctx context.Context, s string) error {
94+
serviceProvider.EXPECT().DeletePhoto(mock.Anything, mock.Anything).RunAndReturn(func(ctx context.Context, s string) error {
6995
return errors.New("any")
7096
}).Once()
7197

@@ -77,7 +103,7 @@ func TestUsersUserProfilePhotoApi(t *testing.T) {
77103
t.Run("successfully deletes the requested photo", func(t *testing.T) {
78104
w := httptest.NewRecorder()
79105

80-
usersUserProfilePhotoProvider.EXPECT().DeletePhoto(mock.Anything, mock.Anything).RunAndReturn(func(ctx context.Context, s string) error {
106+
serviceProvider.EXPECT().DeletePhoto(mock.Anything, mock.Anything).RunAndReturn(func(ctx context.Context, s string) error {
81107
return nil
82108
}).Once()
83109

@@ -89,12 +115,12 @@ func TestUsersUserProfilePhotoApi(t *testing.T) {
89115

90116
t.Run("UpsertProfilePhoto", func(t *testing.T) {
91117
r := httptest.NewRequest(http.MethodPut, "/", strings.NewReader("body"))
92-
ep := api.UpsertProfilePhoto(dummyDataProvider)
118+
ep := api.UpsertProfilePhoto(dataProvider)
93119

94120
t.Run("fails if photo provider errors", func(t *testing.T) {
95121
w := httptest.NewRecorder()
96122

97-
usersUserProfilePhotoProvider.EXPECT().UpdatePhoto(mock.Anything, mock.Anything, mock.Anything).RunAndReturn(func(ctx context.Context, s string, r io.Reader) error {
123+
serviceProvider.EXPECT().UpdatePhoto(mock.Anything, mock.Anything, mock.Anything).RunAndReturn(func(ctx context.Context, s string, r io.Reader) error {
98124
return errors.New("any")
99125
}).Once()
100126

@@ -106,7 +132,7 @@ func TestUsersUserProfilePhotoApi(t *testing.T) {
106132
t.Run("successfully upserts the photo", func(t *testing.T) {
107133
w := httptest.NewRecorder()
108134

109-
usersUserProfilePhotoProvider.EXPECT().UpdatePhoto(mock.Anything, mock.Anything, mock.Anything).RunAndReturn(func(ctx context.Context, s string, r io.Reader) error {
135+
serviceProvider.EXPECT().UpdatePhoto(mock.Anything, mock.Anything, mock.Anything).RunAndReturn(func(ctx context.Context, s string, r io.Reader) error {
110136
return nil
111137
}).Once()
112138

0 commit comments

Comments
 (0)