Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upload submissions #1

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ generate:
buf generate --path ./proto/user/address.proto
buf generate --path ./proto/user/streetaddress.proto
buf generate --path ./proto/user/user_messages.proto
buf generate --path ./proto/user/upload_submissions_messages.proto
buf generate --path ./proto/user/usergroup_messages.proto
buf generate --path ./proto/user/track_messages.proto
buf generate --path ./proto/user/trackgroup_messages.proto
buf generate --path ./proto/user/user.proto
# Generate static assets for OpenAPI UI
statik -m -f -src third_party/OpenAPI/
Expand Down
89 changes: 52 additions & 37 deletions authorization/auth_interceptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,43 +195,10 @@ func (interceptor *AuthInterceptor) authorize(ctx context.Context, req interface
if isPublicAccessMethod {
// everyone can access but check it's against their own ID
if activeRole > int32(model.LabelRole) {
// dealing with normal users, Label admins can maintain own artist content.
// attempt to extract the Id from all the possible request types dealing with failure
// TODO a bit nasty, can we make this more elegant and less opinionated?
var id string
userReq, ok := req.(*pbUser.UserRequest)
if !ok {
userUpdateReq, ok := req.(*pbUser.UserUpdateRequest)
if !ok {
userGroupCreateReq, ok := req.(*pbUser.UserGroupCreateRequest)
if !ok {
userGroupUpdateReq, ok := req.(*pbUser.UserGroupUpdateRequest)
if !ok {
return status.Errorf(codes.PermissionDenied, "UUID in request is not valid")
} else {

newUserGroup := new(model.UserGroup)

err = interceptor.db.NewSelect().
Model(newUserGroup).
Where("owner_id = ?", accessTokenRecord.UserID).
Where("id = ?", userGroupUpdateReq.Id).
Scan(ctx)

if err != nil {
return status.Errorf(codes.PermissionDenied, "Supplied UUID for User Group is not valid or logged in User doesn't own Group")
}

id = accessTokenRecord.UserID.String()
}
} else {
id = userGroupCreateReq.Id
}
} else {
id = userUpdateReq.Id
}
} else {
id = userReq.Id
id, err := interceptor.extractUserIdFromReq(ctx, req, accessTokenRecord)

if err != nil {
return err
}

ID, err := uuid.Parse(id)
Expand Down Expand Up @@ -314,6 +281,54 @@ func (interceptor *AuthInterceptor) Authenticate(token string) (*model.AccessTok
return accessToken, nil
}

func (interceptor *AuthInterceptor) extractUserIdFromReq(ctx context.Context, req interface{}, accessTokenRecord *model.AccessToken) (string, error) {
// Dealing with normal users, Label admins can maintain own artist content.
// attempt to extract the Id from all the possible request types dealing with failure
userReq, ok := req.(*pbUser.UserRequest)

if ok {
return userReq.Id, nil
}

userUpdateReq, ok := req.(*pbUser.UserUpdateRequest)

if ok {
return userUpdateReq.Id, nil
}

userGroupCreateReq, ok := req.(*pbUser.UserGroupCreateRequest)

if ok {
return userGroupCreateReq.Id, nil
}

userGroupUpdateReq, ok := req.(*pbUser.UserGroupUpdateRequest)

if ok {
newUserGroup := new(model.UserGroup)

err := interceptor.db.NewSelect().
Model(newUserGroup).
Where("owner_id = ?", accessTokenRecord.UserID).
Where("id = ?", userGroupUpdateReq.Id).
Scan(ctx)

if err != nil {
return "", status.Errorf(codes.PermissionDenied, "Supplied UUID for User Group is not valid or logged in User doesn't own Group")
}

return accessTokenRecord.UserID.String(), nil
}

uploadSubmissionAddReq, ok := req.(*pbUser.UploadSubmissionAddRequest)

if ok {
return uploadSubmissionAddReq.Id, nil
}

return "", status.Errorf(codes.PermissionDenied, "UUID in request is not valid")
}

func (interceptor *AuthInterceptor) find(slice []string, val string) (int, bool) {
for i, item := range slice {
if item == val {
Expand Down
4 changes: 2 additions & 2 deletions conf.local.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ refreshtoken:

access:
no_token_methods: "/user.ResonateUser/AddUser,/user.ResonateUser/GetUserGroup"
public_methods: "/user.ResonateUser/GetUser,/user.ResonateUser/GetUserCredits,/user.ResonateUser/GetUserMembership,/user.ResonateUser/UpdateUser,/user.ResonateUser/AddUserGroup,/user.ResonateUser/UpdateUserGroup,/user.ResonateUser/ListUsersUserGroups"
write_methods: "/user.ResonateUser/DeleteUser,/user.ResonateUser/UpdateUser,/user.ResonateUser/AddUserGroup,/user.ResonateUser/UpdateUserGroup"
public_methods: "/user.ResonateUser/AddUploadSubmission,/user.ResonateUser/UpdateUploadSubmission,/user.ResonateUser/DeleteUploadSubmission,/user.ResonateUser/GetUser,/user.ResonateUser/GetUserCredits,/user.ResonateUser/GetUserMembership,/user.ResonateUser/UpdateUser,/user.ResonateUser/AddUserGroup,/user.ResonateUser/UpdateUserGroup,/user.ResonateUser/ListUsersUserGroups"
write_methods: "/user.ResonateUser/AddUploadSubmission,/user.ResonateUser/UpdateUploadSubmission,/user.ResonateUser/DeleteUploadSubmission,/user.ResonateUser/DeleteUser,/user.ResonateUser/UpdateUser,/user.ResonateUser/AddUserGroup,/user.ResonateUser/UpdateUserGroup"

application:
min_password_strength: 0 # Minimum password zxcvbn strength
Expand Down
60 changes: 60 additions & 0 deletions migrations/22052021010104_trackserver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package migrations

import (
"context"
"fmt"

"github.com/uptrace/bun"

"github.com/resonatecoop/user-api/model"
)

func init() {

// Drop and create tables.
models := []interface{}{
(*model.UploadSubmission)(nil),
(*model.Track)(nil),
(*model.TrackGroup)(nil),
(*model.Play)(nil),
}

Migrations.MustRegister(func(ctx context.Context, db *bun.DB) error {
fmt.Print(" [up migration] ")

if _, err := db.Exec(`CREATE TYPE track_status AS ENUM ('paid', 'free', 'both');`); err != nil {
return err
}

if _, err := db.Exec(`CREATE TYPE play_type AS ENUM ('paid', 'free');`); err != nil {
return err
}

if _, err := db.Exec(`CREATE TYPE track_group_type AS ENUM ('lp', 'ep', 'single', 'playlist');`); err != nil {
return err
}

for _, model := range models {
_, err := db.NewDropTable().Model(model).IfExists().Exec(ctx)
if err != nil {
panic(err)
}

_, err = db.NewCreateTable().Model(model).Exec(ctx)
if err != nil {
panic(err)
}
}

return nil
}, func(ctx context.Context, db *bun.DB) error {
fmt.Print(" [down migration] ")
for _, this_model := range models {
_, err := db.NewDropTable().Model(this_model).IfExists().Exec(ctx)
if err != nil {
panic(err)
}
}
return nil
})
}
31 changes: 31 additions & 0 deletions model/play.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package model

import (
"context"

uuid "github.com/google/uuid"
"github.com/uptrace/bun"
)

type Play struct {
IDRecord
UserId uuid.UUID `bun:",type:uuid,notnull"`
TrackId uuid.UUID `bun:",type:uuid,notnull"`
Type string `bun:"type:play_type,notnull"`
Credits float32 `bun:",notnull"`
}

// Count number of times a track has been played (and paid) by a user
func CountPlays(ctx context.Context, db *bun.DB, trackId uuid.UUID, userId uuid.UUID) (int32, error) {
play := Play{}
count, err := db.NewSelect().
Model(play).
Where("user_id = ?", userId).
Where("track_id = ?", trackId).
Where("type = 'paid'").
Count(ctx)
if err != nil {
return 0, err
}
return int32(count), nil
}
36 changes: 36 additions & 0 deletions model/track.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package model

import (
uuid "github.com/google/uuid"
)

type Track struct {
IDRecord
Title string `bun:",notnull"`
Status string `bun:"type:track_status,notnull"`
Enabled bool `bun:",notnull"`
TrackNumber int32 `bun:",notnull"`
Duration float32
Download bool `bun:",notnull"` // Allow or disallow download

TrackGroups []uuid.UUID `bun:",type:uuid[],array"`
FavoriteOfUsers []uuid.UUID `bun:",type:uuid[],array"`

TrackServerId uuid.UUID `bun:"type:uuid,notnull"`

OwnerId uuid.UUID `bun:"type:uuid,notnull"`
Owner *User `bun:"rel:has-one"`

UserGroupId uuid.UUID `bun:"type:uuid,notnull"`
UserGroup *UserGroup `bun:"rel:has-one"` // track belongs to user group (the one who gets paid)

Artists []uuid.UUID `bun:",type:uuid[]" pg:",array"` // for display purposes
Tags []uuid.UUID `bun:",type:uuid[]" pg:",array"`

Composers map[string]string `pg:",hstore"`
Performers map[string]string `pg:",hstore"`

ISRC string

// Plays []User `pg:"many2many:plays"` Payment API
}
40 changes: 40 additions & 0 deletions model/track_group.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package model

import (
"time"

"github.com/google/uuid"
)

// TrackGroup
type TrackGroup struct {
IDRecord

Title string `bun:",notnull"`
Slug string `bun:",notnull"` // Slug title
ReleaseDate time.Time `bun:",notnull"`
Type string `bun:"type:track_group_type,notnull"` // EP, LP, Single, Playlist
Cover []byte `bun:",notnull"`
DisplayArtist string // for display purposes, e.g. "Various" for compilation
MultipleComposers bool `bun:",notnull"`
Private bool `bun:",notnull"`
About string

OwnerId uuid.UUID `bun:"type:uuid,notnull"`
Owner *User `bun:"rel:has-one"`

UserGroupId uuid.UUID `bun:"type:uuid,default:uuid_nil()"` // track group belongs to user group, can be null if user playlist
LabelId uuid.UUID `bun:"type:uuid,default:uuid_nil()"`

Tracks []uuid.UUID `bun:",type:uuid[]" pg:",array"`
Tags []uuid.UUID `bun:",type:uuid[]" pg:",array"`

TerritoriesIncl []string `pg:",array"`
CLineYear time.Time
PLineYear time.Time
CLineText string
PLineText string
RightExpiryDate time.Time
TotalVolumes int
CatalogNumber string
}
16 changes: 16 additions & 0 deletions model/upload_submission.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package model

import "github.com/google/uuid"

// UploadSubmission
type UploadSubmission struct {
IDRecord
Active bool `bun:"default:true,notnull"`
Description string `bun:",notnull"`
Files []uuid.UUID `bun:",type:uuid[],array"`
Name string `bun:",notnull"`
TrackGroup *TrackGroup `bun:"rel:has-one"`
TrackGroupID uuid.UUID `bun:"type:uuid"`
UserID uuid.UUID `bun:"type:uuid,notnull"`
User *User `bun:"rel:has-one"`
}
32 changes: 23 additions & 9 deletions pkg/uuid/uuid.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package uuidpkg

import (
"errors"

uuid "github.com/google/uuid"
)

Expand All @@ -9,7 +11,7 @@ func IsValidUUID(u string) bool {
return err == nil
}

//ConvertUUIDToStrArray returns a slice of uuids for given slive of strings
//ConvertUUIDToStrArray returns a slice of strings for given slice of uuids
func ConvertUUIDToStrArray(uuids []uuid.UUID) []string {
strArray := make([]string, len(uuids))
for i := range uuids {
Expand All @@ -18,14 +20,26 @@ func ConvertUUIDToStrArray(uuids []uuid.UUID) []string {
return strArray
}

// // GetUUIDFromString returns id as string and returns error if not a valid uuid
// func GetUUIDFromString(id string) (uuid.UUID, twirp.Error) {
// uid, err := uuid.FromString(id)
// if err != nil {
// return uuid.UUID{}, twirp.InvalidArgumentError("id", "must be a valid uuid")
// }
// return uid, nil
// }
//ConvertUUIDToStrArray returns a slice of uuids for given slice of strings
func ConvertStrToUUIDArray(str []string) []uuid.UUID {
uuidArray := make([]uuid.UUID, len(str))
for i := range str {
u, err := uuid.Parse(str[i])
if err != nil {
uuidArray[i] = u
}
}
return uuidArray
}

// GetUUIDFromString returns id as string and returns error if not a valid uuid
func GetUUIDFromString(id string) (uuid.UUID, error) {
uid, err := uuid.Parse(id)
if err != nil {
return uuid.UUID{}, errors.New("must be a valid uuid")
}
return uid, nil
}

// Difference returns difference between two slices of uuids
func Difference(a, b []uuid.UUID) []uuid.UUID {
Expand Down
12 changes: 12 additions & 0 deletions proto/user/common.proto
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,18 @@ message RelatedUserGroup {
bytes avatar = 3;
}

message RelatedTrackGroup {
string id = 1; // required
string title = 2; // required
bytes cover = 3; // required
string type = 4; // required
string about = 5;
bool private = 6;
string display_artist = 7;
int32 total_tracks = 8;
RelatedUserGroup user_group = 9;
}

message User {
string id = 1; // required
string username = 2; // required
Expand Down
Loading