Basic Auth Context

This commit is contained in:
Evan Reichard 2021-02-02 22:55:35 -05:00
parent 7e6454c593
commit c39fe6ec24
13 changed files with 828 additions and 202 deletions

View File

@ -42,8 +42,8 @@ type ResolverRoot interface {
} }
type DirectiveRoot struct { type DirectiveRoot struct {
HasRole func(ctx context.Context, obj interface{}, next graphql.Resolver, role model.Role) (res interface{}, err error) HasMinRole func(ctx context.Context, obj interface{}, next graphql.Resolver, role model.Role) (res interface{}, err error)
Meta func(ctx context.Context, obj interface{}, next graphql.Resolver, gorm *string) (res interface{}, err error) Meta func(ctx context.Context, obj interface{}, next graphql.Resolver, gorm *string) (res interface{}, err error)
} }
type ComplexityRoot struct { type ComplexityRoot struct {
@ -59,6 +59,11 @@ type ComplexityRoot struct {
PageInfo func(childComplexity int) int PageInfo func(childComplexity int) int
} }
AuthResponse struct {
Error func(childComplexity int) int
Result func(childComplexity int) int
}
Device struct { Device struct {
CreatedAt func(childComplexity int) int CreatedAt func(childComplexity int) int
ID func(childComplexity int) int ID func(childComplexity int) int
@ -113,6 +118,9 @@ type ComplexityRoot struct {
Albums func(childComplexity int, filter *model.AlbumFilter, count *int, page *int) int Albums func(childComplexity int, filter *model.AlbumFilter, count *int, page *int) int
Device func(childComplexity int, id string) int Device func(childComplexity int, id string) int
Devices func(childComplexity int, filter *model.DeviceFilter, count *int, page *int) int Devices func(childComplexity int, filter *model.DeviceFilter, count *int, page *int) int
Login func(childComplexity int, user string, password string) int
Logout func(childComplexity int) int
Me func(childComplexity int) int
MediaItem func(childComplexity int, id string) int MediaItem func(childComplexity int, id string) int
MediaItems func(childComplexity int, filter *model.MediaItemFilter, count *int, page *int) int MediaItems func(childComplexity int, filter *model.MediaItemFilter, count *int, page *int) int
Tag func(childComplexity int, id string) int Tag func(childComplexity int, id string) int
@ -160,11 +168,14 @@ type MutationResolver interface {
CreateUser(ctx context.Context, input model.NewUser) (*model.User, error) CreateUser(ctx context.Context, input model.NewUser) (*model.User, error)
} }
type QueryResolver interface { type QueryResolver interface {
Login(ctx context.Context, user string, password string) (model.AuthResult, error)
Logout(ctx context.Context) (model.AuthResult, error)
MediaItem(ctx context.Context, id string) (*model.MediaItem, error) MediaItem(ctx context.Context, id string) (*model.MediaItem, error)
Device(ctx context.Context, id string) (*model.Device, error) Device(ctx context.Context, id string) (*model.Device, error)
Album(ctx context.Context, id string) (*model.Album, error) Album(ctx context.Context, id string) (*model.Album, error)
Tag(ctx context.Context, id string) (*model.Tag, error)
User(ctx context.Context, id string) (*model.User, error) User(ctx context.Context, id string) (*model.User, error)
Tag(ctx context.Context, id string) (*model.Tag, error)
Me(ctx context.Context) (*model.User, error)
MediaItems(ctx context.Context, filter *model.MediaItemFilter, count *int, page *int) (*model.MediaItemResponse, error) MediaItems(ctx context.Context, filter *model.MediaItemFilter, count *int, page *int) (*model.MediaItemResponse, error)
Devices(ctx context.Context, filter *model.DeviceFilter, count *int, page *int) (*model.DeviceResponse, error) Devices(ctx context.Context, filter *model.DeviceFilter, count *int, page *int) (*model.DeviceResponse, error)
Albums(ctx context.Context, filter *model.AlbumFilter, count *int, page *int) (*model.AlbumResponse, error) Albums(ctx context.Context, filter *model.AlbumFilter, count *int, page *int) (*model.AlbumResponse, error)
@ -229,6 +240,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.AlbumResponse.PageInfo(childComplexity), true return e.complexity.AlbumResponse.PageInfo(childComplexity), true
case "AuthResponse.Error":
if e.complexity.AuthResponse.Error == nil {
break
}
return e.complexity.AuthResponse.Error(childComplexity), true
case "AuthResponse.Result":
if e.complexity.AuthResponse.Result == nil {
break
}
return e.complexity.AuthResponse.Result(childComplexity), true
case "Device.createdAt": case "Device.createdAt":
if e.complexity.Device.CreatedAt == nil { if e.complexity.Device.CreatedAt == nil {
break break
@ -519,6 +544,32 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Query.Devices(childComplexity, args["filter"].(*model.DeviceFilter), args["count"].(*int), args["page"].(*int)), true return e.complexity.Query.Devices(childComplexity, args["filter"].(*model.DeviceFilter), args["count"].(*int), args["page"].(*int)), true
case "Query.login":
if e.complexity.Query.Login == nil {
break
}
args, err := ec.field_Query_login_args(context.TODO(), rawArgs)
if err != nil {
return 0, false
}
return e.complexity.Query.Login(childComplexity, args["user"].(string), args["password"].(string)), true
case "Query.logout":
if e.complexity.Query.Logout == nil {
break
}
return e.complexity.Query.Logout(childComplexity), true
case "Query.me":
if e.complexity.Query.Me == nil {
break
}
return e.complexity.Query.Me(childComplexity), true
case "Query.mediaItem": case "Query.mediaItem":
if e.complexity.Query.MediaItem == nil { if e.complexity.Query.MediaItem == nil {
break break
@ -787,7 +838,7 @@ scalar Time
scalar Upload scalar Upload
# https://gqlgen.com/reference/directives/ # https://gqlgen.com/reference/directives/
directive @hasRole(role: Role!) on FIELD_DEFINITION directive @hasMinRole(role: Role!) on FIELD_DEFINITION
directive @meta( directive @meta(
gorm: String, gorm: String,
@ -814,6 +865,20 @@ enum AuthType {
LDAP LDAP
} }
# ------------------------------------------------------------
# ---------------------- Authentication ----------------------
# ------------------------------------------------------------
enum AuthResult {
Success
Failure
}
type AuthResponse {
Result: AuthResult!
Error: String
}
# ------------------------------------------------------------ # ------------------------------------------------------------
# ----------------------- Type Filters ----------------------- # ----------------------- Type Filters -----------------------
# ------------------------------------------------------------ # ------------------------------------------------------------
@ -1095,47 +1160,56 @@ type AlbumResponse {
# ------------------------------------------------------------ # ------------------------------------------------------------
type Query { type Query {
# Authentication
login(
user: String!
password: String!
): AuthResult!
logout: AuthResult! @hasMinRole(role: User)
# Single Item # Single Item
mediaItem(id: ID!): MediaItem! @hasRole(role: User) mediaItem(id: ID!): MediaItem! @hasMinRole(role: User)
device(id: ID!): Device! @hasRole(role: User) device(id: ID!): Device! @hasMinRole(role: User)
album(id: ID!): Album! @hasRole(role: User) album(id: ID!): Album! @hasMinRole(role: User)
tag(id: ID!): Tag! @hasRole(role: User) user(id: ID!): User! @hasMinRole(role: Admin)
user(id: ID!): User! @hasRole(role: Admin) tag(id: ID!): Tag! @hasMinRole(role: User)
me: User! @hasMinRole(role: User)
# All # All
mediaItems( mediaItems(
filter: MediaItemFilter filter: MediaItemFilter
count: Int count: Int
page: Int page: Int
): MediaItemResponse! @hasRole(role: User) ): MediaItemResponse! @hasMinRole(role: User)
devices( devices(
filter: DeviceFilter filter: DeviceFilter
count: Int count: Int
page: Int page: Int
): DeviceResponse! @hasRole(role: User) ): DeviceResponse! @hasMinRole(role: User)
albums( albums(
filter: AlbumFilter filter: AlbumFilter
count: Int count: Int
page: Int page: Int
): AlbumResponse! @hasRole(role: User) ): AlbumResponse! @hasMinRole(role: User)
tags( tags(
filter: TagFilter filter: TagFilter
count: Int count: Int
page: Int page: Int
): TagResponse! @hasRole(role: User) ): TagResponse! @hasMinRole(role: User)
users( users(
filter: UserFilter filter: UserFilter
count: Int count: Int
page: Int page: Int
): UserResponse! @hasRole(role: Admin) ): UserResponse! @hasMinRole(role: Admin)
} }
type Mutation { type Mutation {
createMediaItem(input: NewMediaItem!): MediaItem! @hasRole(role: User) createMediaItem(input: NewMediaItem!): MediaItem! @hasMinRole(role: User)
createDevice(input: NewDevice!): Device! @hasRole(role: User) createDevice(input: NewDevice!): Device! @hasMinRole(role: User)
createAlbum(input: NewAlbum!): Album! @hasRole(role: User) createAlbum(input: NewAlbum!): Album! @hasMinRole(role: User)
createTag(input: NewTag!): Tag! @hasRole(role: User) createTag(input: NewTag!): Tag! @hasMinRole(role: User)
createUser(input: NewUser!): User! @hasRole(role: Admin) createUser(input: NewUser!): User! @hasMinRole(role: Admin)
} }
`, BuiltIn: false}, `, BuiltIn: false},
} }
@ -1145,7 +1219,7 @@ var parsedSchema = gqlparser.MustLoadSchema(sources...)
// region ***************************** args.gotpl ***************************** // region ***************************** args.gotpl *****************************
func (ec *executionContext) dir_hasRole_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { func (ec *executionContext) dir_hasMinRole_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error var err error
args := map[string]interface{}{} args := map[string]interface{}{}
var arg0 model.Role var arg0 model.Role
@ -1361,6 +1435,30 @@ func (ec *executionContext) field_Query_devices_args(ctx context.Context, rawArg
return args, nil return args, nil
} }
func (ec *executionContext) field_Query_login_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
var arg0 string
if tmp, ok := rawArgs["user"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("user"))
arg0, err = ec.unmarshalNString2string(ctx, tmp)
if err != nil {
return nil, err
}
}
args["user"] = arg0
var arg1 string
if tmp, ok := rawArgs["password"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("password"))
arg1, err = ec.unmarshalNString2string(ctx, tmp)
if err != nil {
return nil, err
}
}
args["password"] = arg1
return args, nil
}
func (ec *executionContext) field_Query_mediaItem_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { func (ec *executionContext) field_Query_mediaItem_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error var err error
args := map[string]interface{}{} args := map[string]interface{}{}
@ -1789,6 +1887,73 @@ func (ec *executionContext) _AlbumResponse_pageInfo(ctx context.Context, field g
return ec.marshalNPageInfo2ᚖreichardᚗioᚋimaginiᚋgraphᚋmodelᚐPageInfo(ctx, field.Selections, res) return ec.marshalNPageInfo2ᚖreichardᚗioᚋimaginiᚋgraphᚋmodelᚐPageInfo(ctx, field.Selections, res)
} }
func (ec *executionContext) _AuthResponse_Result(ctx context.Context, field graphql.CollectedField, obj *model.AuthResponse) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "AuthResponse",
Field: field,
Args: nil,
IsMethod: false,
IsResolver: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Result, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(model.AuthResult)
fc.Result = res
return ec.marshalNAuthResult2reichardᚗioᚋimaginiᚋgraphᚋmodelᚐAuthResult(ctx, field.Selections, res)
}
func (ec *executionContext) _AuthResponse_Error(ctx context.Context, field graphql.CollectedField, obj *model.AuthResponse) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "AuthResponse",
Field: field,
Args: nil,
IsMethod: false,
IsResolver: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Error, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*string)
fc.Result = res
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
}
func (ec *executionContext) _Device_id(ctx context.Context, field graphql.CollectedField, obj *model.Device) (ret graphql.Marshaler) { func (ec *executionContext) _Device_id(ctx context.Context, field graphql.CollectedField, obj *model.Device) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -2843,10 +3008,10 @@ func (ec *executionContext) _Mutation_createMediaItem(ctx context.Context, field
if err != nil { if err != nil {
return nil, err return nil, err
} }
if ec.directives.HasRole == nil { if ec.directives.HasMinRole == nil {
return nil, errors.New("directive hasRole is not implemented") return nil, errors.New("directive hasMinRole is not implemented")
} }
return ec.directives.HasRole(ctx, nil, directive0, role) return ec.directives.HasMinRole(ctx, nil, directive0, role)
} }
tmp, err := directive1(rctx) tmp, err := directive1(rctx)
@ -2909,10 +3074,10 @@ func (ec *executionContext) _Mutation_createDevice(ctx context.Context, field gr
if err != nil { if err != nil {
return nil, err return nil, err
} }
if ec.directives.HasRole == nil { if ec.directives.HasMinRole == nil {
return nil, errors.New("directive hasRole is not implemented") return nil, errors.New("directive hasMinRole is not implemented")
} }
return ec.directives.HasRole(ctx, nil, directive0, role) return ec.directives.HasMinRole(ctx, nil, directive0, role)
} }
tmp, err := directive1(rctx) tmp, err := directive1(rctx)
@ -2975,10 +3140,10 @@ func (ec *executionContext) _Mutation_createAlbum(ctx context.Context, field gra
if err != nil { if err != nil {
return nil, err return nil, err
} }
if ec.directives.HasRole == nil { if ec.directives.HasMinRole == nil {
return nil, errors.New("directive hasRole is not implemented") return nil, errors.New("directive hasMinRole is not implemented")
} }
return ec.directives.HasRole(ctx, nil, directive0, role) return ec.directives.HasMinRole(ctx, nil, directive0, role)
} }
tmp, err := directive1(rctx) tmp, err := directive1(rctx)
@ -3041,10 +3206,10 @@ func (ec *executionContext) _Mutation_createTag(ctx context.Context, field graph
if err != nil { if err != nil {
return nil, err return nil, err
} }
if ec.directives.HasRole == nil { if ec.directives.HasMinRole == nil {
return nil, errors.New("directive hasRole is not implemented") return nil, errors.New("directive hasMinRole is not implemented")
} }
return ec.directives.HasRole(ctx, nil, directive0, role) return ec.directives.HasMinRole(ctx, nil, directive0, role)
} }
tmp, err := directive1(rctx) tmp, err := directive1(rctx)
@ -3107,10 +3272,10 @@ func (ec *executionContext) _Mutation_createUser(ctx context.Context, field grap
if err != nil { if err != nil {
return nil, err return nil, err
} }
if ec.directives.HasRole == nil { if ec.directives.HasMinRole == nil {
return nil, errors.New("directive hasRole is not implemented") return nil, errors.New("directive hasMinRole is not implemented")
} }
return ec.directives.HasRole(ctx, nil, directive0, role) return ec.directives.HasMinRole(ctx, nil, directive0, role)
} }
tmp, err := directive1(rctx) tmp, err := directive1(rctx)
@ -3245,6 +3410,107 @@ func (ec *executionContext) _PageInfo_total(ctx context.Context, field graphql.C
return ec.marshalNInt2int(ctx, field.Selections, res) return ec.marshalNInt2int(ctx, field.Selections, res)
} }
func (ec *executionContext) _Query_login(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "Query",
Field: field,
Args: nil,
IsMethod: true,
IsResolver: true,
}
ctx = graphql.WithFieldContext(ctx, fc)
rawArgs := field.ArgumentMap(ec.Variables)
args, err := ec.field_Query_login_args(ctx, rawArgs)
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
fc.Args = args
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Query().Login(rctx, args["user"].(string), args["password"].(string))
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(model.AuthResult)
fc.Result = res
return ec.marshalNAuthResult2reichardᚗioᚋimaginiᚋgraphᚋmodelᚐAuthResult(ctx, field.Selections, res)
}
func (ec *executionContext) _Query_logout(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "Query",
Field: field,
Args: nil,
IsMethod: true,
IsResolver: true,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
directive0 := func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Query().Logout(rctx)
}
directive1 := func(ctx context.Context) (interface{}, error) {
role, err := ec.unmarshalNRole2reichardᚗioᚋimaginiᚋgraphᚋmodelᚐRole(ctx, "User")
if err != nil {
return nil, err
}
if ec.directives.HasMinRole == nil {
return nil, errors.New("directive hasMinRole is not implemented")
}
return ec.directives.HasMinRole(ctx, nil, directive0, role)
}
tmp, err := directive1(rctx)
if err != nil {
return nil, graphql.ErrorOnPath(ctx, err)
}
if tmp == nil {
return nil, nil
}
if data, ok := tmp.(model.AuthResult); ok {
return data, nil
}
return nil, fmt.Errorf(`unexpected type %T from directive, should be reichard.io/imagini/graph/model.AuthResult`, tmp)
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(model.AuthResult)
fc.Result = res
return ec.marshalNAuthResult2reichardᚗioᚋimaginiᚋgraphᚋmodelᚐAuthResult(ctx, field.Selections, res)
}
func (ec *executionContext) _Query_mediaItem(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { func (ec *executionContext) _Query_mediaItem(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -3278,10 +3544,10 @@ func (ec *executionContext) _Query_mediaItem(ctx context.Context, field graphql.
if err != nil { if err != nil {
return nil, err return nil, err
} }
if ec.directives.HasRole == nil { if ec.directives.HasMinRole == nil {
return nil, errors.New("directive hasRole is not implemented") return nil, errors.New("directive hasMinRole is not implemented")
} }
return ec.directives.HasRole(ctx, nil, directive0, role) return ec.directives.HasMinRole(ctx, nil, directive0, role)
} }
tmp, err := directive1(rctx) tmp, err := directive1(rctx)
@ -3344,10 +3610,10 @@ func (ec *executionContext) _Query_device(ctx context.Context, field graphql.Col
if err != nil { if err != nil {
return nil, err return nil, err
} }
if ec.directives.HasRole == nil { if ec.directives.HasMinRole == nil {
return nil, errors.New("directive hasRole is not implemented") return nil, errors.New("directive hasMinRole is not implemented")
} }
return ec.directives.HasRole(ctx, nil, directive0, role) return ec.directives.HasMinRole(ctx, nil, directive0, role)
} }
tmp, err := directive1(rctx) tmp, err := directive1(rctx)
@ -3410,10 +3676,10 @@ func (ec *executionContext) _Query_album(ctx context.Context, field graphql.Coll
if err != nil { if err != nil {
return nil, err return nil, err
} }
if ec.directives.HasRole == nil { if ec.directives.HasMinRole == nil {
return nil, errors.New("directive hasRole is not implemented") return nil, errors.New("directive hasMinRole is not implemented")
} }
return ec.directives.HasRole(ctx, nil, directive0, role) return ec.directives.HasMinRole(ctx, nil, directive0, role)
} }
tmp, err := directive1(rctx) tmp, err := directive1(rctx)
@ -3443,72 +3709,6 @@ func (ec *executionContext) _Query_album(ctx context.Context, field graphql.Coll
return ec.marshalNAlbum2ᚖreichardᚗioᚋimaginiᚋgraphᚋmodelᚐAlbum(ctx, field.Selections, res) return ec.marshalNAlbum2ᚖreichardᚗioᚋimaginiᚋgraphᚋmodelᚐAlbum(ctx, field.Selections, res)
} }
func (ec *executionContext) _Query_tag(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "Query",
Field: field,
Args: nil,
IsMethod: true,
IsResolver: true,
}
ctx = graphql.WithFieldContext(ctx, fc)
rawArgs := field.ArgumentMap(ec.Variables)
args, err := ec.field_Query_tag_args(ctx, rawArgs)
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
fc.Args = args
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
directive0 := func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Query().Tag(rctx, args["id"].(string))
}
directive1 := func(ctx context.Context) (interface{}, error) {
role, err := ec.unmarshalNRole2reichardᚗioᚋimaginiᚋgraphᚋmodelᚐRole(ctx, "User")
if err != nil {
return nil, err
}
if ec.directives.HasRole == nil {
return nil, errors.New("directive hasRole is not implemented")
}
return ec.directives.HasRole(ctx, nil, directive0, role)
}
tmp, err := directive1(rctx)
if err != nil {
return nil, graphql.ErrorOnPath(ctx, err)
}
if tmp == nil {
return nil, nil
}
if data, ok := tmp.(*model.Tag); ok {
return data, nil
}
return nil, fmt.Errorf(`unexpected type %T from directive, should be *reichard.io/imagini/graph/model.Tag`, tmp)
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(*model.Tag)
fc.Result = res
return ec.marshalNTag2ᚖreichardᚗioᚋimaginiᚋgraphᚋmodelᚐTag(ctx, field.Selections, res)
}
func (ec *executionContext) _Query_user(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { func (ec *executionContext) _Query_user(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -3542,10 +3742,135 @@ func (ec *executionContext) _Query_user(ctx context.Context, field graphql.Colle
if err != nil { if err != nil {
return nil, err return nil, err
} }
if ec.directives.HasRole == nil { if ec.directives.HasMinRole == nil {
return nil, errors.New("directive hasRole is not implemented") return nil, errors.New("directive hasMinRole is not implemented")
} }
return ec.directives.HasRole(ctx, nil, directive0, role) return ec.directives.HasMinRole(ctx, nil, directive0, role)
}
tmp, err := directive1(rctx)
if err != nil {
return nil, graphql.ErrorOnPath(ctx, err)
}
if tmp == nil {
return nil, nil
}
if data, ok := tmp.(*model.User); ok {
return data, nil
}
return nil, fmt.Errorf(`unexpected type %T from directive, should be *reichard.io/imagini/graph/model.User`, tmp)
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(*model.User)
fc.Result = res
return ec.marshalNUser2ᚖreichardᚗioᚋimaginiᚋgraphᚋmodelᚐUser(ctx, field.Selections, res)
}
func (ec *executionContext) _Query_tag(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "Query",
Field: field,
Args: nil,
IsMethod: true,
IsResolver: true,
}
ctx = graphql.WithFieldContext(ctx, fc)
rawArgs := field.ArgumentMap(ec.Variables)
args, err := ec.field_Query_tag_args(ctx, rawArgs)
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
fc.Args = args
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
directive0 := func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Query().Tag(rctx, args["id"].(string))
}
directive1 := func(ctx context.Context) (interface{}, error) {
role, err := ec.unmarshalNRole2reichardᚗioᚋimaginiᚋgraphᚋmodelᚐRole(ctx, "User")
if err != nil {
return nil, err
}
if ec.directives.HasMinRole == nil {
return nil, errors.New("directive hasMinRole is not implemented")
}
return ec.directives.HasMinRole(ctx, nil, directive0, role)
}
tmp, err := directive1(rctx)
if err != nil {
return nil, graphql.ErrorOnPath(ctx, err)
}
if tmp == nil {
return nil, nil
}
if data, ok := tmp.(*model.Tag); ok {
return data, nil
}
return nil, fmt.Errorf(`unexpected type %T from directive, should be *reichard.io/imagini/graph/model.Tag`, tmp)
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(*model.Tag)
fc.Result = res
return ec.marshalNTag2ᚖreichardᚗioᚋimaginiᚋgraphᚋmodelᚐTag(ctx, field.Selections, res)
}
func (ec *executionContext) _Query_me(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "Query",
Field: field,
Args: nil,
IsMethod: true,
IsResolver: true,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
directive0 := func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Query().Me(rctx)
}
directive1 := func(ctx context.Context) (interface{}, error) {
role, err := ec.unmarshalNRole2reichardᚗioᚋimaginiᚋgraphᚋmodelᚐRole(ctx, "User")
if err != nil {
return nil, err
}
if ec.directives.HasMinRole == nil {
return nil, errors.New("directive hasMinRole is not implemented")
}
return ec.directives.HasMinRole(ctx, nil, directive0, role)
} }
tmp, err := directive1(rctx) tmp, err := directive1(rctx)
@ -3608,10 +3933,10 @@ func (ec *executionContext) _Query_mediaItems(ctx context.Context, field graphql
if err != nil { if err != nil {
return nil, err return nil, err
} }
if ec.directives.HasRole == nil { if ec.directives.HasMinRole == nil {
return nil, errors.New("directive hasRole is not implemented") return nil, errors.New("directive hasMinRole is not implemented")
} }
return ec.directives.HasRole(ctx, nil, directive0, role) return ec.directives.HasMinRole(ctx, nil, directive0, role)
} }
tmp, err := directive1(rctx) tmp, err := directive1(rctx)
@ -3674,10 +3999,10 @@ func (ec *executionContext) _Query_devices(ctx context.Context, field graphql.Co
if err != nil { if err != nil {
return nil, err return nil, err
} }
if ec.directives.HasRole == nil { if ec.directives.HasMinRole == nil {
return nil, errors.New("directive hasRole is not implemented") return nil, errors.New("directive hasMinRole is not implemented")
} }
return ec.directives.HasRole(ctx, nil, directive0, role) return ec.directives.HasMinRole(ctx, nil, directive0, role)
} }
tmp, err := directive1(rctx) tmp, err := directive1(rctx)
@ -3740,10 +4065,10 @@ func (ec *executionContext) _Query_albums(ctx context.Context, field graphql.Col
if err != nil { if err != nil {
return nil, err return nil, err
} }
if ec.directives.HasRole == nil { if ec.directives.HasMinRole == nil {
return nil, errors.New("directive hasRole is not implemented") return nil, errors.New("directive hasMinRole is not implemented")
} }
return ec.directives.HasRole(ctx, nil, directive0, role) return ec.directives.HasMinRole(ctx, nil, directive0, role)
} }
tmp, err := directive1(rctx) tmp, err := directive1(rctx)
@ -3806,10 +4131,10 @@ func (ec *executionContext) _Query_tags(ctx context.Context, field graphql.Colle
if err != nil { if err != nil {
return nil, err return nil, err
} }
if ec.directives.HasRole == nil { if ec.directives.HasMinRole == nil {
return nil, errors.New("directive hasRole is not implemented") return nil, errors.New("directive hasMinRole is not implemented")
} }
return ec.directives.HasRole(ctx, nil, directive0, role) return ec.directives.HasMinRole(ctx, nil, directive0, role)
} }
tmp, err := directive1(rctx) tmp, err := directive1(rctx)
@ -3872,10 +4197,10 @@ func (ec *executionContext) _Query_users(ctx context.Context, field graphql.Coll
if err != nil { if err != nil {
return nil, err return nil, err
} }
if ec.directives.HasRole == nil { if ec.directives.HasMinRole == nil {
return nil, errors.New("directive hasRole is not implemented") return nil, errors.New("directive hasMinRole is not implemented")
} }
return ec.directives.HasRole(ctx, nil, directive0, role) return ec.directives.HasMinRole(ctx, nil, directive0, role)
} }
tmp, err := directive1(rctx) tmp, err := directive1(rctx)
@ -7006,6 +7331,35 @@ func (ec *executionContext) _AlbumResponse(ctx context.Context, sel ast.Selectio
return out return out
} }
var authResponseImplementors = []string{"AuthResponse"}
func (ec *executionContext) _AuthResponse(ctx context.Context, sel ast.SelectionSet, obj *model.AuthResponse) graphql.Marshaler {
fields := graphql.CollectFields(ec.OperationContext, sel, authResponseImplementors)
out := graphql.NewFieldSet(fields)
var invalids uint32
for i, field := range fields {
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("AuthResponse")
case "Result":
out.Values[i] = ec._AuthResponse_Result(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "Error":
out.Values[i] = ec._AuthResponse_Error(ctx, field, obj)
default:
panic("unknown field " + strconv.Quote(field.Name))
}
}
out.Dispatch()
if invalids > 0 {
return graphql.Null
}
return out
}
var deviceImplementors = []string{"Device"} var deviceImplementors = []string{"Device"}
func (ec *executionContext) _Device(ctx context.Context, sel ast.SelectionSet, obj *model.Device) graphql.Marshaler { func (ec *executionContext) _Device(ctx context.Context, sel ast.SelectionSet, obj *model.Device) graphql.Marshaler {
@ -7264,6 +7618,34 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
switch field.Name { switch field.Name {
case "__typename": case "__typename":
out.Values[i] = graphql.MarshalString("Query") out.Values[i] = graphql.MarshalString("Query")
case "login":
field := field
out.Concurrently(i, func() (res graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._Query_login(ctx, field)
if res == graphql.Null {
atomic.AddUint32(&invalids, 1)
}
return res
})
case "logout":
field := field
out.Concurrently(i, func() (res graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._Query_logout(ctx, field)
if res == graphql.Null {
atomic.AddUint32(&invalids, 1)
}
return res
})
case "mediaItem": case "mediaItem":
field := field field := field
out.Concurrently(i, func() (res graphql.Marshaler) { out.Concurrently(i, func() (res graphql.Marshaler) {
@ -7306,6 +7688,20 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
} }
return res return res
}) })
case "user":
field := field
out.Concurrently(i, func() (res graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._Query_user(ctx, field)
if res == graphql.Null {
atomic.AddUint32(&invalids, 1)
}
return res
})
case "tag": case "tag":
field := field field := field
out.Concurrently(i, func() (res graphql.Marshaler) { out.Concurrently(i, func() (res graphql.Marshaler) {
@ -7320,7 +7716,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
} }
return res return res
}) })
case "user": case "me":
field := field field := field
out.Concurrently(i, func() (res graphql.Marshaler) { out.Concurrently(i, func() (res graphql.Marshaler) {
defer func() { defer func() {
@ -7328,7 +7724,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
ec.Error(ctx, ec.Recover(ctx, r)) ec.Error(ctx, ec.Recover(ctx, r))
} }
}() }()
res = ec._Query_user(ctx, field) res = ec._Query_me(ctx, field)
if res == graphql.Null { if res == graphql.Null {
atomic.AddUint32(&invalids, 1) atomic.AddUint32(&invalids, 1)
} }
@ -7837,6 +8233,16 @@ func (ec *executionContext) marshalNAlbumResponse2ᚖreichardᚗioᚋimaginiᚋg
return ec._AlbumResponse(ctx, sel, v) return ec._AlbumResponse(ctx, sel, v)
} }
func (ec *executionContext) unmarshalNAuthResult2reichardᚗioᚋimaginiᚋgraphᚋmodelᚐAuthResult(ctx context.Context, v interface{}) (model.AuthResult, error) {
var res model.AuthResult
err := res.UnmarshalGQL(v)
return res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) marshalNAuthResult2reichardᚗioᚋimaginiᚋgraphᚋmodelᚐAuthResult(ctx context.Context, sel ast.SelectionSet, v model.AuthResult) graphql.Marshaler {
return v
}
func (ec *executionContext) unmarshalNAuthType2reichardᚗioᚋimaginiᚋgraphᚋmodelᚐAuthType(ctx context.Context, v interface{}) (model.AuthType, error) { func (ec *executionContext) unmarshalNAuthType2reichardᚗioᚋimaginiᚋgraphᚋmodelᚐAuthType(ctx context.Context, v interface{}) (model.AuthType, error) {
var res model.AuthType var res model.AuthType
err := res.UnmarshalGQL(v) err := res.UnmarshalGQL(v)

View File

@ -0,0 +1,12 @@
package model
import (
"net/http"
)
type AuthContext struct {
AccessToken string
RefreshToken string
AuthResponse *http.ResponseWriter
AuthRequest *http.Request
}

36
graph/model/models_db.go Normal file
View File

@ -0,0 +1,36 @@
package model
import (
"gorm.io/gorm"
"github.com/google/uuid"
)
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
newID := uuid.New().String()
u.ID = &newID
return
}
func (a *Album) BeforeCreate(tx *gorm.DB) (err error) {
newID := uuid.New().String()
a.ID = &newID
return
}
func (m *MediaItem) BeforeCreate(tx *gorm.DB) (err error) {
newID := uuid.New().String()
m.ID = &newID
return
}
func (t *Tag) BeforeCreate(tx *gorm.DB) (err error) {
newID := uuid.New().String()
t.ID = &newID
return
}
func (d *Device) BeforeCreate(tx *gorm.DB) (err error) {
newID := uuid.New().String()
d.ID = &newID
return
}

View File

@ -32,6 +32,11 @@ type AlbumResponse struct {
PageInfo *PageInfo `json:"pageInfo" ` PageInfo *PageInfo `json:"pageInfo" `
} }
type AuthResponse struct {
Result AuthResult `json:"Result" `
Error *string `json:"Error" `
}
type AuthTypeFilter struct { type AuthTypeFilter struct {
EqualTo *AuthType `json:"equalTo" ` EqualTo *AuthType `json:"equalTo" `
NotEqualTo *AuthType `json:"notEqualTo" ` NotEqualTo *AuthType `json:"notEqualTo" `
@ -261,6 +266,47 @@ type UserResponse struct {
PageInfo *PageInfo `json:"pageInfo" ` PageInfo *PageInfo `json:"pageInfo" `
} }
type AuthResult string
const (
AuthResultSuccess AuthResult = "Success"
AuthResultFailure AuthResult = "Failure"
)
var AllAuthResult = []AuthResult{
AuthResultSuccess,
AuthResultFailure,
}
func (e AuthResult) IsValid() bool {
switch e {
case AuthResultSuccess, AuthResultFailure:
return true
}
return false
}
func (e AuthResult) String() string {
return string(e)
}
func (e *AuthResult) UnmarshalGQL(v interface{}) error {
str, ok := v.(string)
if !ok {
return fmt.Errorf("enums must be strings")
}
*e = AuthResult(str)
if !e.IsValid() {
return fmt.Errorf("%s is not a valid AuthResult", str)
}
return nil
}
func (e AuthResult) MarshalGQL(w io.Writer) {
fmt.Fprint(w, strconv.Quote(e.String()))
}
type AuthType string type AuthType string
const ( const (

View File

@ -4,7 +4,7 @@ scalar Time
scalar Upload scalar Upload
# https://gqlgen.com/reference/directives/ # https://gqlgen.com/reference/directives/
directive @hasRole(role: Role!) on FIELD_DEFINITION directive @hasMinRole(role: Role!) on FIELD_DEFINITION
directive @meta( directive @meta(
gorm: String, gorm: String,
@ -31,6 +31,20 @@ enum AuthType {
LDAP LDAP
} }
# ------------------------------------------------------------
# ---------------------- Authentication ----------------------
# ------------------------------------------------------------
enum AuthResult {
Success
Failure
}
type AuthResponse {
Result: AuthResult!
Error: String
}
# ------------------------------------------------------------ # ------------------------------------------------------------
# ----------------------- Type Filters ----------------------- # ----------------------- Type Filters -----------------------
# ------------------------------------------------------------ # ------------------------------------------------------------
@ -312,45 +326,54 @@ type AlbumResponse {
# ------------------------------------------------------------ # ------------------------------------------------------------
type Query { type Query {
# Authentication
login(
user: String!
password: String!
): AuthResult!
logout: AuthResult! @hasMinRole(role: User)
# Single Item # Single Item
mediaItem(id: ID!): MediaItem! @hasRole(role: User) mediaItem(id: ID!): MediaItem! @hasMinRole(role: User)
device(id: ID!): Device! @hasRole(role: User) device(id: ID!): Device! @hasMinRole(role: User)
album(id: ID!): Album! @hasRole(role: User) album(id: ID!): Album! @hasMinRole(role: User)
tag(id: ID!): Tag! @hasRole(role: User) user(id: ID!): User! @hasMinRole(role: Admin)
user(id: ID!): User! @hasRole(role: Admin) tag(id: ID!): Tag! @hasMinRole(role: User)
me: User! @hasMinRole(role: User)
# All # All
mediaItems( mediaItems(
filter: MediaItemFilter filter: MediaItemFilter
count: Int count: Int
page: Int page: Int
): MediaItemResponse! @hasRole(role: User) ): MediaItemResponse! @hasMinRole(role: User)
devices( devices(
filter: DeviceFilter filter: DeviceFilter
count: Int count: Int
page: Int page: Int
): DeviceResponse! @hasRole(role: User) ): DeviceResponse! @hasMinRole(role: User)
albums( albums(
filter: AlbumFilter filter: AlbumFilter
count: Int count: Int
page: Int page: Int
): AlbumResponse! @hasRole(role: User) ): AlbumResponse! @hasMinRole(role: User)
tags( tags(
filter: TagFilter filter: TagFilter
count: Int count: Int
page: Int page: Int
): TagResponse! @hasRole(role: User) ): TagResponse! @hasMinRole(role: User)
users( users(
filter: UserFilter filter: UserFilter
count: Int count: Int
page: Int page: Int
): UserResponse! @hasRole(role: Admin) ): UserResponse! @hasMinRole(role: Admin)
} }
type Mutation { type Mutation {
createMediaItem(input: NewMediaItem!): MediaItem! @hasRole(role: User) createMediaItem(input: NewMediaItem!): MediaItem! @hasMinRole(role: User)
createDevice(input: NewDevice!): Device! @hasRole(role: User) createDevice(input: NewDevice!): Device! @hasMinRole(role: User)
createAlbum(input: NewAlbum!): Album! @hasRole(role: User) createAlbum(input: NewAlbum!): Album! @hasMinRole(role: User)
createTag(input: NewTag!): Tag! @hasRole(role: User) createTag(input: NewTag!): Tag! @hasMinRole(role: User)
createUser(input: NewUser!): User! @hasRole(role: Admin) createUser(input: NewUser!): User! @hasMinRole(role: Admin)
} }

View File

@ -4,6 +4,7 @@ package graph
// will be copied through when generating and any unknown code will be moved to the end. // will be copied through when generating and any unknown code will be moved to the end.
import ( import (
"net/http"
"context" "context"
"fmt" "fmt"
@ -28,7 +29,38 @@ func (r *mutationResolver) CreateTag(ctx context.Context, input model.NewTag) (*
} }
func (r *mutationResolver) CreateUser(ctx context.Context, input model.NewUser) (*model.User, error) { func (r *mutationResolver) CreateUser(ctx context.Context, input model.NewUser) (*model.User, error) {
panic(fmt.Errorf("not implemented")) user := &model.User{
Email: input.Email,
Username: input.Username,
FirstName: input.FirstName,
LastName: input.LastName,
Role: input.Role,
AuthType: input.AuthType,
Password: input.Password,
}
err := r.DB.CreateUser(user)
if err != nil {
panic(fmt.Errorf("DB Error"))
}
return user, nil
}
func (r *queryResolver) Login(ctx context.Context, user string, password string) (model.AuthResult, error) {
// Set Cookie From Context
authContext := ctx.Value("auth").(*model.AuthContext)
resp := *authContext.AuthResponse
testCookie := http.Cookie{Name: "TestCookie", Value: "Test123", Path: "/", HttpOnly: true}
http.SetCookie(resp, &testCookie)
return model.AuthResultSuccess, nil
}
func (r *queryResolver) Logout(ctx context.Context) (model.AuthResult, error) {
// panic(fmt.Errorf("not implemented"))
return model.AuthResultSuccess, nil
} }
func (r *queryResolver) MediaItem(ctx context.Context, id string) (*model.MediaItem, error) { func (r *queryResolver) MediaItem(ctx context.Context, id string) (*model.MediaItem, error) {
@ -43,11 +75,15 @@ func (r *queryResolver) Album(ctx context.Context, id string) (*model.Album, err
panic(fmt.Errorf("not implemented")) panic(fmt.Errorf("not implemented"))
} }
func (r *queryResolver) User(ctx context.Context, id string) (*model.User, error) {
panic(fmt.Errorf("not implemented"))
}
func (r *queryResolver) Tag(ctx context.Context, id string) (*model.Tag, error) { func (r *queryResolver) Tag(ctx context.Context, id string) (*model.Tag, error) {
panic(fmt.Errorf("not implemented")) panic(fmt.Errorf("not implemented"))
} }
func (r *queryResolver) User(ctx context.Context, id string) (*model.User, error) { func (r *queryResolver) Me(ctx context.Context) (*model.User, error) {
panic(fmt.Errorf("not implemented")) panic(fmt.Errorf("not implemented"))
} }
@ -68,7 +104,18 @@ func (r *queryResolver) Tags(ctx context.Context, filter *model.TagFilter, count
} }
func (r *queryResolver) Users(ctx context.Context, filter *model.UserFilter, count *int, page *int) (*model.UserResponse, error) { func (r *queryResolver) Users(ctx context.Context, filter *model.UserFilter, count *int, page *int) (*model.UserResponse, error) {
panic(fmt.Errorf("not implemented")) resp, totalCount, err := r.DB.Users()
if err != nil {
panic(fmt.Errorf("DB Error"))
}
return &model.UserResponse{
Data: resp,
PageInfo: &model.PageInfo{
Count: int(totalCount),
Page: 0,
Total: int(totalCount),
},
}, nil
} }
// Mutation returns generated.MutationResolver implementation. // Mutation returns generated.MutationResolver implementation.

View File

@ -4,14 +4,16 @@ import (
"fmt" "fmt"
"time" "time"
"strings" "strings"
"context"
"net/http" "net/http"
"encoding/json" "encoding/json"
"github.com/google/uuid" "github.com/google/uuid"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/lestrrat-go/jwx/jwt" "github.com/lestrrat-go/jwx/jwt"
"github.com/99designs/gqlgen/graphql"
"reichard.io/imagini/internal/models" "reichard.io/imagini/graph/model"
graphql "reichard.io/imagini/graph/model"
) )
func (api *API) loginHandler(w http.ResponseWriter, r *http.Request) { func (api *API) loginHandler(w http.ResponseWriter, r *http.Request) {
@ -22,7 +24,7 @@ func (api *API) loginHandler(w http.ResponseWriter, r *http.Request) {
} }
// Decode into Struct // Decode into Struct
var creds models.APICredentials var creds APICredentials
err := json.NewDecoder(r.Body).Decode(&creds) err := json.NewDecoder(r.Body).Decode(&creds)
if err != nil { if err != nil {
errorJSON(w, "Invalid parameters.", http.StatusBadRequest) errorJSON(w, "Invalid parameters.", http.StatusBadRequest)
@ -36,7 +38,7 @@ func (api *API) loginHandler(w http.ResponseWriter, r *http.Request) {
} }
// Do login // Do login
resp, user := api.Auth.AuthenticateUser(creds) resp, user := api.Auth.AuthenticateUser(creds.User, creds.Password)
if !resp { if !resp {
errorJSON(w, "Invalid credentials.", http.StatusUnauthorized) errorJSON(w, "Invalid credentials.", http.StatusUnauthorized)
return return
@ -82,7 +84,7 @@ func (api *API) logoutHandler(w http.ResponseWriter, r *http.Request) {
/** /**
* This will find or create the requested device based on ID and User. * This will find or create the requested device based on ID and User.
**/ **/
func (api *API) upsertRequestedDevice(user graphql.User, r *http.Request) (graphql.Device, error) { func (api *API) upsertRequestedDevice(user model.User, r *http.Request) (model.Device, error) {
requestedDevice := deriveRequestedDevice(r) requestedDevice := deriveRequestedDevice(r)
requestedDevice.Type = deriveDeviceType(r) requestedDevice.Type = deriveDeviceType(r)
requestedDevice.User.ID = user.ID requestedDevice.User.ID = user.ID
@ -93,7 +95,7 @@ func (api *API) upsertRequestedDevice(user graphql.User, r *http.Request) (graph
return createdDevice, err return createdDevice, err
} }
foundDevice, err := api.DB.Device(&graphql.Device{ foundDevice, err := api.DB.Device(&model.Device{
ID: requestedDevice.ID, ID: requestedDevice.ID,
User: &user, User: &user,
}) })
@ -101,28 +103,28 @@ func (api *API) upsertRequestedDevice(user graphql.User, r *http.Request) (graph
return foundDevice, err return foundDevice, err
} }
func deriveDeviceType(r *http.Request) graphql.DeviceType { func deriveDeviceType(r *http.Request) model.DeviceType {
userAgent := strings.ToLower(r.Header.Get("User-Agent")) userAgent := strings.ToLower(r.Header.Get("User-Agent"))
if strings.HasPrefix(userAgent, "ios-imagini"){ if strings.HasPrefix(userAgent, "ios-imagini"){
return graphql.DeviceTypeIOs return model.DeviceTypeIOs
} else if strings.HasPrefix(userAgent, "android-imagini"){ } else if strings.HasPrefix(userAgent, "android-imagini"){
return graphql.DeviceTypeAndroid return model.DeviceTypeAndroid
} else if strings.HasPrefix(userAgent, "chrome"){ } else if strings.HasPrefix(userAgent, "chrome"){
return graphql.DeviceTypeChrome return model.DeviceTypeChrome
} else if strings.HasPrefix(userAgent, "firefox"){ } else if strings.HasPrefix(userAgent, "firefox"){
return graphql.DeviceTypeFirefox return model.DeviceTypeFirefox
} else if strings.HasPrefix(userAgent, "msie"){ } else if strings.HasPrefix(userAgent, "msie"){
return graphql.DeviceTypeInternetExplorer return model.DeviceTypeInternetExplorer
} else if strings.HasPrefix(userAgent, "edge"){ } else if strings.HasPrefix(userAgent, "edge"){
return graphql.DeviceTypeEdge return model.DeviceTypeEdge
} else if strings.HasPrefix(userAgent, "safari"){ } else if strings.HasPrefix(userAgent, "safari"){
return graphql.DeviceTypeSafari return model.DeviceTypeSafari
} }
return graphql.DeviceTypeUnknown return model.DeviceTypeUnknown
} }
func deriveRequestedDevice(r *http.Request) graphql.Device { func deriveRequestedDevice(r *http.Request) model.Device {
deviceSkeleton := graphql.Device{} deviceSkeleton := model.Device{}
authHeader := r.Header.Get("X-Imagini-Authorization") authHeader := r.Header.Get("X-Imagini-Authorization")
splitAuthInfo := strings.Split(authHeader, ",") splitAuthInfo := strings.Split(authHeader, ",")
@ -202,8 +204,8 @@ func (api *API) refreshAccessToken(w http.ResponseWriter, r *http.Request) (jwt.
stringDeviceUUID := deviceUUID.String() stringDeviceUUID := deviceUUID.String()
// Device & User Skeleton // Device & User Skeleton
user := graphql.User{ID: &stringUserUUID} user := model.User{ID: &stringUserUUID}
device := graphql.Device{ID: &stringDeviceUUID} device := model.Device{ID: &stringDeviceUUID}
// Update token // Update token
accessTokenString, err := api.Auth.CreateJWTAccessToken(user, device) accessTokenString, err := api.Auth.CreateJWTAccessToken(user, device)
@ -230,3 +232,17 @@ func trimQuotes(s string) string {
} }
return s return s
} }
func hasMinRoleDirective(ctx context.Context, obj interface{}, next graphql.Resolver, role model.Role) (res interface{}, err error) {
// if !getCurrentUser(ctx).HasRole(role) {
// // block calling the next resolver
// return nil, fmt.Errorf("Access denied")
// }
// or let it pass through
return next(ctx)
}
func metaDirective(ctx context.Context, obj interface{}, next graphql.Resolver, gorm *string) (res interface{}, err error){
return next(ctx)
}

View File

@ -1,10 +1,12 @@
package api package api
import ( import (
"os"
"context"
"net/http"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"net/http"
"context"
"os"
"reichard.io/imagini/graph/model"
) )
type Middleware func(http.Handler) http.HandlerFunc type Middleware func(http.Handler) http.HandlerFunc
@ -20,6 +22,35 @@ func multipleMiddleware(h http.HandlerFunc, m ...Middleware) http.HandlerFunc {
return wrapped return wrapped
} }
func (api *API) injectContextMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Info("[middleware] Entering testMiddleware...")
authContext := &model.AuthContext{
AuthResponse: &w,
AuthRequest: r,
}
accessCookie, err := r.Cookie("AccessToken")
if err != nil {
log.Warn("[middleware] AccessToken not found")
} else {
authContext.AccessToken = accessCookie.Value
}
refreshCookie, err := r.Cookie("RefreshToken")
if err != nil {
log.Warn("[middleware] RefreshToken not found")
} else {
authContext.RefreshToken = refreshCookie.Value
}
// Add context
ctx := context.WithValue(r.Context(), "auth", authContext)
r = r.WithContext(ctx)
log.Info("[middleware] Exiting testMiddleware...")
next.ServeHTTP(w, r)
})
}
func (api *API) authMiddleware(next http.Handler) http.HandlerFunc { func (api *API) authMiddleware(next http.Handler) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

View File

@ -1,4 +1,4 @@
package models package api
type APICredentials struct { type APICredentials struct {
User string `json:"user"` User string `json:"user"`

View File

@ -1,33 +1,38 @@
package api package api
import ( import (
"encoding/json"
"net/http" "net/http"
"encoding/json"
"reichard.io/imagini/internal/models"
"github.com/99designs/gqlgen/graphql/handler" "github.com/99designs/gqlgen/graphql/handler"
"github.com/99designs/gqlgen/graphql/playground" "github.com/99designs/gqlgen/graphql/playground"
"reichard.io/imagini/graph" "reichard.io/imagini/graph"
"reichard.io/imagini/graph/generated" "reichard.io/imagini/graph/generated"
) )
func (api *API) registerRoutes() { func (api *API) registerRoutes() {
srv := handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: &graph.Resolver{}}))
// TODO: Provide Authentication for srv // Set up Directives
c := generated.Config{ Resolvers: &graph.Resolver{ DB: api.DB } }
c.Directives.HasMinRole = hasMinRoleDirective
c.Directives.Meta = metaDirective
srv := handler.NewDefaultServer(generated.NewExecutableSchema(c))
// Handle GraphQL
api.Router.Handle("/playground", playground.Handler("GraphQL playground", "/query")) api.Router.Handle("/playground", playground.Handler("GraphQL playground", "/query"))
api.Router.Handle("/query", srv) api.Router.Handle("/query", api.injectContextMiddleware(srv))
// Handle Resource Route
api.Router.HandleFunc("/media/", multipleMiddleware( api.Router.HandleFunc("/media/", multipleMiddleware(
api.mediaHandler, api.mediaHandler,
api.authMiddleware, api.authMiddleware,
)) ))
api.Router.HandleFunc("/logout", api.logoutHandler)
api.Router.HandleFunc("/login", api.loginHandler)
} }
func errorJSON(w http.ResponseWriter, err string, code int) { func errorJSON(w http.ResponseWriter, err string, code int) {
errStruct := &models.APIResponse{Error: &models.APIError{Message: err, Code: int64(code)}} errStruct := &APIResponse{Error: &APIError{Message: err, Code: int64(code)}}
responseJSON(w, errStruct, code) responseJSON(w, errStruct, code)
} }

View File

@ -12,11 +12,9 @@ import (
"github.com/lestrrat-go/jwx/jwa" "github.com/lestrrat-go/jwx/jwa"
"github.com/lestrrat-go/jwx/jwt" "github.com/lestrrat-go/jwx/jwt"
"reichard.io/imagini/graph/model"
"reichard.io/imagini/internal/db" "reichard.io/imagini/internal/db"
"reichard.io/imagini/internal/config" "reichard.io/imagini/internal/config"
graphql "reichard.io/imagini/graph/model"
"reichard.io/imagini/internal/models"
"reichard.io/imagini/internal/session" "reichard.io/imagini/internal/session"
) )
@ -35,21 +33,21 @@ func NewMgr(db *db.DBManager, c *config.Config) *AuthManager {
} }
} }
func (auth *AuthManager) AuthenticateUser(creds models.APICredentials) (bool, graphql.User) { func (auth *AuthManager) AuthenticateUser(user, password string) (bool, model.User) {
// Search Objects // Search Objects
userByName := &graphql.User{} userByName := &model.User{}
userByName.Username = creds.User userByName.Username = user
foundUser, err := auth.DB.User(userByName) foundUser, err := auth.DB.User(userByName)
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
userByEmail := &graphql.User{} userByEmail := &model.User{}
userByEmail.Email = creds.User userByEmail.Email = user
foundUser, err = auth.DB.User(userByEmail) foundUser, err = auth.DB.User(userByEmail)
} }
// Error Checking // Error Checking
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
log.Warn("[auth] User not found: ", creds.User) log.Warn("[auth] User not found: ", user)
return false, foundUser return false, foundUser
} else if err != nil { } else if err != nil {
log.Error(err) log.Error(err)
@ -61,15 +59,15 @@ func (auth *AuthManager) AuthenticateUser(creds models.APICredentials) (bool, gr
// Determine Type // Determine Type
switch foundUser.AuthType { switch foundUser.AuthType {
case "Local": case "Local":
return authenticateLocalUser(foundUser, creds.Password), foundUser return authenticateLocalUser(foundUser, password), foundUser
case "LDAP": case "LDAP":
return authenticateLDAPUser(foundUser, creds.Password), foundUser return authenticateLDAPUser(foundUser, password), foundUser
default: default:
return false, foundUser return false, foundUser
} }
} }
func (auth *AuthManager) getRole(user graphql.User) string { func (auth *AuthManager) getRole(user model.User) string {
// TODO: Lookup role of user // TODO: Lookup role of user
return "User" return "User"
} }
@ -88,7 +86,7 @@ func (auth *AuthManager) ValidateJWTRefreshToken(refreshJWT string) (jwt.Token,
return nil, errors.New("did does not parse") return nil, errors.New("did does not parse")
} }
stringDeviceID := deviceID.String() stringDeviceID := deviceID.String()
device, err := auth.DB.Device(&graphql.Device{ID: &stringDeviceID}) device, err := auth.DB.Device(&model.Device{ID: &stringDeviceID})
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -119,7 +117,7 @@ func (auth *AuthManager) ValidateJWTAccessToken(accessJWT string) (jwt.Token, er
return verifiedToken, nil return verifiedToken, nil
} }
func (auth *AuthManager) CreateJWTRefreshToken(user graphql.User, device graphql.Device) (string, error) { func (auth *AuthManager) CreateJWTRefreshToken(user model.User, device model.Device) (string, error) {
// Acquire Refresh Key // Acquire Refresh Key
byteKey := []byte(*device.RefreshKey) byteKey := []byte(*device.RefreshKey)
@ -154,7 +152,7 @@ func (auth *AuthManager) CreateJWTRefreshToken(user graphql.User, device graphql
return string(signed), nil return string(signed), nil
} }
func (auth *AuthManager) CreateJWTAccessToken(user graphql.User, device graphql.Device) (string, error) { func (auth *AuthManager) CreateJWTAccessToken(user model.User, device model.Device) (string, error) {
// Create New Token // Create New Token
tm := time.Now() tm := time.Now()
t := jwt.New() t := jwt.New()

View File

@ -1,14 +1,14 @@
package db package db
import ( import (
"fmt"
"path"
"errors" "errors"
"path"
"fmt"
"gorm.io/gorm" log "github.com/sirupsen/logrus"
// "gorm.io/gorm/logger" // "gorm.io/gorm/logger"
"gorm.io/driver/sqlite" "gorm.io/driver/sqlite"
log "github.com/sirupsen/logrus" "gorm.io/gorm"
"reichard.io/imagini/internal/config" "reichard.io/imagini/internal/config"
"reichard.io/imagini/graph/model" "reichard.io/imagini/graph/model"
@ -59,6 +59,7 @@ func (dbm *DBManager) bootstrapDatabase() {
Username: "admin", Username: "admin",
AuthType: "Local", AuthType: "Local",
Password: &password, Password: &password,
Role: model.RoleAdmin,
} }
err := dbm.CreateUser(user) err := dbm.CreateUser(user)
@ -68,8 +69,6 @@ func (dbm *DBManager) bootstrapDatabase() {
} }
} }
// func (dmb *DBManager) {}
func (dbm *DBManager) QueryBuilder(dest interface{}, params []byte) (int64, error) { func (dbm *DBManager) QueryBuilder(dest interface{}, params []byte) (int64, error) {
// TODO: // TODO:
// - Where Filters // - Where Filters

View File

@ -7,7 +7,7 @@ import (
"reichard.io/imagini/graph/model" "reichard.io/imagini/graph/model"
) )
func (dbm *DBManager) CreateUser(user *model.User) error { func (dbm *DBManager) CreateUser (user *model.User) error {
log.Info("[db] Creating user: ", user.Username) log.Info("[db] Creating user: ", user.Username)
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(*user.Password), bcrypt.DefaultCost) hashedPassword, err := bcrypt.GenerateFromPassword([]byte(*user.Password), bcrypt.DefaultCost)
if err != nil { if err != nil {
@ -27,6 +27,13 @@ func (dbm *DBManager) User (user *model.User) (model.User, error) {
return foundUser, err return foundUser, err
} }
func (dbm *DBManager) Users () ([]*model.User, int64, error) {
var foundUsers []*model.User
var count int64
err := dbm.db.Find(&foundUsers).Count(&count).Error
return foundUsers, count, err
}
func (dbm *DBManager) DeleteUser (user model.User) error { func (dbm *DBManager) DeleteUser (user model.User) error {
return nil return nil
} }