add hasPrivilege & asUser directive, fix nil dereference in UpdateUser, add & rephrase error messages

This commit is contained in:
gilex-dev 2024-02-02 21:23:32 +01:00
parent 7d2010eba7
commit a1a907e81b
10 changed files with 695 additions and 94 deletions

View File

@ -185,6 +185,6 @@ func initDB() {
globals.Logger.Println("Connecting to SQLite3", db_path)
globals.DB = database.InitSQLite3(db_path, globals.DB_schema, globals.Logger, []byte(viper.GetString("database.secret")), viper.GetString("database.initialAdmin.userName"), viper.GetString("database.initialAdmin.password"))
globals.DB.CleanExpiredRefreshTokensTicker(time.Minute * 10)
globals.DB.CleanRevokedAccessTokensTicker(time.Minute * 10)
globals.DB.CleanExpiredRefreshTokensTicker(time.Minute * 10) //TODO: add to viper
globals.DB.CleanRevokedAccessTokensTicker(time.Minute * 10) //TODO: add to viper
}

View File

@ -94,13 +94,13 @@ func (db CustomDB) GenerateHashFromPassword(password string) (passwordHash []byt
}
// GetRefreshTokenOwner takes a tokenId and return the owner's userId. Call before Update/Get/DeleteRefreshToken when not IS_admin.
func (db CustomDB) GetRefreshTokenOwner(tokenId string) (string, error) {
func (db CustomDB) GetRefreshTokenOwner(tokenId string) (ownerId string, err error) {
numTokenId, err := strconv.Atoi(tokenId)
if err != nil {
return "", errors.New("invalid tokenId")
return "", errors.New("malformed refresh token Id")
}
statement, err := db.connection.Prepare("SELECT FK_User_userId, FROM RefreshToken WHERE tokenId = ?")
statement, err := db.connection.Prepare("SELECT FK_User_userId FROM RefreshToken WHERE tokenId = ?")
if err != nil {
return "", err
}
@ -108,6 +108,9 @@ func (db CustomDB) GetRefreshTokenOwner(tokenId string) (string, error) {
result := statement.QueryRow(numTokenId)
var owner string
if err := result.Scan(&owner); err != nil {
if err == sql.ErrNoRows {
return "", errors.New("invalid refresh token Id")
}
return "", err
}
@ -118,7 +121,7 @@ func (db CustomDB) ValidateUserCredentials(userId *string, userName *string, pas
var result *sql.Row
var hash string
if userId != nil { // user userId
if userId != nil { // use userId
numUserId, err := strconv.Atoi(*userId)
if err != nil {
return "", errors.New("userId not numeric")
@ -131,9 +134,12 @@ func (db CustomDB) ValidateUserCredentials(userId *string, userName *string, pas
result = statement.QueryRow(numUserId)
if err := result.Scan(&hash); err != nil {
if err == sql.ErrNoRows {
return "", errors.New("invalid user Id")
}
return "", err
}
} else if userName != nil { // user userName
} else if userName != nil { // use userName
statement, err := db.connection.Prepare("SELECT userId, passwordHash FROM User WHERE userName = ?")
if err != nil {
return "", err
@ -141,6 +147,9 @@ func (db CustomDB) ValidateUserCredentials(userId *string, userName *string, pas
result = statement.QueryRow(&userName)
if err := result.Scan(&userId, &hash); err != nil {
if err == sql.ErrNoRows {
return "", errors.New("invalid user Id")
}
return "", err
}
} else {
@ -184,6 +193,9 @@ func (db CustomDB) IssueRefreshToken(userId string, tokenName *string) (refreshT
result := statement.QueryRow(numUserId, encSelector, base64.RawURLEncoding.EncodeToString(tokenHash[:]), &tokenName)
if err := result.Scan(&tokenId, &expiryDate); err != nil {
if err == sql.ErrNoRows {
return nil, "", errors.New("failed to add new refresh token")
}
return nil, "", err
}
@ -193,7 +205,7 @@ func (db CustomDB) IssueRefreshToken(userId string, tokenName *string) (refreshT
func (db CustomDB) GetRefreshToken(token *model.RefreshToken) (*model.RefreshToken, error) {
numTokenId, err := strconv.Atoi(token.ID)
if err != nil {
return nil, errors.New("invalid tokenId")
return nil, errors.New("malformed refresh token Id")
}
statement, err := db.connection.Prepare("SELECT expiryDate, tokenName FROM RefreshToken WHERE tokenId = ?")
@ -202,7 +214,10 @@ func (db CustomDB) GetRefreshToken(token *model.RefreshToken) (*model.RefreshTok
}
result := statement.QueryRow(numTokenId)
if err := result.Scan(&token.ID, &token.ExpiryDate, &token.TokenName); err != nil {
if err := result.Scan(&token.ExpiryDate, &token.TokenName); err != nil {
if err == sql.ErrNoRows {
return nil, errors.New("invalid refresh token Id")
}
return nil, err
}
@ -212,7 +227,7 @@ func (db CustomDB) GetRefreshToken(token *model.RefreshToken) (*model.RefreshTok
func (db CustomDB) GetRefreshTokensFrom(userId string) ([]*model.RefreshToken, error) {
numUserId, err := strconv.Atoi(userId)
if err != nil {
return nil, errors.New("invalid userId")
return nil, errors.New("malformed userId")
}
statement, err := db.connection.Prepare("SELECT tokenId, expiaryDate, tokenName FROM RefreshToken WHERE FK_User_userId = ?")
if err != nil {
@ -264,10 +279,10 @@ func (db CustomDB) GetAllRefreshTokens() ([]*model.RefreshToken, error) {
func (db CustomDB) UpdateRefreshToken(tokenId string, changes *model.UpdateRefreshToken) (*model.RefreshToken, error) {
numTokenId, err := strconv.Atoi(tokenId)
if err != nil {
return nil, errors.New("invalid tokenId")
return nil, errors.New("malformed refresh token Id")
}
statement, err := db.connection.Prepare("UPDATE AuthToken SET tokenName = ? WHERE tokenId = ?")
statement, err := db.connection.Prepare("UPDATE RefreshToken SET tokenName = ? WHERE tokenId = ?")
if err != nil {
return nil, err
}
@ -300,7 +315,7 @@ func (db CustomDB) RevokeRefreshToken(tokenId string) (*string, error) {
// TODO: return string instead of *string
numTokenId, err := strconv.Atoi(tokenId)
if err != nil {
return nil, errors.New("invalid tokenId")
return nil, errors.New("malformed refresh token Id")
}
statement, err := db.connection.Prepare("DELETE FROM RefreshToken WHERE tokenId = ? RETURNING FK_User_userId")
@ -312,6 +327,9 @@ func (db CustomDB) RevokeRefreshToken(tokenId string) (*string, error) {
var userId string
if err := result.Scan(&userId); err != nil {
if err == sql.ErrNoRows {
return nil, errors.New("invalid refresh token Id")
}
return nil, err
}
@ -321,7 +339,7 @@ func (db CustomDB) RevokeRefreshToken(tokenId string) (*string, error) {
// IssueAccessToken issues an access token if the passed refresh token is valid. Returned access token must be passed to SignAccessToken to be accepted.
func (db CustomDB) IssueAccessToken(refreshToken *RefreshToken) (*AccessToken, error) {
statement, err := db.connection.Prepare("SELECT RefreshToken.tokenHash, RefreshToken.FK_User_userId, Role.IS_admin, ROLE.IS_userCreator FROM RefreshToken INNER JOIN R_User_Role ON RefreshToken.FK_User_userId = R_User_Role.FK_User_userId INNER JOIN Role ON R_User_Role.FK_Role_roleId = Role.roleId WHERE RefreshToken.selector = ? AND RefreshToken.expiryDate >= unixepoch('now')")
statement, err := db.connection.Prepare("SELECT tokenHash, FK_User_userId FROM RefreshToken WHERE selector = ? AND expiryDate >= unixepoch('now')")
if err != nil {
return nil, err
}
@ -330,7 +348,15 @@ func (db CustomDB) IssueAccessToken(refreshToken *RefreshToken) (*AccessToken, e
var tokenHash string
var newAccessToken AccessToken
if err := result.Scan(&tokenHash, &newAccessToken.UserId, &newAccessToken.IsAdmin, &newAccessToken.IsUserCreator); err != nil {
if err := result.Scan(&tokenHash, &newAccessToken.UserId); err != nil {
if err == sql.ErrNoRows {
return nil, errors.New("invalid refresh token selector")
}
return nil, err
}
newAccessToken.IsAdmin, newAccessToken.IsUserCreator, err = db.GetUserPermissions(newAccessToken.UserId)
if err != nil {
return nil, err
}

View File

@ -17,6 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package database
import (
"database/sql"
"errors"
"strconv"
@ -62,6 +63,9 @@ func (db CustomDB) GetRole(role *model.Role) (*model.Role, error) {
result := statement.QueryRow(id)
if err := result.Scan(&role.RoleName, &role.IsAdmin, &role.IsUserCreator); err != nil {
if err == sql.ErrNoRows {
return nil, errors.New("invalid role Id")
}
return nil, err
}
@ -71,7 +75,7 @@ func (db CustomDB) GetRole(role *model.Role) (*model.Role, error) {
func (db CustomDB) GetRolesFrom(userId string) ([]*model.RelationUserRole, error) {
numUserId, err := strconv.Atoi(userId)
if err != nil {
return nil, errors.New("invalid userId")
return nil, errors.New("malformed userId")
}
statement, err := db.connection.Prepare("SELECT Role.roleId, Role.roleName, Role.IS_admin, Role.IS_userCreator, R_User_Role.IS_roleManager FROM Role INNER JOIN R_User_Role ON R_User_Role.FK_Role_roleId = Role.roleId WHERE R_User_Role.FK_User_userId = ?")
if err != nil {
@ -152,7 +156,7 @@ func (db CustomDB) UpdateRole(roleId string, changes *model.UpdateRole) (*model.
id, err := strconv.Atoi(roleId)
if err != nil {
return nil, errors.New("invalid userId")
return nil, errors.New("malformed userId")
}
statement, err := db.connection.Prepare("UPDATE Role SET roleName = IFNULL(?, roleName), IS_admin = IFNULL(?, IS_admin), IS_userCreator = IFNULL(?, IS_userCreator) WHERE roleId = ?")

View File

@ -17,6 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package database
import (
"database/sql"
"errors"
"strconv"
@ -27,7 +28,7 @@ import (
func (db CustomDB) GetOwner(todoId string) (string, error) {
numTodoId, err := strconv.Atoi(todoId)
if err != nil {
return "", errors.New("invalid todoId")
return "", errors.New("malformed todoId")
}
statement, err := db.connection.Prepare("SELECT FK_User_userId, FROM Todo WHERE todoId = ?")
@ -38,6 +39,9 @@ func (db CustomDB) GetOwner(todoId string) (string, error) {
result := statement.QueryRow(numTodoId)
var owner string
if err := result.Scan(&owner); err != nil {
if err == sql.ErrNoRows {
return "", errors.New("invalid todo Id")
}
return "", err
}
@ -48,7 +52,7 @@ func (db CustomDB) GetOwner(todoId string) (string, error) {
func (db CustomDB) GetTodo(todo *model.Todo) (*model.Todo, error) {
numTodoId, err := strconv.Atoi(todo.ID)
if err != nil {
return nil, errors.New("invalid todoId")
return nil, errors.New("malformed todoId malformatted todoId")
}
statement, err := db.connection.Prepare("SELECT text, IS_done, FK_User_userId FROM Todo WHERE todoId = ?")
@ -59,6 +63,9 @@ func (db CustomDB) GetTodo(todo *model.Todo) (*model.Todo, error) {
todo.User = &model.User{} // TODO: check if this overrides something
result := statement.QueryRow(numTodoId)
if err := result.Scan(&todo.Text, &todo.Done, &todo.User.ID); err != nil {
if err == sql.ErrNoRows {
return nil, errors.New("invalid todo Id")
}
return nil, err
}
@ -69,7 +76,7 @@ func (db CustomDB) GetTodo(todo *model.Todo) (*model.Todo, error) {
func (db CustomDB) GetTodosFrom(user *model.User) ([]*model.Todo, error) {
id, err := strconv.Atoi(user.ID)
if err != nil {
return nil, errors.New("invalid userId")
return nil, errors.New("malformed userId")
}
statement, err := db.connection.Prepare("SELECT todoId, text, IS_done FROM Todo WHERE FK_User_userId = ?")
if err != nil {
@ -146,7 +153,7 @@ func (db CustomDB) UpdateTodo(todoId string, changes *model.UpdateTodo) (*model.
numTodoId, err := strconv.Atoi(todoId)
if err != nil {
return nil, errors.New("invalid todoId")
return nil, errors.New("malformed todoId")
}
statement, err := db.connection.Prepare("UPDATE Todo SET text = IFNULL(?, text), IS_done = IFNULL(?, IS_done) WHERE todoId = ?")
@ -174,7 +181,7 @@ func (db CustomDB) UpdateTodo(todoId string, changes *model.UpdateTodo) (*model.
func (db CustomDB) DeleteTodo(todoId string) (deletedTodoId *string, err error) {
numTodoId, err := strconv.Atoi(todoId)
if err != nil {
return nil, errors.New("invalid todoId")
return nil, errors.New("malformed todoId")
}
statement, err := db.connection.Prepare("DELETE FROM Todo WHERE todoId = ?")
@ -198,3 +205,27 @@ func (db CustomDB) DeleteTodo(todoId string) (deletedTodoId *string, err error)
return &todoId, nil
}
// GetTodoOwner takes a *model.Todo with at least ID set and returns an *model.User with Id set to the todo's owner Id.
func (db CustomDB) GetTodoOwner(todo *model.Todo) (owner *model.User, err error) {
numTodoId, err := strconv.Atoi(todo.ID)
if err != nil {
return nil, errors.New("malformed todoId")
}
statement, err := db.connection.Prepare("SELECT FK_User_userId FROM Todo WHERE todoId = ?")
if err != nil {
return nil, err
}
user := &model.User{} // TODO: check if this overrides something
result := statement.QueryRow(numTodoId)
if err := result.Scan(&user.ID); err != nil {
if err == sql.ErrNoRows {
return nil, errors.New("invalid todo Id")
}
return nil, err
}
return user, nil
}

View File

@ -17,6 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package database
import (
"database/sql"
"errors"
"strconv"
"time"
@ -68,7 +69,7 @@ func (db CustomDB) CreateUser(newUser model.NewUser) (*model.User, error) {
func (db CustomDB) GetUser(user *model.User) (*model.User, error) {
numUserId, err := strconv.Atoi(user.ID)
if err != nil {
return nil, errors.New("invalid userId")
return nil, errors.New("malformed userId")
}
statement, err := db.connection.Prepare("SELECT userID, userName, fullName FROM User WHERE userId = ? OR userName = ?")
if err != nil {
@ -77,6 +78,9 @@ func (db CustomDB) GetUser(user *model.User) (*model.User, error) {
result := statement.QueryRow(numUserId, user.UserName)
if err := result.Scan(&user.ID, &user.UserName, &user.FullName); err != nil {
if err == sql.ErrNoRows {
return nil, errors.New("invalid user Id")
}
return nil, err
}
@ -108,24 +112,22 @@ func (db CustomDB) UpdateUser(userId string, changes *model.UpdateUser) (*model.
id, err := strconv.Atoi(userId)
if err != nil {
return nil, errors.New("invalid userId")
return nil, errors.New("malformed userId")
}
statement, err := db.connection.Prepare("UPDATE User SET userName = IFNULL(?, userName), fullName = IFNULL(NULLIF(?, ''), fullName), passwordHash = IFNULL(?, passwordHash) WHERE userId = ?")
statement, err := db.connection.Prepare("UPDATE User SET userName = IFNULL(?, userName), fullName = NULLIF(?, ''), passwordHash = IFNULL(?, passwordHash) WHERE userId = ?")
if err != nil {
return nil, err
}
if *changes.UserName == "" { // interpret empty string as nil
changes.UserName = nil
} else {
if changes.UserName != nil && *changes.UserName != "" { // ignore empty string
if err := ValidateUserName(*changes.UserName); err != nil {
return nil, err
}
needAccessTokenRefresh = true
}
if *changes.Password == "" { // interpret empty string as nil
if changes.Password == nil { // interpret empty string as nil
passwordHash = nil
} else {
if err := ValidatePassword(*changes.Password); err != nil {
@ -188,7 +190,7 @@ func (db CustomDB) DeleteUser(userId string) (*string, error) {
func (db CustomDB) AddUserRole(userId string, roleId string, isRoleManager bool) (relationId string, err error) {
encUserId, err := strconv.Atoi(userId)
if err != nil {
return "", errors.New("invalid userId")
return "", errors.New("malformed userId")
}
encRoleId, err := strconv.Atoi(roleId)
if err != nil {
@ -226,7 +228,7 @@ func (db CustomDB) AddUserRole(userId string, roleId string, isRoleManager bool)
func (db CustomDB) UpdateUserRole(userId string, roleId string, isRoleManager bool) (relationId string, err error) {
encUserId, err := strconv.Atoi(userId)
if err != nil {
return "", errors.New("invalid userId")
return "", errors.New("malformed userId")
}
encRoleId, err := strconv.Atoi(roleId)
if err != nil {
@ -264,7 +266,7 @@ func (db CustomDB) UpdateUserRole(userId string, roleId string, isRoleManager bo
func (db CustomDB) RemoveUserRole(userId string, roleId string) (relationId string, err error) {
encUserId, err := strconv.Atoi(userId)
if err != nil {
return "", errors.New("invalid userId")
return "", errors.New("malformed userId")
}
encRoleId, err := strconv.Atoi(roleId)
if err != nil {
@ -293,3 +295,36 @@ func (db CustomDB) RemoveUserRole(userId string, roleId string) (relationId stri
RevokeAccessToken(&AccessToken{UserId: userId, ExpiryDate: int(time.Now().Add(accessTokenLifetime).Unix())})
return strconv.FormatInt(int64(encRoleId), 10), nil
}
func (db CustomDB) GetUserPermissions(userId string) (isAdmin bool, isUserCreator bool, err error) {
numUserId, err := strconv.Atoi(userId)
if err != nil {
return false, false, errors.New("malformed userId")
}
statement, err := db.connection.Prepare("SELECT Role.IS_admin, Role.IS_userCreator FROM R_User_Role INNER JOIN Role ON R_User_Role.FK_Role_roleId = Role.roleId WHERE R_User_Role.FK_User_userId = ?")
if err != nil {
return false, false, err
}
rows, err := statement.Query(numUserId)
if err != nil {
return false, false, err
}
defer rows.Close()
var admin, userCreator bool
for rows.Next() {
var gotAdmin, gotUserCreator bool
if err := rows.Scan(&gotAdmin, &gotUserCreator); err != nil {
return false, false, err
}
if gotAdmin {
admin = true
}
if gotUserCreator {
userCreator = true
}
}
return admin, userCreator, nil
}

View File

@ -47,6 +47,8 @@ type ResolverRoot interface {
}
type DirectiveRoot struct {
AsUser func(ctx context.Context, obj interface{}, next graphql.Resolver, id string) (res interface{}, err error)
HasPrivilege func(ctx context.Context, obj interface{}, next graphql.Resolver, privilege model.Privilege) (res interface{}, err error)
}
type ComplexityRoot struct {
@ -347,12 +349,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Mutation.UpdateUser(childComplexity, args["id"].(string), args["changes"].(model.UpdateUser)), true
case "Mutation.UpdateUserRole":
case "Mutation.updateUserRole":
if e.complexity.Mutation.UpdateUserRole == nil {
break
}
args, err := ec.field_Mutation_UpdateUserRole_args(context.TODO(), rawArgs)
args, err := ec.field_Mutation_updateUserRole_args(context.TODO(), rawArgs)
if err != nil {
return 0, false
}
@ -630,7 +632,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler {
if first {
first = false
ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap)
data = ec._Query(ctx, rc.Operation.SelectionSet)
data = ec._queryMiddleware(ctx, rc.Operation, func(ctx context.Context) (interface{}, error) {
return ec._Query(ctx, rc.Operation.SelectionSet), nil
})
} else {
if atomic.LoadInt32(&ec.pendingDeferred) > 0 {
result := <-ec.deferredResults
@ -660,7 +664,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler {
}
first = false
ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap)
data := ec._Mutation(ctx, rc.Operation.SelectionSet)
data := ec._mutationMiddleware(ctx, rc.Operation, func(ctx context.Context) (interface{}, error) {
return ec._Mutation(ctx, rc.Operation.SelectionSet), nil
})
var buf bytes.Buffer
data.MarshalGQL(&buf)
@ -735,36 +741,33 @@ var parsedSchema = gqlparser.MustLoadSchema(sources...)
// region ***************************** args.gotpl *****************************
func (ec *executionContext) field_Mutation_UpdateUserRole_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
func (ec *executionContext) dir_asUser_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["userId"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("userId"))
if tmp, ok := rawArgs["id"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("id"))
arg0, err = ec.unmarshalNID2string(ctx, tmp)
if err != nil {
return nil, err
}
}
args["userId"] = arg0
var arg1 string
if tmp, ok := rawArgs["roleId"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("roleId"))
arg1, err = ec.unmarshalNID2string(ctx, tmp)
args["id"] = arg0
return args, nil
}
func (ec *executionContext) dir_hasPrivilege_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
var arg0 model.Privilege
if tmp, ok := rawArgs["privilege"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("privilege"))
arg0, err = ec.unmarshalNPrivilege2somepiᚗddnsᚗnetᚋgiteaᚋgilexᚑdevᚋYetAnotherToDoListᚋgraphᚋmodelᚐPrivilege(ctx, tmp)
if err != nil {
return nil, err
}
}
args["roleId"] = arg1
var arg2 bool
if tmp, ok := rawArgs["userIsRoleManager"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("userIsRoleManager"))
arg2, err = ec.unmarshalNBoolean2bool(ctx, tmp)
if err != nil {
return nil, err
}
}
args["userIsRoleManager"] = arg2
args["privilege"] = arg0
return args, nil
}
@ -1017,6 +1020,39 @@ func (ec *executionContext) field_Mutation_updateTodo_args(ctx context.Context,
return args, nil
}
func (ec *executionContext) field_Mutation_updateUserRole_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["userId"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("userId"))
arg0, err = ec.unmarshalNID2string(ctx, tmp)
if err != nil {
return nil, err
}
}
args["userId"] = arg0
var arg1 string
if tmp, ok := rawArgs["roleId"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("roleId"))
arg1, err = ec.unmarshalNID2string(ctx, tmp)
if err != nil {
return nil, err
}
}
args["roleId"] = arg1
var arg2 bool
if tmp, ok := rawArgs["userIsRoleManager"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("userIsRoleManager"))
arg2, err = ec.unmarshalNBoolean2bool(ctx, tmp)
if err != nil {
return nil, err
}
}
args["userIsRoleManager"] = arg2
return args, nil
}
func (ec *executionContext) field_Mutation_updateUser_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
@ -1150,6 +1186,72 @@ func (ec *executionContext) field___Type_fields_args(ctx context.Context, rawArg
// region ************************** directives.gotpl **************************
func (ec *executionContext) _queryMiddleware(ctx context.Context, obj *ast.OperationDefinition, next func(ctx context.Context) (interface{}, error)) graphql.Marshaler {
for _, d := range obj.Directives {
switch d.Name {
case "asUser":
rawArgs := d.ArgumentMap(ec.Variables)
args, err := ec.dir_asUser_args(ctx, rawArgs)
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
n := next
next = func(ctx context.Context) (interface{}, error) {
if ec.directives.AsUser == nil {
return nil, errors.New("directive asUser is not implemented")
}
return ec.directives.AsUser(ctx, obj, n, args["id"].(string))
}
}
}
tmp, err := next(ctx)
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if data, ok := tmp.(graphql.Marshaler); ok {
return data
}
ec.Errorf(ctx, `unexpected type %T from directive, should be graphql.Marshaler`, tmp)
return graphql.Null
}
func (ec *executionContext) _mutationMiddleware(ctx context.Context, obj *ast.OperationDefinition, next func(ctx context.Context) (interface{}, error)) graphql.Marshaler {
for _, d := range obj.Directives {
switch d.Name {
case "asUser":
rawArgs := d.ArgumentMap(ec.Variables)
args, err := ec.dir_asUser_args(ctx, rawArgs)
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
n := next
next = func(ctx context.Context) (interface{}, error) {
if ec.directives.AsUser == nil {
return nil, errors.New("directive asUser is not implemented")
}
return ec.directives.AsUser(ctx, obj, n, args["id"].(string))
}
}
}
tmp, err := next(ctx)
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if data, ok := tmp.(graphql.Marshaler); ok {
return data
}
ec.Errorf(ctx, `unexpected type %T from directive, should be graphql.Marshaler`, tmp)
return graphql.Null
}
// endregion ************************** directives.gotpl **************************
// region **************************** field.gotpl *****************************
@ -1167,8 +1269,32 @@ func (ec *executionContext) _Mutation_createUser(ctx context.Context, field grap
}
}()
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.Mutation().CreateUser(rctx, fc.Args["input"].(model.NewUser))
}
directive1 := func(ctx context.Context) (interface{}, error) {
privilege, err := ec.unmarshalNPrivilege2somepiᚗddnsᚗnetᚋgiteaᚋgilexᚑdevᚋYetAnotherToDoListᚋgraphᚋmodelᚐPrivilege(ctx, "isUserCreator")
if err != nil {
return nil, err
}
if ec.directives.HasPrivilege == nil {
return nil, errors.New("directive hasPrivilege is not implemented")
}
return ec.directives.HasPrivilege(ctx, nil, directive0, privilege)
}
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 *somepi.ddns.net/gitea/gilex-dev/YetAnotherToDoList/graph/model.User`, tmp)
})
if err != nil {
ec.Error(ctx, err)
@ -1299,8 +1425,32 @@ func (ec *executionContext) _Mutation_createRole(ctx context.Context, field grap
}
}()
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.Mutation().CreateRole(rctx, fc.Args["input"].(model.NewRole))
}
directive1 := func(ctx context.Context) (interface{}, error) {
privilege, err := ec.unmarshalNPrivilege2somepiᚗddnsᚗnetᚋgiteaᚋgilexᚑdevᚋYetAnotherToDoListᚋgraphᚋmodelᚐPrivilege(ctx, "isAdmin")
if err != nil {
return nil, err
}
if ec.directives.HasPrivilege == nil {
return nil, errors.New("directive hasPrivilege is not implemented")
}
return ec.directives.HasPrivilege(ctx, nil, directive0, privilege)
}
tmp, err := directive1(rctx)
if err != nil {
return nil, graphql.ErrorOnPath(ctx, err)
}
if tmp == nil {
return nil, nil
}
if data, ok := tmp.(*model.Role); ok {
return data, nil
}
return nil, fmt.Errorf(`unexpected type %T from directive, should be *somepi.ddns.net/gitea/gilex-dev/YetAnotherToDoList/graph/model.Role`, tmp)
})
if err != nil {
ec.Error(ctx, err)
@ -1567,8 +1717,32 @@ func (ec *executionContext) _Mutation_updateRole(ctx context.Context, field grap
}
}()
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.Mutation().UpdateRole(rctx, fc.Args["id"].(string), fc.Args["changes"].(model.UpdateRole))
}
directive1 := func(ctx context.Context) (interface{}, error) {
privilege, err := ec.unmarshalNPrivilege2somepiᚗddnsᚗnetᚋgiteaᚋgilexᚑdevᚋYetAnotherToDoListᚋgraphᚋmodelᚐPrivilege(ctx, "isAdmin")
if err != nil {
return nil, err
}
if ec.directives.HasPrivilege == nil {
return nil, errors.New("directive hasPrivilege is not implemented")
}
return ec.directives.HasPrivilege(ctx, nil, directive0, privilege)
}
tmp, err := directive1(rctx)
if err != nil {
return nil, graphql.ErrorOnPath(ctx, err)
}
if tmp == nil {
return nil, nil
}
if data, ok := tmp.(*model.Role); ok {
return data, nil
}
return nil, fmt.Errorf(`unexpected type %T from directive, should be *somepi.ddns.net/gitea/gilex-dev/YetAnotherToDoList/graph/model.Role`, tmp)
})
if err != nil {
ec.Error(ctx, err)
@ -1703,8 +1877,32 @@ func (ec *executionContext) _Mutation_deleteUser(ctx context.Context, field grap
}
}()
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.Mutation().DeleteUser(rctx, fc.Args["id"].(string))
}
directive1 := func(ctx context.Context) (interface{}, error) {
privilege, err := ec.unmarshalNPrivilege2somepiᚗddnsᚗnetᚋgiteaᚋgilexᚑdevᚋYetAnotherToDoListᚋgraphᚋmodelᚐPrivilege(ctx, "isAdmin")
if err != nil {
return nil, err
}
if ec.directives.HasPrivilege == nil {
return nil, errors.New("directive hasPrivilege is not implemented")
}
return ec.directives.HasPrivilege(ctx, nil, directive0, privilege)
}
tmp, err := directive1(rctx)
if err != nil {
return nil, graphql.ErrorOnPath(ctx, err)
}
if tmp == nil {
return nil, nil
}
if data, ok := tmp.(*string); ok {
return data, nil
}
return nil, fmt.Errorf(`unexpected type %T from directive, should be *string`, tmp)
})
if err != nil {
ec.Error(ctx, err)
@ -1807,8 +2005,32 @@ func (ec *executionContext) _Mutation_deleteRole(ctx context.Context, field grap
}
}()
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.Mutation().DeleteRole(rctx, fc.Args["id"].(string))
}
directive1 := func(ctx context.Context) (interface{}, error) {
privilege, err := ec.unmarshalNPrivilege2somepiᚗddnsᚗnetᚋgiteaᚋgilexᚑdevᚋYetAnotherToDoListᚋgraphᚋmodelᚐPrivilege(ctx, "isAdmin")
if err != nil {
return nil, err
}
if ec.directives.HasPrivilege == nil {
return nil, errors.New("directive hasPrivilege is not implemented")
}
return ec.directives.HasPrivilege(ctx, nil, directive0, privilege)
}
tmp, err := directive1(rctx)
if err != nil {
return nil, graphql.ErrorOnPath(ctx, err)
}
if tmp == nil {
return nil, nil
}
if data, ok := tmp.(*string); ok {
return data, nil
}
return nil, fmt.Errorf(`unexpected type %T from directive, should be *string`, tmp)
})
if err != nil {
ec.Error(ctx, err)
@ -1911,8 +2133,32 @@ func (ec *executionContext) _Mutation_addUserRole(ctx context.Context, field gra
}
}()
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.Mutation().AddUserRole(rctx, fc.Args["userId"].(string), fc.Args["roleId"].(string), fc.Args["userIsRoleManager"].(bool))
}
directive1 := func(ctx context.Context) (interface{}, error) {
privilege, err := ec.unmarshalNPrivilege2somepiᚗddnsᚗnetᚋgiteaᚋgilexᚑdevᚋYetAnotherToDoListᚋgraphᚋmodelᚐPrivilege(ctx, "isAdmin")
if err != nil {
return nil, err
}
if ec.directives.HasPrivilege == nil {
return nil, errors.New("directive hasPrivilege is not implemented")
}
return ec.directives.HasPrivilege(ctx, nil, directive0, privilege)
}
tmp, err := directive1(rctx)
if err != nil {
return nil, graphql.ErrorOnPath(ctx, err)
}
if tmp == nil {
return nil, nil
}
if data, ok := tmp.([]*model.RelationUserRole); ok {
return data, nil
}
return nil, fmt.Errorf(`unexpected type %T from directive, should be []*somepi.ddns.net/gitea/gilex-dev/YetAnotherToDoList/graph/model.RelationUserRole`, tmp)
})
if err != nil {
ec.Error(ctx, err)
@ -1959,8 +2205,8 @@ func (ec *executionContext) fieldContext_Mutation_addUserRole(ctx context.Contex
return fc, nil
}
func (ec *executionContext) _Mutation_UpdateUserRole(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Mutation_UpdateUserRole(ctx, field)
func (ec *executionContext) _Mutation_updateUserRole(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Mutation_updateUserRole(ctx, field)
if err != nil {
return graphql.Null
}
@ -1972,8 +2218,32 @@ func (ec *executionContext) _Mutation_UpdateUserRole(ctx context.Context, field
}
}()
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.Mutation().UpdateUserRole(rctx, fc.Args["userId"].(string), fc.Args["roleId"].(string), fc.Args["userIsRoleManager"].(bool))
}
directive1 := func(ctx context.Context) (interface{}, error) {
privilege, err := ec.unmarshalNPrivilege2somepiᚗddnsᚗnetᚋgiteaᚋgilexᚑdevᚋYetAnotherToDoListᚋgraphᚋmodelᚐPrivilege(ctx, "isAdmin")
if err != nil {
return nil, err
}
if ec.directives.HasPrivilege == nil {
return nil, errors.New("directive hasPrivilege is not implemented")
}
return ec.directives.HasPrivilege(ctx, nil, directive0, privilege)
}
tmp, err := directive1(rctx)
if err != nil {
return nil, graphql.ErrorOnPath(ctx, err)
}
if tmp == nil {
return nil, nil
}
if data, ok := tmp.([]*model.RelationUserRole); ok {
return data, nil
}
return nil, fmt.Errorf(`unexpected type %T from directive, should be []*somepi.ddns.net/gitea/gilex-dev/YetAnotherToDoList/graph/model.RelationUserRole`, tmp)
})
if err != nil {
ec.Error(ctx, err)
@ -1990,7 +2260,7 @@ func (ec *executionContext) _Mutation_UpdateUserRole(ctx context.Context, field
return ec.marshalNRelationUserRole2ᚕᚖsomepiᚗddnsᚗnetᚋgiteaᚋgilexᚑdevᚋYetAnotherToDoListᚋgraphᚋmodelᚐRelationUserRoleᚄ(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_Mutation_UpdateUserRole(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
func (ec *executionContext) fieldContext_Mutation_updateUserRole(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "Mutation",
Field: field,
@ -2013,7 +2283,7 @@ func (ec *executionContext) fieldContext_Mutation_UpdateUserRole(ctx context.Con
}
}()
ctx = graphql.WithFieldContext(ctx, fc)
if fc.Args, err = ec.field_Mutation_UpdateUserRole_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {
if fc.Args, err = ec.field_Mutation_updateUserRole_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {
ec.Error(ctx, err)
return fc, err
}
@ -2033,8 +2303,32 @@ func (ec *executionContext) _Mutation_removeUserRole(ctx context.Context, field
}
}()
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.Mutation().RemoveUserRole(rctx, fc.Args["userId"].(string), fc.Args["roleId"].(string))
}
directive1 := func(ctx context.Context) (interface{}, error) {
privilege, err := ec.unmarshalNPrivilege2somepiᚗddnsᚗnetᚋgiteaᚋgilexᚑdevᚋYetAnotherToDoListᚋgraphᚋmodelᚐPrivilege(ctx, "isAdmin")
if err != nil {
return nil, err
}
if ec.directives.HasPrivilege == nil {
return nil, errors.New("directive hasPrivilege is not implemented")
}
return ec.directives.HasPrivilege(ctx, nil, directive0, privilege)
}
tmp, err := directive1(rctx)
if err != nil {
return nil, graphql.ErrorOnPath(ctx, err)
}
if tmp == nil {
return nil, nil
}
if data, ok := tmp.([]*model.RelationUserRole); ok {
return data, nil
}
return nil, fmt.Errorf(`unexpected type %T from directive, should be []*somepi.ddns.net/gitea/gilex-dev/YetAnotherToDoList/graph/model.RelationUserRole`, tmp)
})
if err != nil {
ec.Error(ctx, err)
@ -2094,8 +2388,32 @@ func (ec *executionContext) _Query_todos(ctx context.Context, field graphql.Coll
}
}()
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().Todos(rctx)
}
directive1 := func(ctx context.Context) (interface{}, error) {
privilege, err := ec.unmarshalNPrivilege2somepiᚗddnsᚗnetᚋgiteaᚋgilexᚑdevᚋYetAnotherToDoListᚋgraphᚋmodelᚐPrivilege(ctx, "isAdmin")
if err != nil {
return nil, err
}
if ec.directives.HasPrivilege == nil {
return nil, errors.New("directive hasPrivilege is not implemented")
}
return ec.directives.HasPrivilege(ctx, nil, directive0, privilege)
}
tmp, err := directive1(rctx)
if err != nil {
return nil, graphql.ErrorOnPath(ctx, err)
}
if tmp == nil {
return nil, nil
}
if data, ok := tmp.([]*model.Todo); ok {
return data, nil
}
return nil, fmt.Errorf(`unexpected type %T from directive, should be []*somepi.ddns.net/gitea/gilex-dev/YetAnotherToDoList/graph/model.Todo`, tmp)
})
if err != nil {
ec.Error(ctx, err)
@ -2260,8 +2578,32 @@ func (ec *executionContext) _Query_refreshTokens(ctx context.Context, field grap
}
}()
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().RefreshTokens(rctx)
}
directive1 := func(ctx context.Context) (interface{}, error) {
privilege, err := ec.unmarshalNPrivilege2somepiᚗddnsᚗnetᚋgiteaᚋgilexᚑdevᚋYetAnotherToDoListᚋgraphᚋmodelᚐPrivilege(ctx, "isAdmin")
if err != nil {
return nil, err
}
if ec.directives.HasPrivilege == nil {
return nil, errors.New("directive hasPrivilege is not implemented")
}
return ec.directives.HasPrivilege(ctx, nil, directive0, privilege)
}
tmp, err := directive1(rctx)
if err != nil {
return nil, graphql.ErrorOnPath(ctx, err)
}
if tmp == nil {
return nil, nil
}
if data, ok := tmp.([]*model.RefreshToken); ok {
return data, nil
}
return nil, fmt.Errorf(`unexpected type %T from directive, should be []*somepi.ddns.net/gitea/gilex-dev/YetAnotherToDoList/graph/model.RefreshToken`, tmp)
})
if err != nil {
ec.Error(ctx, err)
@ -6005,9 +6347,9 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
if out.Values[i] == graphql.Null {
out.Invalids++
}
case "UpdateUserRole":
case "updateUserRole":
out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) {
return ec._Mutation_UpdateUserRole(ctx, field)
return ec._Mutation_updateUserRole(ctx, field)
})
if out.Values[i] == graphql.Null {
out.Invalids++
@ -7095,6 +7437,16 @@ func (ec *executionContext) unmarshalNNewUser2somepiᚗddnsᚗnetᚋgiteaᚋgile
return res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) unmarshalNPrivilege2somepiᚗddnsᚗnetᚋgiteaᚋgilexᚑdevᚋYetAnotherToDoListᚋgraphᚋmodelᚐPrivilege(ctx context.Context, v interface{}) (model.Privilege, error) {
var res model.Privilege
err := res.UnmarshalGQL(v)
return res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) marshalNPrivilege2somepiᚗddnsᚗnetᚋgiteaᚋgilexᚑdevᚋYetAnotherToDoListᚋgraphᚋmodelᚐPrivilege(ctx context.Context, sel ast.SelectionSet, v model.Privilege) graphql.Marshaler {
return v
}
func (ec *executionContext) marshalNRefreshToken2somepiᚗddnsᚗnetᚋgiteaᚋgilexᚑdevᚋYetAnotherToDoListᚋgraphᚋmodelᚐRefreshToken(ctx context.Context, sel ast.SelectionSet, v model.RefreshToken) graphql.Marshaler {
return ec._RefreshToken(ctx, sel, &v)
}

View File

@ -2,6 +2,12 @@
package model
import (
"fmt"
"io"
"strconv"
)
type NewRefreshToken struct {
TokenName *string `json:"tokenName,omitempty"`
}
@ -78,3 +84,44 @@ type User struct {
Todos []*Todo `json:"todos"`
Roles []*RelationUserRole `json:"roles"`
}
type Privilege string
const (
PrivilegeIsAdmin Privilege = "isAdmin"
PrivilegeIsUserCreator Privilege = "isUserCreator"
)
var AllPrivilege = []Privilege{
PrivilegeIsAdmin,
PrivilegeIsUserCreator,
}
func (e Privilege) IsValid() bool {
switch e {
case PrivilegeIsAdmin, PrivilegeIsUserCreator:
return true
}
return false
}
func (e Privilege) String() string {
return string(e)
}
func (e *Privilege) UnmarshalGQL(v interface{}) error {
str, ok := v.(string)
if !ok {
return fmt.Errorf("enums must be strings")
}
*e = Privilege(str)
if !e.IsValid() {
return fmt.Errorf("%s is not a valid Privilege", str)
}
return nil
}
func (e Privilege) MarshalGQL(w io.Writer) {
fmt.Fprint(w, strconv.Quote(e.String()))
}

View File

@ -60,10 +60,10 @@ type RefreshToken {
}
type Query {
todos: [Todo!]!
todos: [Todo!]! @hasPrivilege(privilege: isAdmin)
users: [User!]!
roles: [Role!]!
refreshTokens: [RefreshToken!]!
refreshTokens: [RefreshToken!]! @hasPrivilege(privilege: isAdmin)
user(id: ID!): User!
todo(id: ID!): Todo!
role(id: ID!): Role!
@ -113,27 +113,38 @@ input UpdateRefreshToken {
}
type Mutation {
createUser(input: NewUser!): User!
createUser(input: NewUser!): User! @hasPrivilege(privilege: isUserCreator)
createTodo(input: NewTodo!): Todo!
createRole(input: NewRole!): Role!
createRole(input: NewRole!): Role! @hasPrivilege(privilege: isAdmin)
createRefreshToken(input: NewRefreshToken!): RefreshToken!
updateTodo(id: ID!, changes: UpdateTodo!): Todo!
updateUser(id: ID!, changes: UpdateUser!): User!
updateRole(id: ID!, changes: UpdateRole!): Role!
@hasPrivilege(privilege: isAdmin)
updateRefreshToken(id: ID!, changes: UpdateRefreshToken!): RefreshToken!
deleteUser(id: ID!): ID
deleteUser(id: ID!): ID @hasPrivilege(privilege: isAdmin)
deleteTodo(id: ID!): ID
deleteRole(id: ID!): ID
deleteRole(id: ID!): ID @hasPrivilege(privilege: isAdmin)
deleteRefreshToken(id: ID!): ID
addUserRole(
userId: ID!
roleId: ID!
userIsRoleManager: Boolean!
): [RelationUserRole!]!
UpdateUserRole(
): [RelationUserRole!]! @hasPrivilege(privilege: isAdmin)
updateUserRole(
userId: ID!
roleId: ID!
userIsRoleManager: Boolean!
): [RelationUserRole!]!
): [RelationUserRole!]! @hasPrivilege(privilege: isAdmin)
removeUserRole(userId: ID!, roleId: ID!): [RelationUserRole!]!
@hasPrivilege(privilege: isAdmin)
}
directive @hasPrivilege(privilege: Privilege!) on FIELD_DEFINITION
directive @asUser(id: ID!) on MUTATION | QUERY
enum Privilege {
isAdmin
isUserCreator
}

View File

@ -15,6 +15,7 @@ import (
// CreateUser is the resolver for the createUser field.
func (r *mutationResolver) CreateUser(ctx context.Context, input model.NewUser) (*model.User, error) {
// Access managed by directive
todo, err := globals.DB.CreateUser(input)
if err != nil {
globals.Logger.Println("Failed to add new user:", err)
@ -25,6 +26,9 @@ func (r *mutationResolver) CreateUser(ctx context.Context, input model.NewUser)
// CreateTodo is the resolver for the createTodo field.
func (r *mutationResolver) CreateTodo(ctx context.Context, input model.NewTodo) (*model.Todo, error) {
if auth.ForContext(ctx).UserId != input.UserID {
return nil, errors.New("only the owner can create a todo")
}
todo, err := globals.DB.CreateTodo(input)
if err != nil {
globals.Logger.Println("Failed to add new todo:", err)
@ -35,6 +39,9 @@ func (r *mutationResolver) CreateTodo(ctx context.Context, input model.NewTodo)
// CreateRole is the resolver for the createRole field.
func (r *mutationResolver) CreateRole(ctx context.Context, input model.NewRole) (*model.Role, error) {
if !auth.ForContext(ctx).IsAdmin {
return nil, errors.New("only an admin can create a role")
}
role, err := globals.DB.CreateRole(&input)
if err != nil {
globals.Logger.Println("Failed to add new role:", err)
@ -46,57 +53,92 @@ func (r *mutationResolver) CreateRole(ctx context.Context, input model.NewRole)
// CreateRefreshToken is the resolver for the createRefreshToken field.
func (r *mutationResolver) CreateRefreshToken(ctx context.Context, input model.NewRefreshToken) (*model.RefreshToken, error) {
// TODO: unify model.RefreshToken & auth.RefreshToken
userToken := auth.ForContext(ctx)
refreshToken, tokenId, err := globals.DB.IssueRefreshToken(userToken.UserId, input.TokenName)
userId := auth.ForContext(ctx).UserId
refreshToken, tokenId, err := globals.DB.IssueRefreshToken(userId, input.TokenName)
if err != nil {
globals.Logger.Println("Failed to create refresh token:", err)
return nil, errors.New("failed to create refresh token")
}
return &model.RefreshToken{ID: tokenId, ExpiryDate: refreshToken.ExpiryDate, TokenName: input.TokenName, Selector: &refreshToken.Selector, Token: &refreshToken.Token, UserID: userToken.UserId}, nil
return &model.RefreshToken{ID: tokenId, ExpiryDate: refreshToken.ExpiryDate, TokenName: input.TokenName, Selector: &refreshToken.Selector, Token: &refreshToken.Token, UserID: userId}, nil
}
// UpdateTodo is the resolver for the updateTodo field.
func (r *mutationResolver) UpdateTodo(ctx context.Context, id string, changes model.UpdateTodo) (*model.Todo, error) {
owner, err := globals.DB.GetTodoOwner(&model.Todo{ID: id})
if err != nil {
return nil, err
}
if owner.ID != auth.ForContext(ctx).UserId && !auth.ForContext(ctx).IsAdmin {
return nil, errors.New("only the owner can update a todo")
}
return globals.DB.UpdateTodo(id, &changes)
}
// UpdateUser is the resolver for the updateUser field.
func (r *mutationResolver) UpdateUser(ctx context.Context, id string, changes model.UpdateUser) (*model.User, error) {
if auth.ForContext(ctx).UserId != id {
return nil, errors.New("can only update yourself")
}
return globals.DB.UpdateUser(id, &changes)
}
// UpdateRole is the resolver for the updateRole field.
func (r *mutationResolver) UpdateRole(ctx context.Context, id string, changes model.UpdateRole) (*model.Role, error) {
// Access managed by directive
return globals.DB.UpdateRole(id, &changes)
}
// UpdateRefreshToken is the resolver for the updateRefreshToken field.
func (r *mutationResolver) UpdateRefreshToken(ctx context.Context, id string, changes model.UpdateRefreshToken) (*model.RefreshToken, error) {
ownerId, err := globals.DB.GetRefreshTokenOwner(id)
if err != nil {
return nil, err
}
if ownerId != auth.ForContext(ctx).UserId && !auth.ForContext(ctx).IsAdmin {
return nil, errors.New("only the owner can update a refresh token")
}
return globals.DB.UpdateRefreshToken(id, &changes)
}
// DeleteUser is the resolver for the deleteUser field.
func (r *mutationResolver) DeleteUser(ctx context.Context, id string) (*string, error) {
// Access managed by directive
return globals.DB.DeleteUser(id)
}
// DeleteTodo is the resolver for the deleteTodo field.
func (r *mutationResolver) DeleteTodo(ctx context.Context, id string) (*string, error) {
owner, err := globals.DB.GetTodoOwner(&model.Todo{ID: id})
if err != nil {
return nil, err
}
if owner.ID != auth.ForContext(ctx).UserId && !auth.ForContext(ctx).IsAdmin {
return nil, errors.New("only the owner can delete a todo")
}
return globals.DB.DeleteTodo(id)
}
// DeleteRole is the resolver for the deleteRole field.
func (r *mutationResolver) DeleteRole(ctx context.Context, id string) (*string, error) {
// Access managed by directive
return globals.DB.DeleteRole(id)
}
// DeleteRefreshToken is the resolver for the deleteRefreshToken field.
func (r *mutationResolver) DeleteRefreshToken(ctx context.Context, id string) (*string, error) {
ownerId, err := globals.DB.GetRefreshTokenOwner(id)
if err != nil {
return nil, err
}
if ownerId != auth.ForContext(ctx).UserId && !auth.ForContext(ctx).IsAdmin {
return nil, errors.New("only the owner can delete a refresh token")
}
return globals.DB.RevokeRefreshToken(id)
}
// AddUserRole is the resolver for the addUserRole field.
func (r *mutationResolver) AddUserRole(ctx context.Context, userID string, roleID string, userIsRoleManager bool) ([]*model.RelationUserRole, error) {
// Access managed by directive
if _, err := globals.DB.AddUserRole(userID, roleID, userIsRoleManager); err != nil {
return nil, err
}
@ -105,6 +147,7 @@ func (r *mutationResolver) AddUserRole(ctx context.Context, userID string, roleI
// UpdateUserRole is the resolver for the UpdateUserRole field.
func (r *mutationResolver) UpdateUserRole(ctx context.Context, userID string, roleID string, userIsRoleManager bool) ([]*model.RelationUserRole, error) {
// Access managed by directive
if _, err := globals.DB.UpdateUserRole(userID, roleID, userIsRoleManager); err != nil {
return nil, err
}
@ -113,6 +156,7 @@ func (r *mutationResolver) UpdateUserRole(ctx context.Context, userID string, ro
// RemoveUserRole is the resolver for the RemoveUserRole field.
func (r *mutationResolver) RemoveUserRole(ctx context.Context, userID string, roleID string) ([]*model.RelationUserRole, error) {
// Access managed by directive
if _, err := globals.DB.RemoveUserRole(userID, roleID); err != nil {
return nil, err
}
@ -121,6 +165,7 @@ func (r *mutationResolver) RemoveUserRole(ctx context.Context, userID string, ro
// Todos is the resolver for the todos field.
func (r *queryResolver) Todos(ctx context.Context) ([]*model.Todo, error) {
// Access managed by directive
return globals.DB.GetAllTodos()
}
@ -136,6 +181,7 @@ func (r *queryResolver) Roles(ctx context.Context) ([]*model.Role, error) {
// RefreshTokens is the resolver for the refreshTokens field.
func (r *queryResolver) RefreshTokens(ctx context.Context) ([]*model.RefreshToken, error) {
// Access managed by directive
return globals.DB.GetAllRefreshTokens()
}
@ -146,6 +192,13 @@ func (r *queryResolver) User(ctx context.Context, id string) (*model.User, error
// Todo is the resolver for the todo field.
func (r *queryResolver) Todo(ctx context.Context, id string) (*model.Todo, error) {
owner, err := globals.DB.GetTodoOwner(&model.Todo{ID: id})
if err != nil {
return nil, err
}
if owner.ID != auth.ForContext(ctx).UserId && !auth.ForContext(ctx).IsAdmin {
return nil, errors.New("only the owner can view a todo")
}
return globals.DB.GetTodo(&model.Todo{ID: id})
}
@ -156,7 +209,14 @@ func (r *queryResolver) Role(ctx context.Context, id string) (*model.Role, error
// RefreshToken is the resolver for the refreshToken field.
func (r *queryResolver) RefreshToken(ctx context.Context, id string) (*model.RefreshToken, error) {
return globals.DB.GetRefreshToken(&model.RefreshToken{ID: id})
ownerId, err := globals.DB.GetRefreshTokenOwner(id)
if err != nil {
return nil, err
}
if ownerId != auth.ForContext(ctx).UserId && !auth.ForContext(ctx).IsAdmin {
return nil, errors.New("only the owner can view a refresh token")
}
return globals.DB.GetRefreshToken(&model.RefreshToken{ID: id, UserID: ownerId})
}
// RoleMembers is the resolver for the roleMembers field.
@ -172,6 +232,9 @@ func (r *todoResolver) User(ctx context.Context, obj *model.Todo) (*model.User,
// Todos is the resolver for the todos field.
func (r *userResolver) Todos(ctx context.Context, obj *model.User) ([]*model.Todo, error) {
if auth.ForContext(ctx).UserId != obj.ID && !auth.ForContext(ctx).IsAdmin {
return nil, errors.New("only the owner can see this")
}
return globals.DB.GetTodosFrom(obj)
}

View File

@ -17,15 +17,18 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package server
import (
"context"
"fmt"
"net/http"
"strconv"
"github.com/99designs/gqlgen/graphql"
"github.com/99designs/gqlgen/graphql/handler"
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
"somepi.ddns.net/gitea/gilex-dev/YetAnotherToDoList/globals"
"somepi.ddns.net/gitea/gilex-dev/YetAnotherToDoList/graph"
"somepi.ddns.net/gitea/gilex-dev/YetAnotherToDoList/graph/model"
"somepi.ddns.net/gitea/gilex-dev/YetAnotherToDoList/server/auth"
)
@ -39,11 +42,40 @@ func StartServer(portHTTP int, portHTTPS int, certFile string, keyFile string) {
fmt.Fprintf(w, "%s %s", globals.Version, globals.CommitHash)
})
srv := handler.NewDefaultServer(graph.NewExecutableSchema(graph.Config{Resolvers: &graph.Resolver{}}))
router.HandleFunc("/auth/login", auth.IssueRefreshTokenHandler)
router.HandleFunc("/auth", auth.IssueAccessTokenHandler)
router.Group(func(r chi.Router) {
config := graph.Config{Resolvers: &graph.Resolver{}}
config.Directives.HasPrivilege = func(ctx context.Context, obj interface{}, next graphql.Resolver, privilege model.Privilege) (interface{}, error) {
switch privilege {
case model.PrivilegeIsUserCreator:
if !auth.ForContext(ctx).IsUserCreator {
return nil, fmt.Errorf("access denied: you need IsUserCreator")
}
case model.PrivilegeIsAdmin:
if !auth.ForContext(ctx).IsAdmin {
return nil, fmt.Errorf("access denied: you need IsAdmin")
}
}
// or let it pass through
return next(ctx)
}
config.Directives.AsUser = func(ctx context.Context, obj interface{}, next graphql.Resolver, id string) (interface{}, error) {
if !auth.ForContext(ctx).IsAdmin {
// block calling the next resolver
return nil, fmt.Errorf("access denied: you need IsAdmin to use the asUser directive")
}
// or let it pass through
fmt.Printf("Running as %s instead of %s\n", id, auth.ForContext(ctx).UserId) //DEBUG
auth.ForContext(ctx).UserId = id
return next(ctx)
}
srv := handler.NewDefaultServer(graph.NewExecutableSchema(config))
r.Use(auth.Middleware())
r.Handle("/api", srv)
r.HandleFunc("/protected", func(w http.ResponseWriter, r *http.Request) {