1
0

Move the crudhandler to own repo (#27)

This commit is contained in:
konrad
2018-11-30 23:26:56 +00:00
committed by Gitea
parent d9304f6996
commit ce2cae9430
228 changed files with 13281 additions and 3292 deletions

View File

@ -1,26 +0,0 @@
// Vikunja is a todo-list application to facilitate your life.
// Copyright 2018 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package models
// CRUDable defines the crud methods
type CRUDable interface {
Create(*User) error
ReadOne() error
ReadAll(string, *User, int) (interface{}, error)
Update() error
Delete() error
}

View File

@ -1,5 +1,5 @@
// Vikunja is a todo-list application to facilitate your life.
// Copyright 2018 Vikunja and contributors. All rights reserved.
// Copyright 2018 Vikunja and contributors. All web.Rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@ -17,22 +17,11 @@
package models
import (
"code.vikunja.io/web"
"fmt"
"net/http"
)
// HTTPErrorProcessor is executed when the defined error is thrown, it will make sure the user sees an appropriate error message and http status code
type HTTPErrorProcessor interface {
HTTPError() HTTPError
}
// HTTPError holds informations about an http error
type HTTPError struct {
HTTPCode int `json:"-"`
Code int `json:"code"`
Message string `json:"message"`
}
// =====================
// User Operation Errors
// =====================
@ -57,8 +46,8 @@ func (err ErrUsernameExists) Error() string {
const ErrorCodeUsernameExists = 1001
// HTTPError holds the http error description
func (err ErrUsernameExists) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrorCodeUsernameExists, Message: "A user with this username already exists."}
func (err ErrUsernameExists) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrorCodeUsernameExists, Message: "A user with this username already exists."}
}
// ErrUserEmailExists represents a "UserEmailExists" kind of error.
@ -81,8 +70,8 @@ func (err ErrUserEmailExists) Error() string {
const ErrorCodeUserEmailExists = 1002
// HTTPError holds the http error description
func (err ErrUserEmailExists) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrorCodeUserEmailExists, Message: "A user with this email address already exists."}
func (err ErrUserEmailExists) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrorCodeUserEmailExists, Message: "A user with this email address already exists."}
}
// ErrNoUsernamePassword represents a "NoUsernamePassword" kind of error.
@ -102,8 +91,8 @@ func (err ErrNoUsernamePassword) Error() string {
const ErrCodeNoUsernamePassword = 1004
// HTTPError holds the http error description
func (err ErrNoUsernamePassword) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeNoUsernamePassword, Message: "Please specify a username and a password."}
func (err ErrNoUsernamePassword) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeNoUsernamePassword, Message: "Please specify a username and a password."}
}
// ErrUserDoesNotExist represents a "UserDoesNotExist" kind of error.
@ -125,8 +114,8 @@ func (err ErrUserDoesNotExist) Error() string {
const ErrCodeUserDoesNotExist = 1005
// HTTPError holds the http error description
func (err ErrUserDoesNotExist) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeUserDoesNotExist, Message: "The user does not exist."}
func (err ErrUserDoesNotExist) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeUserDoesNotExist, Message: "The user does not exist."}
}
// ErrCouldNotGetUserID represents a "ErrCouldNotGetUserID" kind of error.
@ -146,8 +135,8 @@ func (err ErrCouldNotGetUserID) Error() string {
const ErrCodeCouldNotGetUserID = 1006
// HTTPError holds the http error description
func (err ErrCouldNotGetUserID) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeCouldNotGetUserID, Message: "Could not get user id."}
func (err ErrCouldNotGetUserID) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeCouldNotGetUserID, Message: "Could not get user id."}
}
// ErrNoPasswordResetToken represents an error where no password reset token exists for that user
@ -163,8 +152,8 @@ func (err ErrNoPasswordResetToken) Error() string {
const ErrCodeNoPasswordResetToken = 1008
// HTTPError holds the http error description
func (err ErrNoPasswordResetToken) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeNoPasswordResetToken, Message: "No token to reset a user's password provided."}
func (err ErrNoPasswordResetToken) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeNoPasswordResetToken, Message: "No token to reset a user's password provided."}
}
// ErrInvalidPasswordResetToken is an error where the password reset token is invalid
@ -180,8 +169,8 @@ func (err ErrInvalidPasswordResetToken) Error() string {
const ErrCodeInvalidPasswordResetToken = 1009
// HTTPError holds the http error description
func (err ErrInvalidPasswordResetToken) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeInvalidPasswordResetToken, Message: "Invalid token to reset a user's password."}
func (err ErrInvalidPasswordResetToken) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeInvalidPasswordResetToken, Message: "Invalid token to reset a user's password."}
}
// IsErrInvalidPasswordResetToken checks if an error is a ErrInvalidPasswordResetToken.
@ -203,8 +192,8 @@ func (err ErrInvalidEmailConfirmToken) Error() string {
const ErrCodeInvalidEmailConfirmToken = 1010
// HTTPError holds the http error description
func (err ErrInvalidEmailConfirmToken) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeInvalidEmailConfirmToken, Message: "Invalid email confirm token."}
func (err ErrInvalidEmailConfirmToken) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeInvalidEmailConfirmToken, Message: "Invalid email confirm token."}
}
// IsErrInvalidEmailConfirmToken checks if an error is a ErrInvalidEmailConfirmToken.
@ -225,8 +214,8 @@ func (err ErrWrongUsernameOrPassword) Error() string {
const ErrCodeWrongUsernameOrPassword = 1011
// HTTPError holds the http error description
func (err ErrWrongUsernameOrPassword) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeWrongUsernameOrPassword, Message: "Wrong username or password."}
func (err ErrWrongUsernameOrPassword) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeWrongUsernameOrPassword, Message: "Wrong username or password."}
}
// IsErrWrongUsernameOrPassword checks if an error is a IsErrEmailNotConfirmed.
@ -248,8 +237,8 @@ func (err ErrEmailNotConfirmed) Error() string {
const ErrCodeEmailNotConfirmed = 1012
// HTTPError holds the http error description
func (err ErrEmailNotConfirmed) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeEmailNotConfirmed, Message: "Please confirm your email address."}
func (err ErrEmailNotConfirmed) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeEmailNotConfirmed, Message: "Please confirm your email address."}
}
// IsErrEmailNotConfirmed checks if an error is a IsErrEmailNotConfirmed.
@ -279,8 +268,8 @@ func (err ErrIDCannotBeZero) Error() string {
const ErrCodeIDCannotBeZero = 2001
// HTTPError holds the http error description
func (err ErrIDCannotBeZero) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeIDCannotBeZero, Message: "The ID cannot be empty or 0."}
func (err ErrIDCannotBeZero) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeIDCannotBeZero, Message: "The ID cannot be empty or 0."}
}
// ErrInvalidData represents a "ErrInvalidData" kind of error. Used when a struct is invalid -> validation failed.
@ -302,13 +291,13 @@ func (err ErrInvalidData) Error() string {
const ErrCodeInvalidData = 2002
// HTTPError holds the http error description
func (err ErrInvalidData) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeInvalidData, Message: err.Message}
func (err ErrInvalidData) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeInvalidData, Message: err.Message}
}
// ValidationHTTPError is the http error when a validation fails
type ValidationHTTPError struct {
HTTPError
web.HTTPError
InvalidFields []string `json:"invalid_fields"`
}
@ -343,8 +332,8 @@ func (err ErrListDoesNotExist) Error() string {
const ErrCodeListDoesNotExist = 3001
// HTTPError holds the http error description
func (err ErrListDoesNotExist) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeListDoesNotExist, Message: "This list does not exist."}
func (err ErrListDoesNotExist) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeListDoesNotExist, Message: "This list does not exist."}
}
// ErrNeedToHaveListReadAccess represents an error, where the user dont has read access to that List
@ -367,8 +356,8 @@ func (err ErrNeedToHaveListReadAccess) Error() string {
const ErrCodeNeedToHaveListReadAccess = 3004
// HTTPError holds the http error description
func (err ErrNeedToHaveListReadAccess) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeNeedToHaveListReadAccess, Message: "You need to have read access to this list."}
func (err ErrNeedToHaveListReadAccess) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeNeedToHaveListReadAccess, Message: "You need to have read access to this list."}
}
// ErrListTitleCannotBeEmpty represents a "ErrListTitleCannotBeEmpty" kind of error. Used if the list does not exist.
@ -388,8 +377,8 @@ func (err ErrListTitleCannotBeEmpty) Error() string {
const ErrCodeListTitleCannotBeEmpty = 3005
// HTTPError holds the http error description
func (err ErrListTitleCannotBeEmpty) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeListTitleCannotBeEmpty, Message: "You must provide at least a list title."}
func (err ErrListTitleCannotBeEmpty) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeListTitleCannotBeEmpty, Message: "You must provide at least a list title."}
}
// ================
@ -413,8 +402,8 @@ func (err ErrListTaskCannotBeEmpty) Error() string {
const ErrCodeListTaskCannotBeEmpty = 4001
// HTTPError holds the http error description
func (err ErrListTaskCannotBeEmpty) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeListTaskCannotBeEmpty, Message: "You must provide at least a list task text."}
func (err ErrListTaskCannotBeEmpty) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeListTaskCannotBeEmpty, Message: "You must provide at least a list task text."}
}
// ErrListTaskDoesNotExist represents a "ErrListDoesNotExist" kind of error. Used if the list does not exist.
@ -436,8 +425,8 @@ func (err ErrListTaskDoesNotExist) Error() string {
const ErrCodeListTaskDoesNotExist = 4002
// HTTPError holds the http error description
func (err ErrListTaskDoesNotExist) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeListTaskDoesNotExist, Message: "This list task does not exist"}
func (err ErrListTaskDoesNotExist) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeListTaskDoesNotExist, Message: "This list task does not exist"}
}
// =================
@ -463,8 +452,8 @@ func (err ErrNamespaceDoesNotExist) Error() string {
const ErrCodeNamespaceDoesNotExist = 5001
// HTTPError holds the http error description
func (err ErrNamespaceDoesNotExist) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeNamespaceDoesNotExist, Message: "Namespace not found."}
func (err ErrNamespaceDoesNotExist) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeNamespaceDoesNotExist, Message: "Namespace not found."}
}
// ErrUserDoesNotHaveAccessToNamespace represents an error, where the user is not the owner of that namespace (used i.e. when deleting a namespace)
@ -487,8 +476,8 @@ func (err ErrUserDoesNotHaveAccessToNamespace) Error() string {
const ErrCodeUserDoesNotHaveAccessToNamespace = 5003
// HTTPError holds the http error description
func (err ErrUserDoesNotHaveAccessToNamespace) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeUserDoesNotHaveAccessToNamespace, Message: "This user does not have access to the namespace."}
func (err ErrUserDoesNotHaveAccessToNamespace) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeUserDoesNotHaveAccessToNamespace, Message: "This user does not have access to the namespace."}
}
// ErrNamespaceNameCannotBeEmpty represents an error, where a namespace name is empty.
@ -511,8 +500,8 @@ func (err ErrNamespaceNameCannotBeEmpty) Error() string {
const ErrCodeNamespaceNameCannotBeEmpty = 5006
// HTTPError holds the http error description
func (err ErrNamespaceNameCannotBeEmpty) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeNamespaceNameCannotBeEmpty, Message: "The namespace name cannot be empty."}
func (err ErrNamespaceNameCannotBeEmpty) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeNamespaceNameCannotBeEmpty, Message: "The namespace name cannot be empty."}
}
// ErrNeedToHaveNamespaceReadAccess represents an error, where the user is not the owner of that namespace (used i.e. when deleting a namespace)
@ -535,8 +524,8 @@ func (err ErrNeedToHaveNamespaceReadAccess) Error() string {
const ErrCodeNeedToHaveNamespaceReadAccess = 5009
// HTTPError holds the http error description
func (err ErrNeedToHaveNamespaceReadAccess) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeNeedToHaveNamespaceReadAccess, Message: "You need to have namespace read access to do this."}
func (err ErrNeedToHaveNamespaceReadAccess) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeNeedToHaveNamespaceReadAccess, Message: "You need to have namespace read access to do this."}
}
// ErrTeamDoesNotHaveAccessToNamespace represents an error, where the Team is not the owner of that namespace (used i.e. when deleting a namespace)
@ -559,8 +548,8 @@ func (err ErrTeamDoesNotHaveAccessToNamespace) Error() string {
const ErrCodeTeamDoesNotHaveAccessToNamespace = 5010
// HTTPError holds the http error description
func (err ErrTeamDoesNotHaveAccessToNamespace) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeTeamDoesNotHaveAccessToNamespace, Message: "You need to have access to this namespace to do this."}
func (err ErrTeamDoesNotHaveAccessToNamespace) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeTeamDoesNotHaveAccessToNamespace, Message: "You need to have access to this namespace to do this."}
}
// ErrUserAlreadyHasNamespaceAccess represents an error where a user already has access to a namespace
@ -583,8 +572,8 @@ func (err ErrUserAlreadyHasNamespaceAccess) Error() string {
const ErrCodeUserAlreadyHasNamespaceAccess = 5011
// HTTPError holds the http error description
func (err ErrUserAlreadyHasNamespaceAccess) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusConflict, Code: ErrCodeUserAlreadyHasNamespaceAccess, Message: "This user already has access to this namespace."}
func (err ErrUserAlreadyHasNamespaceAccess) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusConflict, Code: ErrCodeUserAlreadyHasNamespaceAccess, Message: "This user already has access to this namespace."}
}
// ============
@ -610,8 +599,8 @@ func (err ErrTeamNameCannotBeEmpty) Error() string {
const ErrCodeTeamNameCannotBeEmpty = 6001
// HTTPError holds the http error description
func (err ErrTeamNameCannotBeEmpty) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeTeamNameCannotBeEmpty, Message: "The team name cannot be empty"}
func (err ErrTeamNameCannotBeEmpty) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeTeamNameCannotBeEmpty, Message: "The team name cannot be empty"}
}
// ErrTeamDoesNotExist represents an error where a team does not exist
@ -633,8 +622,8 @@ func (err ErrTeamDoesNotExist) Error() string {
const ErrCodeTeamDoesNotExist = 6002
// HTTPError holds the http error description
func (err ErrTeamDoesNotExist) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeTeamDoesNotExist, Message: "This team does not exist."}
func (err ErrTeamDoesNotExist) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeTeamDoesNotExist, Message: "This team does not exist."}
}
// ErrInvalidTeamRight represents an error where a team right is invalid
@ -656,8 +645,8 @@ func (err ErrInvalidTeamRight) Error() string {
const ErrCodeInvalidTeamRight = 6003
// HTTPError holds the http error description
func (err ErrInvalidTeamRight) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeInvalidTeamRight, Message: "The team right is invalid."}
func (err ErrInvalidTeamRight) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeInvalidTeamRight, Message: "The team right is invalid."}
}
// ErrTeamAlreadyHasAccess represents an error where a team already has access to a list/namespace
@ -680,8 +669,8 @@ func (err ErrTeamAlreadyHasAccess) Error() string {
const ErrCodeTeamAlreadyHasAccess = 6004
// HTTPError holds the http error description
func (err ErrTeamAlreadyHasAccess) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusConflict, Code: ErrCodeTeamAlreadyHasAccess, Message: "This team already has access."}
func (err ErrTeamAlreadyHasAccess) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusConflict, Code: ErrCodeTeamAlreadyHasAccess, Message: "This team already has access."}
}
// ErrUserIsMemberOfTeam represents an error where a user is already member of a team.
@ -704,8 +693,8 @@ func (err ErrUserIsMemberOfTeam) Error() string {
const ErrCodeUserIsMemberOfTeam = 6005
// HTTPError holds the http error description
func (err ErrUserIsMemberOfTeam) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusConflict, Code: ErrCodeUserIsMemberOfTeam, Message: "This user is already a member of that team."}
func (err ErrUserIsMemberOfTeam) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusConflict, Code: ErrCodeUserIsMemberOfTeam, Message: "This user is already a member of that team."}
}
// ErrCannotDeleteLastTeamMember represents an error where a user wants to delete the last member of a team (probably himself)
@ -728,8 +717,8 @@ func (err ErrCannotDeleteLastTeamMember) Error() string {
const ErrCodeCannotDeleteLastTeamMember = 6006
// HTTPError holds the http error description
func (err ErrCannotDeleteLastTeamMember) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeCannotDeleteLastTeamMember, Message: "You cannot delete the last member of a team."}
func (err ErrCannotDeleteLastTeamMember) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeCannotDeleteLastTeamMember, Message: "You cannot delete the last member of a team."}
}
// ErrTeamDoesNotHaveAccessToList represents an error, where the Team is not the owner of that List (used i.e. when deleting a List)
@ -752,8 +741,8 @@ func (err ErrTeamDoesNotHaveAccessToList) Error() string {
const ErrCodeTeamDoesNotHaveAccessToList = 6007
// HTTPError holds the http error description
func (err ErrTeamDoesNotHaveAccessToList) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeTeamDoesNotHaveAccessToList, Message: "This team does not have access to the list."}
func (err ErrTeamDoesNotHaveAccessToList) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeTeamDoesNotHaveAccessToList, Message: "This team does not have access to the list."}
}
// ====================
@ -779,8 +768,8 @@ func (err ErrInvalidUserRight) Error() string {
const ErrCodeInvalidUserRight = 7001
// HTTPError holds the http error description
func (err ErrInvalidUserRight) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeInvalidUserRight, Message: "The user right is invalid."}
func (err ErrInvalidUserRight) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeInvalidUserRight, Message: "The user right is invalid."}
}
// ErrUserAlreadyHasAccess represents an error where a user already has access to a list/namespace
@ -803,8 +792,8 @@ func (err ErrUserAlreadyHasAccess) Error() string {
const ErrCodeUserAlreadyHasAccess = 7002
// HTTPError holds the http error description
func (err ErrUserAlreadyHasAccess) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusConflict, Code: ErrCodeUserAlreadyHasAccess, Message: "This user already has access to this list."}
func (err ErrUserAlreadyHasAccess) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusConflict, Code: ErrCodeUserAlreadyHasAccess, Message: "This user already has access to this list."}
}
// ErrUserDoesNotHaveAccessToList represents an error, where the user is not the owner of that List (used i.e. when deleting a List)
@ -827,6 +816,6 @@ func (err ErrUserDoesNotHaveAccessToList) Error() string {
const ErrCodeUserDoesNotHaveAccessToList = 7003
// HTTPError holds the http error description
func (err ErrUserDoesNotHaveAccessToList) HTTPError() HTTPError {
return HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeUserDoesNotHaveAccessToList, Message: "This user does not have access to the list."}
func (err ErrUserDoesNotHaveAccessToList) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeUserDoesNotHaveAccessToList, Message: "This user does not have access to the list."}
}

