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

Commit

Permalink
Create Entitlement Endpoint (#250)
Browse files Browse the repository at this point in the history
* Create Entitlement Endpoint

* update common

* check entitlement claims upon user creation
  • Loading branch information
AnatoleAM authored Dec 12, 2023
1 parent 5252e89 commit 2e5bb1b
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 5 deletions.
2 changes: 0 additions & 2 deletions data/mutate/user.delete.mutation.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package mutate

import (
"context"
"fmt"

"github.com/seventv/common/errors"
"github.com/seventv/common/mongo"
Expand All @@ -22,7 +21,6 @@ func (m *Mutate) DeleteUser(ctx context.Context, opt DeleteUserOptions) (int, er
if opt.Actor.GetHighestRole().Position <= opt.Victim.GetHighestRole().Position {
return 0, errors.ErrInsufficientPrivilege()
}
fmt.Println(opt.Victim, opt.Actor)

// Delete all EUD
for _, query := range userDeleteQueries(opt.Victim.ID) {
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ require (
github.com/nats-io/nats.go v1.28.0
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/prometheus/client_golang v1.14.0
github.com/seventv/common v0.0.0-20231212092216-2658fa1b97ba
github.com/seventv/common v0.0.0-20231212143655-048a247f3aa4
github.com/seventv/compactdisc v0.0.0-20221006190906-ccfe99954e48
github.com/seventv/image-processor/go v0.0.0-20221128171540-d050701ac324
github.com/seventv/message-queue/go v0.0.0-20231201171845-1bb9d5db6881
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,10 @@ github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/seventv/common v0.0.0-20231212092216-2658fa1b97ba h1:byOf2HOo7LZyyfg8j7LePLtODt1vBjOk4D6dBGNETo8=
github.com/seventv/common v0.0.0-20231212092216-2658fa1b97ba/go.mod h1:jHHFe3uNMyzb/ReDqMvaI/A1euvV/PW/G+2PhcWQqWw=
github.com/seventv/common v0.0.0-20231212140741-9412bffd7525 h1:3LdJj533sxENKoUkJLx3p7WHj0YuLko+CyitAYvC5pk=
github.com/seventv/common v0.0.0-20231212140741-9412bffd7525/go.mod h1:jHHFe3uNMyzb/ReDqMvaI/A1euvV/PW/G+2PhcWQqWw=
github.com/seventv/common v0.0.0-20231212143655-048a247f3aa4 h1:TNDOnWqZfRJSdnjD4jq8RueLQD8rL52+6HgPXg9W5s0=
github.com/seventv/common v0.0.0-20231212143655-048a247f3aa4/go.mod h1:jHHFe3uNMyzb/ReDqMvaI/A1euvV/PW/G+2PhcWQqWw=
github.com/seventv/compactdisc v0.0.0-20221006190906-ccfe99954e48 h1:IqWrtt/yob45YnOQ5Wwkbf8qP22eKVtg0WzfyEkGnFg=
github.com/seventv/compactdisc v0.0.0-20221006190906-ccfe99954e48/go.mod h1:T+ldp0YQe03s44+A5HHHI/jB3ZmWqOIaNouEqAS+1Dk=
github.com/seventv/image-processor/go v0.0.0-20221128171540-d050701ac324 h1:iU3wWepRTbkNoTAPR23m6TAW6Yb9pOMCYVr0K++OBAw=
Expand Down
33 changes: 31 additions & 2 deletions internal/api/rest/v3/routes/auth/auth.route.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,14 +170,23 @@ func setupUser(
platform structures.UserConnectionPlatform,
grant auth.OAuth2AuthorizedResponse,
) errors.APIError {
var (
userID primitive.ObjectID
userConn structures.UserConnection[bson.Raw]
)

// Create the user
if ub.User.ID.IsZero() {
userID = primitive.NewObjectIDFromTimestamp(time.Now())

userConn = formatUserConnection(id, platform, b, grant)

ub.User = structures.User{
ID: primitive.NewObjectIDFromTimestamp(time.Now()),
ID: userID,
TokenVersion: 1.0,
RoleIDs: []primitive.ObjectID{},
Editors: []structures.UserEditor{},
Connections: []structures.UserConnection[bson.Raw]{formatUserConnection(id, platform, b, grant)},
Connections: []structures.UserConnection[bson.Raw]{userConn},
State: structures.UserState{
LastLoginDate: time.Now(),
LastVisitDate: time.Now(),
Expand Down Expand Up @@ -213,6 +222,10 @@ func setupUser(
con := formatUserConnection(id, platform, b, grant)
ub.AddConnection(con)

userID = ub.User.ID

userConn = con

// eventapi: dispatch the connection create event
gctx.Inst().Events.Dispatch(events.EventTypeUpdateUser, events.ChangeMap{
ID: ub.User.ID,
Expand All @@ -238,6 +251,22 @@ func setupUser(
}
}

if !userID.IsZero() && userConn.ID != "" {
// Check entitlement claims
if _, err := gctx.Inst().Mongo.Collection(mongo.CollectionNameEntitlements).UpdateMany(ctx, bson.M{
"claim": bson.M{"$exists": true},
"claim.platform": platform,
"claim.id": userConn.ID,
}, bson.M{
"$unset": bson.M{"claim": 1},
"$set": bson.M{
"user_id": userID,
},
}); err != nil {
ctx.Log().Errorw("mongo, failed to update entitlement claims")
}
}

return nil
}

Expand Down
100 changes: 100 additions & 0 deletions internal/api/rest/v3/routes/entitlements/entitlements.create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package entitlements

import (
"encoding/json"
"time"

"github.com/seventv/api/internal/api/rest/middleware"
"github.com/seventv/api/internal/api/rest/rest"
"github.com/seventv/api/internal/global"
"github.com/seventv/common/errors"
"github.com/seventv/common/mongo"
"github.com/seventv/common/structures/v3"
"go.mongodb.org/mongo-driver/bson/primitive"
)

type create struct {
gctx global.Context
}

func newCreate(gctx global.Context) rest.Route {
return &create{gctx}
}

func (r *create) Config() rest.RouteConfig {
return rest.RouteConfig{
URI: "",
Method: rest.POST,
Middleware: []rest.Middleware{
middleware.Auth(r.gctx, true),
},
}
}

func (r *create) Handler(ctx *rest.Ctx) rest.APIError {
actor, ok := ctx.GetActor()
if !ok || !actor.HasPermission(structures.RolePermissionManageEntitlements) {
return errors.ErrInsufficientPrivilege()
}

var body createEntitlementData
if err := json.Unmarshal(ctx.Request.Body(), &body); err != nil {
return errors.ErrInvalidRequest()
}

// Validate ROLE entitlements
// Needs superadmin
if body.Kind == structures.EntitlementKindRole && !actor.HasPermission(structures.RolePermissionSuperAdministrator) {
return errors.ErrInsufficientPrivilege()
}

id := primitive.NewObjectIDFromTimestamp(time.Now())

eb := structures.NewEntitlementBuilder(structures.Entitlement[structures.EntitlementDataBase]{
ID: id,
}).
SetKind(body.Kind).
SetUserID(body.UserID).
SetCondition(body.Condition).
SetData(structures.EntitlementDataBase{
RefID: body.ObjectRef,
}).
SetApp(structures.EntitlementApp{
Name: body.AppName,
ActorID: actor.ID.Hex(),
State: body.AppState,
})

// Set claim (only if UserID empty)
if body.Claim != nil && body.UserID.IsZero() {
u, _ := r.gctx.Inst().Loaders.UserByConnectionID(body.Claim.Platform).Load(body.Claim.ID)

if u.ID.IsZero() {
eb.SetClaim(structures.EntitlementClaim{
Platform: body.Claim.Platform,
ID: body.Claim.ID,
})
} else {
// if the user was found by claim, assign the entitlement directly
eb.SetUserID(u.ID)
}
}

if _, err := r.gctx.Inst().Mongo.Collection(mongo.CollectionNameEntitlements).InsertOne(ctx, eb.Entitlement); err != nil {
ctx.Log().Errorw("mongo, couldn't create entitlement")

return errors.ErrInternalServerError()
}

return ctx.JSON(rest.OK, eb.Entitlement)
}

type createEntitlementData struct {
Kind structures.EntitlementKind `json:"kind"`
ObjectRef primitive.ObjectID `json:"object_ref"`
UserID primitive.ObjectID `json:"user_id"`
Condition structures.EntitlementCondition `json:"condition"`
Claim *structures.EntitlementClaim `json:"claim"`
AppName string `json:"app_name"`
AppState map[string]any `json:"app_state"`
}
29 changes: 29 additions & 0 deletions internal/api/rest/v3/routes/entitlements/entitlements.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package entitlements

import (
"github.com/seventv/api/internal/api/rest/rest"
"github.com/seventv/api/internal/global"
)

type entitlementRoute struct {
gctx global.Context
}

func New(gctx global.Context) rest.Route {
return &entitlementRoute{gctx}
}

func (r *entitlementRoute) Config() rest.RouteConfig {
return rest.RouteConfig{
URI: "/entitlements",
Method: rest.GET,
Children: []rest.Route{
newCreate(r.gctx),
},
Middleware: []rest.Middleware{},
}
}

func (r *entitlementRoute) Handler(ctx *rest.Ctx) rest.APIError {
return nil
}
2 changes: 2 additions & 0 deletions internal/api/rest/v3/routes/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/seventv/api/internal/api/rest/v3/routes/docs"
emote_sets "github.com/seventv/api/internal/api/rest/v3/routes/emote-sets"
"github.com/seventv/api/internal/api/rest/v3/routes/emotes"
"github.com/seventv/api/internal/api/rest/v3/routes/entitlements"
"github.com/seventv/api/internal/api/rest/v3/routes/users"
"github.com/seventv/api/internal/global"
)
Expand All @@ -36,6 +37,7 @@ func (r *Route) Config() rest.RouteConfig {
emotes.New(r.Ctx),
emote_sets.New(r.Ctx),
users.New(r.Ctx),
entitlements.New(r.Ctx),
},
Middleware: []rest.Middleware{
middleware.SetCacheControl(r.Ctx, 30, nil),
Expand Down

0 comments on commit 2e5bb1b

Please sign in to comment.