View File

@ -16,7 +16,10 @@
package models
import "sort"
import (
"code.vikunja.io/web"
"sort"
)
// List represents a list of tasks
type List struct {
@ -32,8 +35,8 @@ type List struct {
Created int64 `xorm:"created" json:"created"`
Updated int64 `xorm:"updated" json:"updated"`
CRUDable `xorm:"-" json:"-"`
Rights `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
web.Rights `xorm:"-" json:"-"`
}
// GetListsByNamespaceID gets all lists in a namespace
@ -55,7 +58,12 @@ func GetListsByNamespaceID(nID int64) (lists []*List, err error) {
// @Failure 403 {object} models.HTTPError "The user does not have access to the list"
// @Failure 500 {object} models.Message "Internal error"
// @Router /lists [get]
func (l *List) ReadAll(search string, u *User, page int) (interface{}, error) {
func (l *List) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
u, err := getUserWithError(a)
if err != nil {
return nil, err
}
lists, err := getRawListsForUser(search, u, page)
if err != nil {
return nil, err
@ -206,7 +214,12 @@ func AddListDetails(lists []*List) (err error) {
// @Success 200 {array} models.List "The tasks"
// @Failure 500 {object} models.Message "Internal error"
// @Router /tasks [get]
func (lt *ListTask) ReadAll(search string, u *User, page int) (interface{}, error) {
func (lt *ListTask) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
u, err := getUserWithError(a)
if err != nil {
return nil, err
}
return GetTasksByUser(search, u, page)
}

View File

@ -16,6 +16,8 @@
package models
import "code.vikunja.io/web"
// CreateOrUpdateList updates a list or creates it if it doesn't exist
func CreateOrUpdateList(list *List) (err error) {
@ -84,7 +86,12 @@ func (l *List) Update() (err error) {
// @Failure 403 {object} models.HTTPError "The user does not have access to the list"
// @Failure 500 {object} models.Message "Internal error"
// @Router /namespaces/{namespaceID}/lists [put]
func (l *List) Create(doer *User) (err error) {
func (l *List) Create(a web.Auth) (err error) {
doer, err := getUserWithError(a)
if err != nil {
return err
}
// Check rights
u, err := GetUserByID(doer.ID)
if err != nil {

View File

@ -18,10 +18,13 @@ package models
import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/web"
)
// IsAdmin returns whether the user has admin rights on the list or not
func (l *List) IsAdmin(u *User) bool {
func (l *List) IsAdmin(a web.Auth) bool {
u := getUserForRights(a)
// Owners are always admins
if l.Owner.ID == u.ID {
return true
@ -36,7 +39,9 @@ func (l *List) IsAdmin(u *User) bool {
}
// CanWrite return whether the user can write on that list or not
func (l *List) CanWrite(user *User) bool {
func (l *List) CanWrite(a web.Auth) bool {
user := getUserForRights(a)
// Admins always have write access
if l.IsAdmin(user) {
return true
@ -51,7 +56,9 @@ func (l *List) CanWrite(user *User) bool {
}
// CanRead checks if a user has read access to a list
func (l *List) CanRead(user *User) bool {
func (l *List) CanRead(a web.Auth) bool {
user := getUserForRights(a)
// Admins always have read access
if l.IsAdmin(user) {
return true
@ -66,7 +73,9 @@ func (l *List) CanRead(user *User) bool {
}
// CanDelete checks if the user can delete a list
func (l *List) CanDelete(doer *User) bool {
func (l *List) CanDelete(a web.Auth) bool {
doer := getUserForRights(a)
if err := l.GetSimpleByID(); err != nil {
log.Log.Error("Error occurred during CanDelete for List: %s", err)
return false
@ -75,7 +84,9 @@ func (l *List) CanDelete(doer *User) bool {
}
// CanUpdate checks if the user can update a list
func (l *List) CanUpdate(doer *User) bool {
func (l *List) CanUpdate(a web.Auth) bool {
doer := getUserForRights(a)
if err := l.GetSimpleByID(); err != nil {
log.Log.Error("Error occurred during CanUpdate for List: %s", err)
return false
@ -84,10 +95,10 @@ func (l *List) CanUpdate(doer *User) bool {
}
// CanCreate checks if the user can update a list
func (l *List) CanCreate(doer *User) bool {
func (l *List) CanCreate(a web.Auth) bool {
// A user can create a list if he has write access to the namespace
n, _ := GetNamespaceByID(l.NamespaceID)
return n.CanWrite(doer)
return n.CanWrite(a)
}
func (l *List) checkListTeamRight(user *User, r TeamRight) bool {

View File

@ -16,6 +16,8 @@
package models
import "code.vikunja.io/web"
// ListTask represents an task in a todolist
type ListTask struct {
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"listtask"`
@ -33,8 +35,8 @@ type ListTask struct {
CreatedBy User `xorm:"-" json:"createdBy" valid:"-"`
CRUDable `xorm:"-" json:"-"`
Rights `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
web.Rights `xorm:"-" json:"-"`
}
// TableName returns the table name for listtasks

View File

@ -17,6 +17,7 @@
package models
import (
"code.vikunja.io/web"
"github.com/imdario/mergo"
)
@ -34,7 +35,12 @@ import (
// @Failure 403 {object} models.HTTPError "The user does not have access to the list"
// @Failure 500 {object} models.Message "Internal error"
// @Router /lists/{id} [put]
func (i *ListTask) Create(doer *User) (err error) {
func (i *ListTask) Create(a web.Auth) (err error) {
doer, err := getUserWithError(a)
if err != nil {
return err
}
i.ID = 0
// Check if we have at least a text

View File

@ -18,10 +18,13 @@ package models
import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/web"
)
// CanDelete checks if the user can delete an task
func (i *ListTask) CanDelete(doer *User) bool {
func (i *ListTask) CanDelete(a web.Auth) bool {
doer := getUserForRights(a)
// Get the task
lI, err := GetListTaskByID(i.ID)
if err != nil {
@ -36,7 +39,9 @@ func (i *ListTask) CanDelete(doer *User) bool {
}
// CanUpdate determines if a user has the right to update a list task
func (i *ListTask) CanUpdate(doer *User) bool {
func (i *ListTask) CanUpdate(a web.Auth) bool {
doer := getUserForRights(a)
// Get the task
lI, err := GetListTaskByID(i.ID)
if err != nil {
@ -51,7 +56,9 @@ func (i *ListTask) CanUpdate(doer *User) bool {
}
// CanCreate determines if a user has the right to create a list task
func (i *ListTask) CanCreate(doer *User) bool {
func (i *ListTask) CanCreate(a web.Auth) bool {
doer := getUserForRights(a)
// A user can create an task if he has write acces to its list
l := &List{ID: i.ListID}
l.ReadOne()

View File

@ -16,6 +16,8 @@
package models
import "code.vikunja.io/web"
// ListUser represents a list <-> user relation
type ListUser struct {
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"namespace"`
@ -26,8 +28,8 @@ type ListUser struct {
Created int64 `xorm:"created" json:"created"`
Updated int64 `xorm:"updated" json:"updated"`
CRUDable `xorm:"-" json:"-"`
Rights `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
web.Rights `xorm:"-" json:"-"`
}
// TableName is the table name for ListUser

View File

@ -16,6 +16,8 @@
package models
import "code.vikunja.io/web"
// Create creates a new list <-> user relation
// @Summary Add a user to a list
// @Description Gives a user access to a list.
@ -31,7 +33,7 @@ package models
// @Failure 403 {object} models.HTTPError "The user does not have access to the list"
// @Failure 500 {object} models.Message "Internal error"
// @Router /lists/{id}/users [put]
func (ul *ListUser) Create(u *User) (err error) {
func (ul *ListUser) Create(a web.Auth) (err error) {
// Check if the right is valid
if err := ul.Right.isValid(); err != nil {

View File

@ -16,6 +16,8 @@
package models
import "code.vikunja.io/web"
// ReadAll gets all users who have access to a list
// @Summary Get users on a list
// @Description Returns a list with all users which have access on a given list.
@ -30,7 +32,12 @@ package models
// @Failure 403 {object} models.HTTPError "No right to see the list."
// @Failure 500 {object} models.Message "Internal error"
// @Router /lists/{id}/users [get]
func (ul *ListUser) ReadAll(search string, u *User, page int) (interface{}, error) {
func (ul *ListUser) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
u, err := getUserWithError(a)
if err != nil {
return nil, err
}
// Check if the user has access to the list
l := &List{ID: ul.ListID}
if err := l.GetSimpleByID(); err != nil {
@ -42,7 +49,7 @@ func (ul *ListUser) ReadAll(search string, u *User, page int) (interface{}, erro
// Get all users
all := []*UserWithRight{}
err := x.
err = x.
Join("INNER", "users_list", "user_id = users.id").
Where("users_list.list_id = ?", ul.ListID).
Limit(getLimitFromPageIndex(page)).

View File

@ -18,10 +18,13 @@ package models
import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/web"
)
// CanCreate checks if the user can create a new user <-> list relation
func (lu *ListUser) CanCreate(doer *User) bool {
func (lu *ListUser) CanCreate(a web.Auth) bool {
doer := getUserForRights(a)
// Get the list and check if the user has write access on it
l := List{ID: lu.ListID}
if err := l.GetSimpleByID(); err != nil {
@ -32,7 +35,9 @@ func (lu *ListUser) CanCreate(doer *User) bool {
}
// CanDelete checks if the user can delete a user <-> list relation
func (lu *ListUser) CanDelete(doer *User) bool {
func (lu *ListUser) CanDelete(a web.Auth) bool {
doer := getUserForRights(a)
// Get the list and check if the user has write access on it
l := List{ID: lu.ListID}
if err := l.GetSimpleByID(); err != nil {
@ -43,7 +48,9 @@ func (lu *ListUser) CanDelete(doer *User) bool {
}
// CanUpdate checks if the user can update a user <-> list relation
func (lu *ListUser) CanUpdate(doer *User) bool {
func (lu *ListUser) CanUpdate(a web.Auth) bool {
doer := getUserForRights(a)
// Get the list and check if the user has write access on it
l := List{ID: lu.ListID}
if err := l.GetSimpleByID(); err != nil {

View File

@ -16,7 +16,10 @@
package models
import "time"
import (
"code.vikunja.io/web"
"time"
)
// Namespace holds informations about a namespace
type Namespace struct {
@ -30,8 +33,8 @@ type Namespace struct {
Created int64 `xorm:"created" json:"created"`
Updated int64 `xorm:"updated" json:"updated"`
CRUDable `xorm:"-" json:"-"`
Rights `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
web.Rights `xorm:"-" json:"-"`
}
// TableName makes beautiful table names
@ -99,7 +102,11 @@ type NamespaceWithLists struct {
// @Success 200 {array} models.NamespaceWithLists "The Namespaces."
// @Failure 500 {object} models.Message "Internal error"
// @Router /namespaces [get]
func (n *Namespace) ReadAll(search string, doer *User, page int) (interface{}, error) {
func (n *Namespace) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
doer, err := getUserWithError(a)
if err != nil {
return nil, err
}
all := []*NamespaceWithLists{}
@ -117,7 +124,7 @@ func (n *Namespace) ReadAll(search string, doer *User, page int) (interface{}, e
[]*List{},
})
err := x.Select("namespaces.*").
err = x.Select("namespaces.*").
Table("namespaces").
Join("LEFT", "team_namespaces", "namespaces.id = team_namespaces.namespace_id").
Join("LEFT", "team_members", "team_members.team_id = team_namespaces.team_id").

View File

@ -16,6 +16,8 @@
package models
import "code.vikunja.io/web"
// Create implements the creation method via the interface
// @Summary Creates a new namespace
// @Description Creates a new namespace.
@ -29,7 +31,12 @@ package models
// @Failure 403 {object} models.HTTPError "The user does not have access to the namespace"
// @Failure 500 {object} models.Message "Internal error"
// @Router /namespaces [put]
func (n *Namespace) Create(doer *User) (err error) {
func (n *Namespace) Create(a web.Auth) (err error) {
doer, err := getUserWithError(a)
if err != nil {
return err
}
// Check if we have at least a name
if n.Name == "" {
return ErrNamespaceNameCannotBeEmpty{NamespaceID: 0, UserID: doer.ID}

View File

@ -18,10 +18,13 @@ package models
import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/web"
)
// IsAdmin returns true or false if the user is admin on that namespace or not
func (n *Namespace) IsAdmin(u *User) bool {
func (n *Namespace) IsAdmin(a web.Auth) bool {
u := getUserForRights(a)
// Owners always have admin rights
if u.ID == n.Owner.ID {
return true
@ -37,7 +40,9 @@ func (n *Namespace) IsAdmin(u *User) bool {
}
// CanWrite checks if a user has write access to a namespace
func (n *Namespace) CanWrite(u *User) bool {
func (n *Namespace) CanWrite(a web.Auth) bool {
u := getUserForRights(a)
// Admins always have write access
if n.IsAdmin(u) {
return true
@ -53,7 +58,9 @@ func (n *Namespace) CanWrite(u *User) bool {
}
// CanRead checks if a user has read access to that namespace
func (n *Namespace) CanRead(u *User) bool {
func (n *Namespace) CanRead(a web.Auth) bool {
u := getUserForRights(a)
// Admins always have read access
if n.IsAdmin(u) {
return true
@ -69,7 +76,9 @@ func (n *Namespace) CanRead(u *User) bool {
}
// CanUpdate checks if the user can update the namespace
func (n *Namespace) CanUpdate(u *User) bool {
func (n *Namespace) CanUpdate(a web.Auth) bool {
u := getUserForRights(a)
nn, err := GetNamespaceByID(n.ID)
if err != nil {
log.Log.Error("Error occurred during CanUpdate for Namespace: %s", err)
@ -79,7 +88,9 @@ func (n *Namespace) CanUpdate(u *User) bool {
}
// CanDelete checks if the user can delete a namespace
func (n *Namespace) CanDelete(u *User) bool {
func (n *Namespace) CanDelete(a web.Auth) bool {
u := getUserForRights(a)
nn, err := GetNamespaceByID(n.ID)
if err != nil {
log.Log.Error("Error occurred during CanDelete for Namespace: %s", err)
@ -89,7 +100,7 @@ func (n *Namespace) CanDelete(u *User) bool {
}
// CanCreate checks if the user can create a new namespace
func (n *Namespace) CanCreate(u *User) bool {
func (n *Namespace) CanCreate(a web.Auth) bool {
// This is currently a dummy function, later on we could imagine global limits etc.
return true
}
@ -99,9 +110,9 @@ func (n *Namespace) checkTeamRights(u *User, r TeamRight) bool {
Table("namespaces").
Join("LEFT", "team_namespaces", "namespaces.id = team_namespaces.namespace_id").
Join("LEFT", "team_members", "team_members.team_id = team_namespaces.team_id").
Where("namespaces.id = ? "+
"AND (team_members.user_id = ? AND team_namespaces.right = ?) "+
"OR namespaces.owner_id = ? ", n.ID, u.ID, r, u.ID).
Where("namespaces.id = ? AND ("+
"(team_members.user_id = ? AND team_namespaces.right = ?) "+
"OR namespaces.owner_id = ?)", n.ID, u.ID, r, u.ID).
Get(&Namespace{})
if err != nil {
log.Log.Error("Error occurred during checkTeamRights for Namespace: %s, TeamRight: %d", err, r)
@ -116,8 +127,8 @@ func (n *Namespace) checkUserRights(u *User, r UserRight) bool {
Table("namespaces").
Join("LEFT", "users_namespace", "users_namespace.namespace_id = namespaces.id").
Where("namespaces.id = ? AND ("+
"namespaces.owner_id = ? "+
"OR (users_namespace.user_id = ? AND users_namespace.right = ?))", n.ID, u.ID, u.ID, r).
"(users_namespace.user_id = ? AND users_namespace.right = ?) "+
"OR namespaces.owner_id = ?)", n.ID, u.ID, r, u.ID).
Get(&Namespace{})
if err != nil {
log.Log.Error("Error occurred during checkUserRights for Namespace: %s, UserRight: %d", err, r)

View File

@ -16,6 +16,8 @@
package models
import "code.vikunja.io/web"
// NamespaceUser represents a namespace <-> user relation
type NamespaceUser struct {
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"namespace"`
@ -26,8 +28,8 @@ type NamespaceUser struct {
Created int64 `xorm:"created" json:"created"`
Updated int64 `xorm:"updated" json:"updated"`
CRUDable `xorm:"-" json:"-"`
Rights `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
web.Rights `xorm:"-" json:"-"`
}
// TableName is the table name for NamespaceUser

View File

@ -16,6 +16,8 @@
package models
import "code.vikunja.io/web"
// Create creates a new namespace <-> user relation
// @Summary Add a user to a namespace
// @Description Gives a user access to a namespace.
@ -31,8 +33,7 @@ package models
// @Failure 403 {object} models.HTTPError "The user does not have access to the namespace"
// @Failure 500 {object} models.Message "Internal error"
// @Router /namespaces/{id}/users [put]
func (un *NamespaceUser) Create(u *User) (err error) {
func (un *NamespaceUser) Create(a web.Auth) (err error) {
// Reset the id
un.ID = 0

View File

@ -16,6 +16,8 @@
package models
import "code.vikunja.io/web"
// ReadAll gets all users who have access to a namespace
// @Summary Get users on a namespace
// @Description Returns a namespace with all users which have access on a given namespace.
@ -30,7 +32,12 @@ package models
// @Failure 403 {object} models.HTTPError "No right to see the namespace."
// @Failure 500 {object} models.Message "Internal error"
// @Router /namespaces/{id}/users [get]
func (un *NamespaceUser) ReadAll(search string, u *User, page int) (interface{}, error) {
func (un *NamespaceUser) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
u, err := getUserWithError(a)
if err != nil {
return nil, err
}
// Check if the user has access to the namespace
l, err := GetNamespaceByID(un.NamespaceID)
if err != nil {

View File

@ -18,10 +18,13 @@ package models
import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/web"
)
// CanCreate checks if the user can create a new user <-> namespace relation
func (nu *NamespaceUser) CanCreate(doer *User) bool {
func (nu *NamespaceUser) CanCreate(a web.Auth) bool {
doer := getUserForRights(a)
// Get the namespace and check if the user has write access on it
n, err := GetNamespaceByID(nu.NamespaceID)
if err != nil {
@ -32,7 +35,9 @@ func (nu *NamespaceUser) CanCreate(doer *User) bool {
}
// CanDelete checks if the user can delete a user <-> namespace relation
func (nu *NamespaceUser) CanDelete(doer *User) bool {
func (nu *NamespaceUser) CanDelete(a web.Auth) bool {
doer := getUserForRights(a)
// Get the namespace and check if the user has write access on it
n, err := GetNamespaceByID(nu.NamespaceID)
if err != nil {
@ -43,7 +48,9 @@ func (nu *NamespaceUser) CanDelete(doer *User) bool {
}
// CanUpdate checks if the user can update a user <-> namespace relation
func (nu *NamespaceUser) CanUpdate(doer *User) bool {
func (nu *NamespaceUser) CanUpdate(a web.Auth) bool {
doer := getUserForRights(a)
// Get the namespace and check if the user has write access on it
n, err := GetNamespaceByID(nu.NamespaceID)
if err != nil {

View File

@ -1,27 +0,0 @@
// Vikunja is a todo-list application to facilitate your life.
// Copyright 2018 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package models
// Rights defines rights methods
type Rights interface {
IsAdmin(*User) bool
CanWrite(*User) bool
CanRead(*User) bool
CanDelete(*User) bool
CanUpdate(*User) bool
CanCreate(*User) bool
}

View File

@ -16,6 +16,8 @@
package models
import "code.vikunja.io/web"
// TeamList defines the relation between a team and a list
type TeamList struct {
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id"`
@ -26,8 +28,8 @@ type TeamList struct {
Created int64 `xorm:"created" json:"created"`
Updated int64 `xorm:"updated" json:"updated"`
CRUDable `xorm:"-" json:"-"`
Rights `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
web.Rights `xorm:"-" json:"-"`
}
// TableName makes beautiful table names

View File

@ -16,6 +16,8 @@
package models
import "code.vikunja.io/web"
// Create creates a new team <-> list relation
// @Summary Add a team to a list
// @Description Gives a team access to a list.
@ -31,7 +33,7 @@ package models
// @Failure 403 {object} models.HTTPError "The user does not have access to the list"
// @Failure 500 {object} models.Message "Internal error"
// @Router /lists/{id}/teams [put]
func (tl *TeamList) Create(doer *User) (err error) {
func (tl *TeamList) Create(a web.Auth) (err error) {
// Check if the rights are valid
if err = tl.Right.isValid(); err != nil {

View File

@ -16,6 +16,8 @@
package models
import "code.vikunja.io/web"
// ReadAll implements the method to read all teams of a list
// @Summary Get teams on a list
// @Description Returns a list with all teams which have access on a given list.
@ -30,7 +32,12 @@ package models
// @Failure 403 {object} models.HTTPError "No right to see the list."
// @Failure 500 {object} models.Message "Internal error"
// @Router /lists/{id}/teams [get]
func (tl *TeamList) ReadAll(search string, u *User, page int) (interface{}, error) {
func (tl *TeamList) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
u, err := getUserWithError(a)
if err != nil {
return nil, err
}
// Check if the user can read the namespace
l := &List{ID: tl.ListID}
if err := l.GetSimpleByID(); err != nil {
@ -42,7 +49,7 @@ func (tl *TeamList) ReadAll(search string, u *User, page int) (interface{}, erro
// Get the teams
all := []*TeamWithRight{}
err := x.
err = x.
Table("teams").
Join("INNER", "team_list", "team_id = teams.id").
Where("team_list.list_id = ?", tl.ListID).

View File

@ -18,10 +18,13 @@ package models
import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/web"
)
// CanCreate checks if the user can create a team <-> list relation
func (tl *TeamList) CanCreate(u *User) bool {
func (tl *TeamList) CanCreate(a web.Auth) bool {
u := getUserForRights(a)
l := List{ID: tl.ListID}
if err := l.GetSimpleByID(); err != nil {
log.Log.Error("Error occurred during CanCreate for TeamList: %s", err)
@ -31,7 +34,9 @@ func (tl *TeamList) CanCreate(u *User) bool {
}
// CanDelete checks if the user can delete a team <-> list relation
func (tl *TeamList) CanDelete(user *User) bool {
func (tl *TeamList) CanDelete(a web.Auth) bool {
user := getUserForRights(a)
l := List{ID: tl.ListID}
if err := l.GetSimpleByID(); err != nil {
log.Log.Error("Error occurred during CanDelete for TeamList: %s", err)
@ -41,7 +46,9 @@ func (tl *TeamList) CanDelete(user *User) bool {
}
// CanUpdate checks if the user can update a team <-> list relation
func (tl *TeamList) CanUpdate(user *User) bool {
func (tl *TeamList) CanUpdate(a web.Auth) bool {
user := getUserForRights(a)
l := List{ID: tl.ListID}
if err := l.GetSimpleByID(); err != nil {
log.Log.Error("Error occurred during CanUpdate for TeamList: %s", err)

View File

@ -16,6 +16,8 @@
package models
import "code.vikunja.io/web"
// Create implements the create method to assign a user to a team
// @Summary Add a user to a team
// @Description Add a user to a team.
@ -30,7 +32,8 @@ package models
// @Failure 403 {object} models.HTTPError "The user does not have access to the team"
// @Failure 500 {object} models.Message "Internal error"
// @Router /teams/{id}/members [put]
func (tm *TeamMember) Create(doer *User) (err error) {
func (tm *TeamMember) Create(a web.Auth) (err error) {
// Check if the team extst
_, err = GetTeamByID(tm.TeamID)
if err != nil {

View File

@ -18,20 +18,23 @@ package models
import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/web"
)
// CanCreate checks if the user can add a new tem member
func (tm *TeamMember) CanCreate(u *User) bool {
return tm.IsAdmin(u)
func (tm *TeamMember) CanCreate(a web.Auth) bool {
return tm.IsAdmin(a)
}
// CanDelete checks if the user can delete a new team member
func (tm *TeamMember) CanDelete(u *User) bool {
return tm.IsAdmin(u)
func (tm *TeamMember) CanDelete(a web.Auth) bool {
return tm.IsAdmin(a)
}
// IsAdmin checks if the user is team admin
func (tm *TeamMember) IsAdmin(u *User) bool {
func (tm *TeamMember) IsAdmin(a web.Auth) bool {
u := getUserForRights(a)
// A user can add a member to a team if he is admin of that team
exists, err := x.Where("user_id = ? AND team_id = ? AND admin = ?", u.ID, tm.TeamID, true).
Get(&TeamMember{})

View File

@ -16,6 +16,8 @@
package models
import "code.vikunja.io/web"
// TeamNamespace defines the relationship between a Team and a Namespace
type TeamNamespace struct {
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id"`
@ -26,8 +28,8 @@ type TeamNamespace struct {
Created int64 `xorm:"created" json:"created"`
Updated int64 `xorm:"updated" json:"updated"`
CRUDable `xorm:"-" json:"-"`
Rights `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
web.Rights `xorm:"-" json:"-"`
}
// TableName makes beautiful table names

View File

@ -16,6 +16,8 @@
package models
import "code.vikunja.io/web"
// Create creates a new team <-> namespace relation
// @Summary Add a team to a namespace
// @Description Gives a team access to a namespace.
@ -31,7 +33,7 @@ package models
// @Failure 403 {object} models.HTTPError "The team does not have access to the namespace"
// @Failure 500 {object} models.Message "Internal error"
// @Router /namespaces/{id}/teams [put]
func (tn *TeamNamespace) Create(doer *User) (err error) {
func (tn *TeamNamespace) Create(a web.Auth) (err error) {
// Check if the rights are valid
if err = tn.Right.isValid(); err != nil {

View File

@ -16,6 +16,8 @@
package models
import "code.vikunja.io/web"
// ReadAll implements the method to read all teams of a namespace
// @Summary Get teams on a namespace
// @Description Returns a namespace with all teams which have access on a given namespace.
@ -30,7 +32,12 @@ package models
// @Failure 403 {object} models.HTTPError "No right to see the namespace."
// @Failure 500 {object} models.Message "Internal error"
// @Router /namespaces/{id}/teams [get]
func (tn *TeamNamespace) ReadAll(search string, user *User, page int) (interface{}, error) {
func (tn *TeamNamespace) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
user, err := getUserWithError(a)
if err != nil {
return nil, err
}
// Check if the user can read the namespace
n, err := GetNamespaceByID(tn.NamespaceID)
if err != nil {

View File

@ -18,10 +18,13 @@ package models
import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/web"
)
// CanCreate checks if one can create a new team <-> namespace relation
func (tn *TeamNamespace) CanCreate(user *User) bool {
func (tn *TeamNamespace) CanCreate(a web.Auth) bool {
user := getUserForRights(a)
n, err := GetNamespaceByID(tn.NamespaceID)
if err != nil {
log.Log.Error("Error occurred during CanCreate for TeamNamespace: %s", err)
@ -31,7 +34,9 @@ func (tn *TeamNamespace) CanCreate(user *User) bool {
}
// CanDelete checks if a user can remove a team from a namespace. Only namespace admins can do that.
func (tn *TeamNamespace) CanDelete(user *User) bool {
func (tn *TeamNamespace) CanDelete(a web.Auth) bool {
user := getUserForRights(a)
n, err := GetNamespaceByID(tn.NamespaceID)
if err != nil {
log.Log.Error("Error occurred during CanDelete for TeamNamespace: %s", err)
@ -41,7 +46,9 @@ func (tn *TeamNamespace) CanDelete(user *User) bool {
}
// CanUpdate checks if a user can update a team from a Only namespace admins can do that.
func (tn *TeamNamespace) CanUpdate(user *User) bool {
func (tn *TeamNamespace) CanUpdate(a web.Auth) bool {
user := getUserForRights(a)
n, err := GetNamespaceByID(tn.NamespaceID)
if err != nil {
log.Log.Error("Error occurred during CanUpdate for TeamNamespace: %s", err)

View File

@ -16,6 +16,8 @@
package models
import "code.vikunja.io/web"
// Team holds a team object
type Team struct {
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"team"`
@ -29,8 +31,8 @@ type Team struct {
Created int64 `xorm:"created" json:"created"`
Updated int64 `xorm:"updated" json:"updated"`
CRUDable `xorm:"-" json:"-"`
Rights `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
web.Rights `xorm:"-" json:"-"`
}
// TableName makes beautiful table names
@ -61,8 +63,8 @@ type TeamMember struct {
Created int64 `xorm:"created" json:"created"`
Updated int64 `xorm:"updated" json:"updated"`
CRUDable `xorm:"-" json:"-"`
Rights `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
web.Rights `xorm:"-" json:"-"`
}
// TableName makes beautiful table names
@ -122,7 +124,12 @@ func (t *Team) ReadOne() (err error) {
// @Success 200 {array} models.Team "The teams."
// @Failure 500 {object} models.Message "Internal error"
// @Router /teams [get]
func (t *Team) ReadAll(search string, user *User, page int) (teams interface{}, err error) {
func (t *Team) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
user, err := getUserWithError(a)
if err != nil {
return nil, err
}
all := []*Team{}
err = x.Select("teams.*").
Table("teams").

View File

@ -16,6 +16,8 @@
package models
import "code.vikunja.io/web"
// Create is the handler to create a team
// @Summary Creates a new team
// @Description Creates a new team in a given namespace. The user needs write-access to the namespace.
@ -28,7 +30,12 @@ package models
// @Failure 400 {object} models.HTTPError "Invalid team object provided."
// @Failure 500 {object} models.Message "Internal error"
// @Router /teams [put]
func (t *Team) Create(doer *User) (err error) {
func (t *Team) Create(a web.Auth) (err error) {
doer, err := getUserWithError(a)
if err != nil {
return err
}
// Check if we have a name
if t.Name == "" {
return ErrTeamNameCannotBeEmpty{}

View File

@ -18,16 +18,18 @@ package models
import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/web"
)
// CanCreate checks if the user can create a new team
func (t *Team) CanCreate(u *User) bool {
func (t *Team) CanCreate(a web.Auth) bool {
// This is currently a dummy function, later on we could imagine global limits etc.
return true
}
// CanUpdate checks if the user can update a team
func (t *Team) CanUpdate(u *User) bool {
func (t *Team) CanUpdate(a web.Auth) bool {
u := getUserForRights(a)
// Check if the current user is in the team and has admin rights in it
exists, err := x.Where("team_id = ?", t.ID).
@ -43,12 +45,14 @@ func (t *Team) CanUpdate(u *User) bool {
}
// CanDelete checks if a user can delete a team
func (t *Team) CanDelete(u *User) bool {
return t.IsAdmin(u)
func (t *Team) CanDelete(a web.Auth) bool {
return t.IsAdmin(a)
}
// IsAdmin returns true when the user is admin of a team
func (t *Team) IsAdmin(u *User) bool {
func (t *Team) IsAdmin(a web.Auth) bool {
u := getUserForRights(a)
exists, err := x.Where("team_id = ?", t.ID).
And("user_id = ?", u.ID).
And("admin = ?", true).
@ -61,7 +65,9 @@ func (t *Team) IsAdmin(u *User) bool {
}
// CanRead returns true if the user has read access to the team
func (t *Team) CanRead(user *User) bool {
func (t *Team) CanRead(a web.Auth) bool {
user := getUserForRights(a)
// Check if the user is in the team
exists, err := x.Where("team_id = ?", t.ID).
And("user_id = ?", user.ID).

View File

@ -17,9 +17,13 @@
package models
import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/web"
"fmt"
"github.com/dgrijalva/jwt-go"
"github.com/labstack/echo"
"golang.org/x/crypto/bcrypt"
"reflect"
)
// UserLogin Object to recive user credentials in JSON format
@ -41,13 +45,34 @@ type User struct {
Created int64 `xorm:"created" json:"created"`
Updated int64 `xorm:"updated" json:"updated"`
web.Auth `xorm:"-" json:"-"`
}
// AuthDummy implements the auth of the crud handler
func (User) AuthDummy() {}
// TableName returns the table name for users
func (User) TableName() string {
return "users"
}
func getUserForRights(a web.Auth) *User {
u, err := getUserWithError(a)
if err != nil {
log.Log.Error(err.Error())
}
return u
}
func getUserWithError(a web.Auth) (*User, error) {
u, is := a.(*User)
if !is {
return &User{}, fmt.Errorf("user is not user element, is %s", reflect.TypeOf(a))
}
return u, nil
}
// APIUserPassword represents a user object without timestamps and a json password field.
type APIUserPassword struct {
ID int64 `json:"id"`
@ -119,14 +144,14 @@ func CheckUserCredentials(u *UserLogin) (User, error) {
}
// GetCurrentUser returns the current user based on its jwt token
func GetCurrentUser(c echo.Context) (user User, err error) {
func GetCurrentUser(c echo.Context) (user *User, err error) {
jwtinf := c.Get("user").(*jwt.Token)
claims := jwtinf.Claims.(jwt.MapClaims)
userID, ok := claims["id"].(float64)
if !ok {
return user, ErrCouldNotGetUserID{}
}
user = User{
user = &User{
ID: int64(userID),
Email: claims["email"].(string),
Username: claims["username"].(string),

View File

@ -19,7 +19,7 @@ package v1
import (
"code.vikunja.io/api/pkg/caldav"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/routes/crud"
"code.vikunja.io/web/handler"
"github.com/labstack/echo"
"net/http"
"time"
@ -54,7 +54,7 @@ func Caldav(c echo.Context) error {
// Get all tasks for that user
tasks, err := models.GetTasksByUser("", &u, -1)
if err != nil {
return crud.HandleHTTPError(err)
return handler.HandleHTTPError(err, c)
}
hour := int64(time.Hour.Seconds())

View File

@ -81,7 +81,7 @@ func getNamespace(c echo.Context) (namespace models.Namespace, err error) {
if err != nil {
return
}
if !namespace.CanRead(&user) {
if !namespace.CanRead(user) {
return
}

View File

@ -18,7 +18,7 @@ package v1
import (
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/routes/crud"
"code.vikunja.io/web/handler"
"crypto/md5"
"encoding/hex"
"github.com/dgrijalva/jwt-go"
@ -53,7 +53,7 @@ func Login(c echo.Context) error {
// Check user
user, err := models.CheckUserCredentials(&u)
if err != nil {
return crud.HandleHTTPError(err)
return handler.HandleHTTPError(err, c)
}
// Create token

View File

@ -18,7 +18,7 @@ package v1
import (
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/routes/crud"
"code.vikunja.io/web/handler"
"github.com/labstack/echo"
"net/http"
)
@ -44,7 +44,7 @@ func RegisterUser(c echo.Context) error {
// Insert the user
newUser, err := models.CreateUser(datUser.APIFormat())
if err != nil {
return crud.HandleHTTPError(err)
return handler.HandleHTTPError(err, c)
}
return c.JSON(http.StatusOK, newUser)

View File

@ -18,7 +18,7 @@ package v1
import (
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/routes/crud"
"code.vikunja.io/web/handler"
"github.com/labstack/echo"
"net/http"
)
@ -43,7 +43,7 @@ func UserConfirmEmail(c echo.Context) error {
err := models.UserEmailConfirm(&emailConfirm)
if err != nil {
return crud.HandleHTTPError(err)
return handler.HandleHTTPError(err, c)
}
return c.JSON(http.StatusOK, models.Message{"The email was confirmed successfully."})

View File

@ -18,7 +18,7 @@ package v1
import (
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/routes/crud"
"code.vikunja.io/web/handler"
"github.com/labstack/echo"
"net/http"
"strconv"
@ -55,10 +55,10 @@ func UserDelete(c echo.Context) error {
}
// Delete it
err = models.DeleteUserByID(userID, &doer)
err = models.DeleteUserByID(userID, doer)
if err != nil {
return crud.HandleHTTPError(err)
return handler.HandleHTTPError(err, c)
}
return c.JSON(http.StatusOK, models.Message{"success"})

View File

@ -18,7 +18,7 @@ package v1
import (
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/routes/crud"
"code.vikunja.io/web/handler"
"github.com/labstack/echo"
"net/http"
)
@ -39,7 +39,7 @@ func UserList(c echo.Context) error {
s := c.QueryParam("s")
users, err := models.ListUsers(s)
if err != nil {
return crud.HandleHTTPError(err)
return handler.HandleHTTPError(err, c)
}
// Obfuscate the mailadresses

View File

@ -18,7 +18,7 @@ package v1
import (
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/routes/crud"
"code.vikunja.io/web/handler"
"github.com/labstack/echo"
"net/http"
)
@ -43,7 +43,7 @@ func UserResetPassword(c echo.Context) error {
err := models.UserPasswordReset(&pwReset)
if err != nil {
return crud.HandleHTTPError(err)
return handler.HandleHTTPError(err, c)
}
return c.JSON(http.StatusOK, models.Message{"The password was updated successfully."})
@ -69,7 +69,7 @@ func UserRequestResetPasswordToken(c echo.Context) error {
err := models.RequestUserPasswordResetToken(&pwTokenReset)
if err != nil {
return crud.HandleHTTPError(err)
return handler.HandleHTTPError(err, c)
}
return c.JSON(http.StatusOK, models.Message{"Token was sent."})

View File

@ -18,7 +18,7 @@ package v1
import (
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/routes/crud"
"code.vikunja.io/web/handler"
"github.com/labstack/echo"
"net/http"
)
@ -42,7 +42,7 @@ func UserShow(c echo.Context) error {
user, err := models.GetUserByID(userInfos.ID)
if err != nil {
return crud.HandleHTTPError(err)
return handler.HandleHTTPError(err, c)
}
return c.JSON(http.StatusOK, user)

View File

@ -18,7 +18,7 @@ package v1
import (
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/routes/crud"
"code.vikunja.io/web/handler"
"github.com/labstack/echo"
"net/http"
)
@ -57,12 +57,12 @@ func UserChangePassword(c echo.Context) error {
// Check the current password
if _, err = models.CheckUserCredentials(&models.UserLogin{Username: doer.Username, Password: newPW.OldPassword}); err != nil {
return crud.HandleHTTPError(err)
return handler.HandleHTTPError(err, c)
}
// Update the password
if err = models.UpdateUserPassword(&doer, newPW.NewPassword); err != nil {
return crud.HandleHTTPError(err)
if err = models.UpdateUserPassword(doer, newPW.NewPassword); err != nil {
return handler.HandleHTTPError(err, c)
}
return c.JSON(http.StatusOK, models.Message{"The password was updated successfully."})

View File

@ -1,60 +0,0 @@
// Vikunja is a todo-list application to facilitate your life.
// Copyright 2018 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package crud
import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models"
"github.com/labstack/echo"
"net/http"
)
// CreateWeb is the handler to create an object
func (c *WebHandler) CreateWeb(ctx echo.Context) error {
// Get our model
currentStruct := c.EmptyStruct()
// Get the object & bind params to struct
if err := ParamBinder(currentStruct, ctx); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "No or invalid model provided.")
}
// Validate the struct
if err := ctx.Validate(currentStruct); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err)
}
// Get the user to pass for later checks
currentUser, err := models.GetCurrentUser(ctx)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Could not determine the current user.")
}
// Check rights
if !currentStruct.CanCreate(&currentUser) {
log.Log.Noticef("%s [ID: %d] tried to create while not having the rights for it", currentUser.Username, currentUser.ID)
return echo.NewHTTPError(http.StatusForbidden)
}
// Create
err = currentStruct.Create(&currentUser)
if err != nil {
return HandleHTTPError(err)
}
return ctx.JSON(http.StatusCreated, currentStruct)
}

View File

@ -1,53 +0,0 @@
// Vikunja is a todo-list application to facilitate your life.
// Copyright 2018 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package crud
import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models"
"github.com/labstack/echo"
"net/http"
)
// DeleteWeb is the web handler to delete something
func (c *WebHandler) DeleteWeb(ctx echo.Context) error {
// Get our model
currentStruct := c.EmptyStruct()
// Bind params to struct
if err := ParamBinder(currentStruct, ctx); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid URL param.")
}
// Check if the user has the right to delete
currentUser, err := models.GetCurrentUser(ctx)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError)
}
if !currentStruct.CanDelete(&currentUser) {
log.Log.Noticef("%s [ID: %d] tried to delete while not having the rights for it", currentUser.Username, currentUser.ID)
return echo.NewHTTPError(http.StatusForbidden)
}
err = currentStruct.Delete()
if err != nil {
return HandleHTTPError(err)
}
return ctx.JSON(http.StatusOK, models.Message{"Successfully deleted."})
}

View File

@ -1,46 +0,0 @@
// Vikunja is a todo-list application to facilitate your life.
// Copyright 2018 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package crud
import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models"
"github.com/labstack/echo"
"net/http"
)
// WebHandler defines the webhandler object
// This does web stuff, aka returns json etc. Uses CRUDable Methods to get the data
type WebHandler struct {
EmptyStruct func() CObject
}
// CObject is the definition of our object, holds the structs
type CObject interface {
models.CRUDable
models.Rights
}
// HandleHTTPError does what it says
func HandleHTTPError(err error) *echo.HTTPError {
if a, has := err.(models.HTTPErrorProcessor); has {
errDetails := a.HTTPError()
return echo.NewHTTPError(errDetails.HTTPCode, errDetails)
}
log.Log.Error(err.Error())
return echo.NewHTTPError(http.StatusInternalServerError)
}

View File

@ -1,288 +0,0 @@
// Vikunja is a todo-list application to facilitate your life.
// Copyright 2018 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package crud
import (
"errors"
"github.com/labstack/echo"
"reflect"
"strconv"
"strings"
)
const paramTagName = "param"
// ParamBinder binds parameters to a struct.
// Currently a working implementation, waiting to implement this officially into echo.
func ParamBinder(i interface{}, c echo.Context) (err error) {
// Default binder
db := new(echo.DefaultBinder)
if err = db.Bind(i, c); err != nil {
return
}
paramNames := c.ParamNames()
paramValues := c.ParamValues()
paramVars := make(map[string][]string)
for in, name := range paramNames {
// Hotfix for an echo bug where a param name would show up which dont exist
names := strings.Split(name, ",")
for _, n := range names {
paramVars[n] = append(paramVars[name], paramValues[in])
}
}
b := Binder{}
err = b.bindData(i, paramVars, paramTagName)
/*
// Our custom magic starts here
paramNames := c.ParamNames()
paramValues := c.ParamValues()
v := reflect.ValueOf(i)
t := reflect.TypeOf(i)
s := reflect.ValueOf(i).Elem()
for i := 0; i < v.NumField(); i++ {
field := t.Field(i)
f := s.Field(i)
// Check if it has a param tag
tag := field.Tag.Get(paramTagName)
if tag != "" {
// If it has one, range over all url parameters to see if we have a match
for in, name := range paramNames {
// Found match
if tag == name {
// Put the value of that match in our sruct
switch field.Type.Name() {
case "int64": // SetInt only accepts int64, so the struct field can only have int64 of int (no int32/16/int...)
intParam, err := strconv.ParseInt(paramValues[in], 10, 64)
f.SetInt(intParam)
if err != nil {
return err
}
case "string":
f.SetString(paramValues[in])
}
}
}
}
//f.SetString("blub")
}*/
return
}
// Binder represents a binder
type Binder struct{}
func (b *Binder) bindData(ptr interface{}, data map[string][]string, tag string) error {
typ := reflect.TypeOf(ptr).Elem()
val := reflect.ValueOf(ptr).Elem()
if typ.Kind() != reflect.Struct {
return errors.New("Binding element must be a struct")
}
for i := 0; i < typ.NumField(); i++ {
typeField := typ.Field(i)
structField := val.Field(i)
if !structField.CanSet() {
continue
}
structFieldKind := structField.Kind()
inputFieldName := typeField.Tag.Get(tag)
if inputFieldName == "" {
inputFieldName = typeField.Name
// If tag is nil, we inspect if the field is a struct.
if _, ok := bindUnmarshaler(structField); !ok && structFieldKind == reflect.Struct {
err := b.bindData(structField.Addr().Interface(), data, tag)
if err != nil {
return err
}
continue
}
}
inputValue, exists := data[inputFieldName]
if !exists {
continue
}
// Call this first, in case we're dealing with an alias to an array type
if ok, err := unmarshalField(typeField.Type.Kind(), inputValue[0], structField); ok {
if err != nil {
return err
}
continue
}
numElems := len(inputValue)
if structFieldKind == reflect.Slice && numElems > 0 {
sliceOf := structField.Type().Elem().Kind()
slice := reflect.MakeSlice(structField.Type(), numElems, numElems)
for j := 0; j < numElems; j++ {
if err := setWithProperType(sliceOf, inputValue[j], slice.Index(j)); err != nil {
return err
}
}
val.Field(i).Set(slice)
} else {
if err := setWithProperType(typeField.Type.Kind(), inputValue[0], structField); err != nil {
return err
}
}
}
return nil
}
func setWithProperType(valueKind reflect.Kind, val string, structField reflect.Value) error {
// But also call it here, in case we're dealing with an array of BindUnmarshalers
if ok, err := unmarshalField(valueKind, val, structField); ok {
return err
}
switch valueKind {
case reflect.Int:
return setIntField(val, 0, structField)
case reflect.Int8:
return setIntField(val, 8, structField)
case reflect.Int16:
return setIntField(val, 16, structField)
case reflect.Int32:
return setIntField(val, 32, structField)
case reflect.Int64:
return setIntField(val, 64, structField)
case reflect.Uint:
return setUintField(val, 0, structField)
case reflect.Uint8:
return setUintField(val, 8, structField)
case reflect.Uint16:
return setUintField(val, 16, structField)
case reflect.Uint32:
return setUintField(val, 32, structField)
case reflect.Uint64:
return setUintField(val, 64, structField)
case reflect.Bool:
return setBoolField(val, structField)
case reflect.Float32:
return setFloatField(val, 32, structField)
case reflect.Float64:
return setFloatField(val, 64, structField)
case reflect.String:
structField.SetString(val)
default:
return errors.New("unknown type")
}
return nil
}
func setIntField(value string, bitSize int, field reflect.Value) error {
if value == "" {
value = "0"
}
intVal, err := strconv.ParseInt(value, 10, bitSize)
if err == nil {
field.SetInt(intVal)
}
return err
}
func setUintField(value string, bitSize int, field reflect.Value) error {
if value == "" {
value = "0"
}
uintVal, err := strconv.ParseUint(value, 10, bitSize)
if err == nil {
field.SetUint(uintVal)
}
return err
}
func setBoolField(value string, field reflect.Value) error {
if value == "" {
value = "false"
}
boolVal, err := strconv.ParseBool(value)
if err == nil {
field.SetBool(boolVal)
}
return err
}
func setFloatField(value string, bitSize int, field reflect.Value) error {
if value == "" {
value = "0.0"
}
floatVal, err := strconv.ParseFloat(value, bitSize)
if err == nil {
field.SetFloat(floatVal)
}
return err
}
// BindUnmarshaler type
type BindUnmarshaler interface {
// UnmarshalParam decodes and assigns a value from an form or query param.
UnmarshalParam(param string) error
}
// bindUnmarshaler attempts to unmarshal a reflect.Value into a BindUnmarshaler
func bindUnmarshaler(field reflect.Value) (BindUnmarshaler, bool) {
ptr := reflect.New(field.Type())
if ptr.CanInterface() {
iface := ptr.Interface()
if unmarshaler, ok := iface.(BindUnmarshaler); ok {
return unmarshaler, ok
}
}
return nil, false
}
func unmarshalField(valueKind reflect.Kind, val string, field reflect.Value) (bool, error) {
switch valueKind {
case reflect.Ptr:
return unmarshalFieldPtr(val, field)
default:
return unmarshalFieldNonPtr(val, field)
}
}
func unmarshalFieldNonPtr(value string, field reflect.Value) (bool, error) {
if unmarshaler, ok := bindUnmarshaler(field); ok {
err := unmarshaler.UnmarshalParam(value)
field.Set(reflect.ValueOf(unmarshaler).Elem())
return true, err
}
return false, nil
}
func unmarshalFieldPtr(value string, field reflect.Value) (bool, error) {
if field.IsNil() {
// Initialize the pointer to a nil value
field.Set(reflect.New(field.Type().Elem()))
}
return unmarshalFieldNonPtr(value, field.Elem())
}

View File

@ -1,65 +0,0 @@
// Vikunja is a todo-list application to facilitate your life.
// Copyright 2018 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package crud
import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models"
"github.com/labstack/echo"
"net/http"
"strconv"
)
// ReadAllWeb is the webhandler to get all objects of a type
func (c *WebHandler) ReadAllWeb(ctx echo.Context) error {
// Get our model
currentStruct := c.EmptyStruct()
currentUser, err := models.GetCurrentUser(ctx)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Could not determine the current user.")
}
// Get the object & bind params to struct
if err := ParamBinder(currentStruct, ctx); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "No or invalid model provided.")
}
// Pagination
page := ctx.QueryParam("page")
if page == "" {
page = "1"
}
pageNumber, err := strconv.Atoi(page)
if err != nil {
log.Log.Error(err.Error())
return echo.NewHTTPError(http.StatusBadRequest, "Bad page requested.")
}
if pageNumber < 0 {
return echo.NewHTTPError(http.StatusBadRequest, "Bad page requested.")
}
// Search
search := ctx.QueryParam("s")
lists, err := currentStruct.ReadAll(search, &currentUser, pageNumber)
if err != nil {
return HandleHTTPError(err)
}
return ctx.JSON(http.StatusOK, lists)
}

View File

@ -1,54 +0,0 @@
// Vikunja is a todo-list application to facilitate your life.
// Copyright 2018 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package crud
import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models"
"github.com/labstack/echo"
"net/http"
)
// ReadOneWeb is the webhandler to get one object
func (c *WebHandler) ReadOneWeb(ctx echo.Context) error {
// Get our model
currentStruct := c.EmptyStruct()
// Get the object & bind params to struct
if err := ParamBinder(currentStruct, ctx); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "No or invalid model provided.")
}
// Get our object
err := currentStruct.ReadOne()
if err != nil {
return HandleHTTPError(err)
}
// Check rights
// We can only check the rights on a full object, which is why we need to check it afterwards
currentUser, err := models.GetCurrentUser(ctx)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Could not determine the current user.")
}
if !currentStruct.CanRead(&currentUser) {
log.Log.Noticef("%s [ID: %d] tried to read while not having the rights for it", currentUser.Username, currentUser.ID)
return echo.NewHTTPError(http.StatusForbidden, "You don't have the right to see this")
}
return ctx.JSON(http.StatusOK, currentStruct)
}

View File

@ -1,59 +0,0 @@
// Vikunja is a todo-list application to facilitate your life.
// Copyright 2018 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package crud
import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models"
"github.com/labstack/echo"
"net/http"
)
// UpdateWeb is the webhandler to update an object
func (c *WebHandler) UpdateWeb(ctx echo.Context) error {
// Get our model
currentStruct := c.EmptyStruct()
// Get the object & bind params to struct
if err := ParamBinder(currentStruct, ctx); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "No or invalid model provided.")
}
// Validate the struct
if err := ctx.Validate(currentStruct); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err)
}
// Check if the user has the right to do that
currentUser, err := models.GetCurrentUser(ctx)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Could not determine the current user.")
}
if !currentStruct.CanUpdate(&currentUser) {
log.Log.Noticef("%s [ID: %d] tried to update while not having the rights for it", currentUser.Username, currentUser.ID)
return echo.NewHTTPError(http.StatusForbidden)
}
// Do the update
err = currentStruct.Update()
if err != nil {
return HandleHTTPError(err)
}
return ctx.JSON(http.StatusOK, currentStruct)
}

View File

@ -44,9 +44,11 @@ package routes
import (
_ "code.vikunja.io/api/docs" // To generate swagger docs
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models"
apiv1 "code.vikunja.io/api/pkg/routes/api/v1"
"code.vikunja.io/api/pkg/routes/crud"
"code.vikunja.io/web"
"code.vikunja.io/web/handler"
"github.com/asaskevich/govalidator"
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
@ -67,7 +69,7 @@ func (cv *CustomValidator) Validate(i interface{}) error {
}
httperr := models.ValidationHTTPError{
models.HTTPError{
web.HTTPError{
Code: models.ErrCodeInvalidData,
Message: "Invalid Data",
},
@ -122,6 +124,20 @@ func RegisterRoutes(e *echo.Echo) {
// ===== Routes with Authetification =====
// Authetification
a.Use(middleware.JWT([]byte(viper.GetString("service.JWTSecret"))))
// Put the authprovider in the context to be able to use it later
e.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
c.Set("AuthProvider", &web.Auths{
AuthObject: func(echo.Context) (web.Auth, error) {
return models.GetCurrentUser(c)
},
})
c.Set("LoggingProvider", &log.Log)
return next(c)
}
})
a.POST("/tokenTest", apiv1.CheckToken)
// User stuff
@ -129,8 +145,8 @@ func RegisterRoutes(e *echo.Echo) {
a.POST("/user/password", apiv1.UserChangePassword)
a.GET("/users", apiv1.UserList)
listHandler := &crud.WebHandler{
EmptyStruct: func() crud.CObject {
listHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
return &models.List{}
},
}
@ -140,8 +156,8 @@ func RegisterRoutes(e *echo.Echo) {
a.DELETE("/lists/:list", listHandler.DeleteWeb)
a.PUT("/namespaces/:namespace/lists", listHandler.CreateWeb)
taskHandler := &crud.WebHandler{
EmptyStruct: func() crud.CObject {
taskHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
return &models.ListTask{}
},
}
@ -150,8 +166,8 @@ func RegisterRoutes(e *echo.Echo) {
a.DELETE("/tasks/:listtask", taskHandler.DeleteWeb)
a.POST("/tasks/:listtask", taskHandler.UpdateWeb)
listTeamHandler := &crud.WebHandler{
EmptyStruct: func() crud.CObject {
listTeamHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
return &models.TeamList{}
},
}
@ -160,8 +176,8 @@ func RegisterRoutes(e *echo.Echo) {
a.DELETE("/lists/:list/teams/:team", listTeamHandler.DeleteWeb)
a.POST("/lists/:list/teams/:team", listTeamHandler.UpdateWeb)
listUserHandler := &crud.WebHandler{
EmptyStruct: func() crud.CObject {
listUserHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
return &models.ListUser{}
},
}
@ -170,8 +186,8 @@ func RegisterRoutes(e *echo.Echo) {
a.DELETE("/lists/:list/users/:user", listUserHandler.DeleteWeb)
a.POST("/lists/:list/users/:user", listUserHandler.UpdateWeb)
namespaceHandler := &crud.WebHandler{
EmptyStruct: func() crud.CObject {
namespaceHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
return &models.Namespace{}
},
}
@ -182,8 +198,8 @@ func RegisterRoutes(e *echo.Echo) {
a.DELETE("/namespaces/:namespace", namespaceHandler.DeleteWeb)
a.GET("/namespaces/:namespace/lists", apiv1.GetListsByNamespaceID)
namespaceTeamHandler := &crud.WebHandler{
EmptyStruct: func() crud.CObject {
namespaceTeamHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
return &models.TeamNamespace{}
},
}
@ -192,8 +208,8 @@ func RegisterRoutes(e *echo.Echo) {
a.DELETE("/namespaces/:namespace/teams/:team", namespaceTeamHandler.DeleteWeb)
a.POST("/namespaces/:namespace/teams/:team", namespaceTeamHandler.UpdateWeb)
namespaceUserHandler := &crud.WebHandler{
EmptyStruct: func() crud.CObject {
namespaceUserHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
return &models.NamespaceUser{}
},
}
@ -202,8 +218,8 @@ func RegisterRoutes(e *echo.Echo) {
a.DELETE("/namespaces/:namespace/users/:user", namespaceUserHandler.DeleteWeb)
a.POST("/namespaces/:namespace/users/:user", namespaceUserHandler.UpdateWeb)
teamHandler := &crud.WebHandler{
EmptyStruct: func() crud.CObject {
teamHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
return &models.Team{}
},
}
@ -213,8 +229,8 @@ func RegisterRoutes(e *echo.Echo) {
a.POST("/teams/:team", teamHandler.UpdateWeb)
a.DELETE("/teams/:team", teamHandler.DeleteWeb)
teamMemberHandler := &crud.WebHandler{
EmptyStruct: func() crud.CObject {
teamMemberHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
return &models.TeamMember{}
},
}