Use the username instead of a user when adding a user to a team or giving it rights (#76)
This commit is contained in:
parent
3fffcbd986
commit
50d1f29125
19 changed files with 121 additions and 85 deletions
|
@ -215,9 +215,8 @@ Sorry for some of them being in German, I'll tranlate them at some point.
|
||||||
* [x] Fix lint errors
|
* [x] Fix lint errors
|
||||||
* [x] Add settings for max open/idle connections and max connection lifetime
|
* [x] Add settings for max open/idle connections and max connection lifetime
|
||||||
* [x] Reminders should use an extra table so we can make reverse lookups aka "give me all tasks with reminders in this period" which we'll need for things like email reminders notifications
|
* [x] Reminders should use an extra table so we can make reverse lookups aka "give me all tasks with reminders in this period" which we'll need for things like email reminders notifications
|
||||||
* [ ] Teams and users should also have uuids (for users these can be the username)
|
* [x] When giving a user access to a list/namespace, they should be reffered to by uuid, not numeric id
|
||||||
* [ ] When giving a team or user access to a list/namespace, they should be reffered to by uuid, not numeric id
|
* [x] Adding users to a team should also use uuid
|
||||||
* [ ] Adding users to a team should also use uuid
|
|
||||||
* [ ] Check if the team/user really exist before updating them on lists/namespaces
|
* [ ] Check if the team/user really exist before updating them on lists/namespaces
|
||||||
* [ ] Check if the email is properly obfuscated everywhere -> alter GetUser() and add a new method GetUserWithEmail
|
* [ ] Check if the email is properly obfuscated everywhere -> alter GetUser() and add a new method GetUserWithEmail
|
||||||
|
|
||||||
|
|
|
@ -22,8 +22,10 @@ import "code.vikunja.io/web"
|
||||||
type ListUser struct {
|
type ListUser struct {
|
||||||
// The unique, numeric id of this list <-> user relation.
|
// The unique, numeric id of this list <-> user relation.
|
||||||
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"namespace"`
|
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"namespace"`
|
||||||
// The user id.
|
// The username.
|
||||||
UserID int64 `xorm:"int(11) not null INDEX" json:"userID" param:"user"`
|
Username string `xorm:"-" json:"username" param:"user"`
|
||||||
|
// Used internally to reference the user
|
||||||
|
UserID int64 `xorm:"int(11) not null INDEX" json:"-"`
|
||||||
// The list id.
|
// The list id.
|
||||||
ListID int64 `xorm:"int(11) not null INDEX" json:"-" param:"list"`
|
ListID int64 `xorm:"int(11) not null INDEX" json:"-" param:"list"`
|
||||||
// The right this user has. 0 = Read only, 1 = Read & Write, 2 = Admin. See the docs for more details.
|
// The right this user has. 0 = Read only, 1 = Read & Write, 2 = Admin. See the docs for more details.
|
||||||
|
|
|
@ -47,9 +47,11 @@ func (lu *ListUser) Create(a web.Auth) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the user exists
|
// Check if the user exists
|
||||||
if _, err = GetUserByID(lu.UserID); err != nil {
|
user, err := GetUserByUsername(lu.Username)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
lu.UserID = user.ID
|
||||||
|
|
||||||
// Check if the user already has access or is owner of that list
|
// Check if the user already has access or is owner of that list
|
||||||
// We explicitly DONT check for teams here
|
// We explicitly DONT check for teams here
|
||||||
|
|
|
@ -34,10 +34,11 @@ import _ "code.vikunja.io/web" // For swaggerdocs generation
|
||||||
func (lu *ListUser) Delete() (err error) {
|
func (lu *ListUser) Delete() (err error) {
|
||||||
|
|
||||||
// Check if the user exists
|
// Check if the user exists
|
||||||
_, err = GetUserByID(lu.UserID)
|
user, err := GetUserByUsername(lu.Username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
lu.UserID = user.ID
|
||||||
|
|
||||||
// Check if the user has access to the list
|
// Check if the user has access to the list
|
||||||
has, err := x.Where("user_id = ? AND list_id = ?", lu.UserID, lu.ListID).
|
has, err := x.Where("user_id = ? AND list_id = ?", lu.UserID, lu.ListID).
|
||||||
|
|
|
@ -29,6 +29,7 @@ func TestListUser_Create(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
ID int64
|
ID int64
|
||||||
UserID int64
|
UserID int64
|
||||||
|
Username string
|
||||||
ListID int64
|
ListID int64
|
||||||
Right Right
|
Right Right
|
||||||
Created int64
|
Created int64
|
||||||
|
@ -49,15 +50,15 @@ func TestListUser_Create(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "ListUsers Create normally",
|
name: "ListUsers Create normally",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
UserID: 1,
|
Username: "user1",
|
||||||
ListID: 2,
|
ListID: 2,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ListUsers Create for duplicate",
|
name: "ListUsers Create for duplicate",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
UserID: 1,
|
Username: "user1",
|
||||||
ListID: 2,
|
ListID: 2,
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
errType: IsErrUserAlreadyHasAccess,
|
errType: IsErrUserAlreadyHasAccess,
|
||||||
|
@ -65,9 +66,9 @@ func TestListUser_Create(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "ListUsers Create with invalid right",
|
name: "ListUsers Create with invalid right",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
UserID: 1,
|
Username: "user1",
|
||||||
ListID: 2,
|
ListID: 2,
|
||||||
Right: 500,
|
Right: 500,
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
errType: IsErrInvalidRight,
|
errType: IsErrInvalidRight,
|
||||||
|
@ -75,8 +76,8 @@ func TestListUser_Create(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "ListUsers Create with inexisting list",
|
name: "ListUsers Create with inexisting list",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
UserID: 1,
|
Username: "user1",
|
||||||
ListID: 2000,
|
ListID: 2000,
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
errType: IsErrListDoesNotExist,
|
errType: IsErrListDoesNotExist,
|
||||||
|
@ -84,8 +85,8 @@ func TestListUser_Create(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "ListUsers Create with inexisting user",
|
name: "ListUsers Create with inexisting user",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
UserID: 500,
|
Username: "user500",
|
||||||
ListID: 2,
|
ListID: 2,
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
errType: IsErrUserDoesNotExist,
|
errType: IsErrUserDoesNotExist,
|
||||||
|
@ -93,8 +94,8 @@ func TestListUser_Create(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "ListUsers Create with the owner as shared user",
|
name: "ListUsers Create with the owner as shared user",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
UserID: 1,
|
Username: "user1",
|
||||||
ListID: 1,
|
ListID: 1,
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
errType: IsErrUserAlreadyHasAccess,
|
errType: IsErrUserAlreadyHasAccess,
|
||||||
|
@ -105,6 +106,7 @@ func TestListUser_Create(t *testing.T) {
|
||||||
ul := &ListUser{
|
ul := &ListUser{
|
||||||
ID: tt.fields.ID,
|
ID: tt.fields.ID,
|
||||||
UserID: tt.fields.UserID,
|
UserID: tt.fields.UserID,
|
||||||
|
Username: tt.fields.Username,
|
||||||
ListID: tt.fields.ListID,
|
ListID: tt.fields.ListID,
|
||||||
Right: tt.fields.Right,
|
Right: tt.fields.Right,
|
||||||
Created: tt.fields.Created,
|
Created: tt.fields.Created,
|
||||||
|
@ -291,7 +293,7 @@ func TestListUser_Update(t *testing.T) {
|
||||||
func TestListUser_Delete(t *testing.T) {
|
func TestListUser_Delete(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
ID int64
|
ID int64
|
||||||
UserID int64
|
Username string
|
||||||
ListID int64
|
ListID int64
|
||||||
Right Right
|
Right Right
|
||||||
Created int64
|
Created int64
|
||||||
|
@ -308,8 +310,8 @@ func TestListUser_Delete(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "Try deleting some unexistant user",
|
name: "Try deleting some unexistant user",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
UserID: 1000,
|
Username: "user1000",
|
||||||
ListID: 2,
|
ListID: 2,
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
errType: IsErrUserDoesNotExist,
|
errType: IsErrUserDoesNotExist,
|
||||||
|
@ -317,8 +319,8 @@ func TestListUser_Delete(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "Try deleting a user which does not has access but exists",
|
name: "Try deleting a user which does not has access but exists",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
UserID: 1,
|
Username: "user1",
|
||||||
ListID: 4,
|
ListID: 4,
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
errType: IsErrUserDoesNotHaveAccessToList,
|
errType: IsErrUserDoesNotHaveAccessToList,
|
||||||
|
@ -326,8 +328,8 @@ func TestListUser_Delete(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "Try deleting normally",
|
name: "Try deleting normally",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
UserID: 1,
|
Username: "user1",
|
||||||
ListID: 3,
|
ListID: 3,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -335,7 +337,7 @@ func TestListUser_Delete(t *testing.T) {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
lu := &ListUser{
|
lu := &ListUser{
|
||||||
ID: tt.fields.ID,
|
ID: tt.fields.ID,
|
||||||
UserID: tt.fields.UserID,
|
Username: tt.fields.Username,
|
||||||
ListID: tt.fields.ListID,
|
ListID: tt.fields.ListID,
|
||||||
Right: tt.fields.Right,
|
Right: tt.fields.Right,
|
||||||
Created: tt.fields.Created,
|
Created: tt.fields.Created,
|
||||||
|
|
|
@ -22,8 +22,9 @@ import "code.vikunja.io/web"
|
||||||
type NamespaceUser struct {
|
type NamespaceUser struct {
|
||||||
// The unique, numeric id of this namespace <-> user relation.
|
// The unique, numeric id of this namespace <-> user relation.
|
||||||
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"namespace"`
|
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"namespace"`
|
||||||
// The user id.
|
// The username.
|
||||||
UserID int64 `xorm:"int(11) not null INDEX" json:"userID" param:"user"`
|
Username string `xorm:"-" json:"userID" param:"user"`
|
||||||
|
UserID int64 `xorm:"int(11) not null INDEX" json:"-"`
|
||||||
// The namespace id
|
// The namespace id
|
||||||
NamespaceID int64 `xorm:"int(11) not null INDEX" json:"-" param:"namespace"`
|
NamespaceID int64 `xorm:"int(11) not null INDEX" json:"-" param:"namespace"`
|
||||||
// The right this user has. 0 = Read only, 1 = Read & Write, 2 = Admin. See the docs for more details.
|
// The right this user has. 0 = Read only, 1 = Read & Write, 2 = Admin. See the docs for more details.
|
||||||
|
|
|
@ -49,9 +49,11 @@ func (nu *NamespaceUser) Create(a web.Auth) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the user exists
|
// Check if the user exists
|
||||||
if _, err = GetUserByID(nu.UserID); err != nil {
|
user, err := GetUserByUsername(nu.Username)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
nu.UserID = user.ID
|
||||||
|
|
||||||
// Check if the user already has access or is owner of that namespace
|
// Check if the user already has access or is owner of that namespace
|
||||||
// We explicitly DO NOT check for teams here
|
// We explicitly DO NOT check for teams here
|
||||||
|
|
|
@ -34,10 +34,11 @@ import _ "code.vikunja.io/web" // For swaggerdocs generation
|
||||||
func (nu *NamespaceUser) Delete() (err error) {
|
func (nu *NamespaceUser) Delete() (err error) {
|
||||||
|
|
||||||
// Check if the user exists
|
// Check if the user exists
|
||||||
_, err = GetUserByID(nu.UserID)
|
user, err := GetUserByUsername(nu.Username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
nu.UserID = user.ID
|
||||||
|
|
||||||
// Check if the user has access to the namespace
|
// Check if the user has access to the namespace
|
||||||
has, err := x.Where("user_id = ? AND namespace_id = ?", nu.UserID, nu.NamespaceID).
|
has, err := x.Where("user_id = ? AND namespace_id = ?", nu.UserID, nu.NamespaceID).
|
||||||
|
|
|
@ -29,7 +29,7 @@ import (
|
||||||
func TestNamespaceUser_Create(t *testing.T) {
|
func TestNamespaceUser_Create(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
ID int64
|
ID int64
|
||||||
UserID int64
|
Username string
|
||||||
NamespaceID int64
|
NamespaceID int64
|
||||||
Right Right
|
Right Right
|
||||||
Created int64
|
Created int64
|
||||||
|
@ -50,14 +50,14 @@ func TestNamespaceUser_Create(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "NamespaceUsers Create normally",
|
name: "NamespaceUsers Create normally",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
UserID: 1,
|
Username: "user1",
|
||||||
NamespaceID: 2,
|
NamespaceID: 2,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "NamespaceUsers Create for duplicate",
|
name: "NamespaceUsers Create for duplicate",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
UserID: 1,
|
Username: "user1",
|
||||||
NamespaceID: 2,
|
NamespaceID: 2,
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
|
@ -66,7 +66,7 @@ func TestNamespaceUser_Create(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "NamespaceUsers Create with invalid right",
|
name: "NamespaceUsers Create with invalid right",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
UserID: 1,
|
Username: "user1",
|
||||||
NamespaceID: 2,
|
NamespaceID: 2,
|
||||||
Right: 500,
|
Right: 500,
|
||||||
},
|
},
|
||||||
|
@ -76,7 +76,7 @@ func TestNamespaceUser_Create(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "NamespaceUsers Create with inexisting list",
|
name: "NamespaceUsers Create with inexisting list",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
UserID: 1,
|
Username: "user1",
|
||||||
NamespaceID: 2000,
|
NamespaceID: 2000,
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
|
@ -85,7 +85,7 @@ func TestNamespaceUser_Create(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "NamespaceUsers Create with inexisting user",
|
name: "NamespaceUsers Create with inexisting user",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
UserID: 500,
|
Username: "user500",
|
||||||
NamespaceID: 2,
|
NamespaceID: 2,
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
|
@ -94,7 +94,7 @@ func TestNamespaceUser_Create(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "NamespaceUsers Create with the owner as shared user",
|
name: "NamespaceUsers Create with the owner as shared user",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
UserID: 1,
|
Username: "user1",
|
||||||
NamespaceID: 1,
|
NamespaceID: 1,
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
|
@ -105,7 +105,7 @@ func TestNamespaceUser_Create(t *testing.T) {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
un := &NamespaceUser{
|
un := &NamespaceUser{
|
||||||
ID: tt.fields.ID,
|
ID: tt.fields.ID,
|
||||||
UserID: tt.fields.UserID,
|
Username: tt.fields.Username,
|
||||||
NamespaceID: tt.fields.NamespaceID,
|
NamespaceID: tt.fields.NamespaceID,
|
||||||
Right: tt.fields.Right,
|
Right: tt.fields.Right,
|
||||||
Created: tt.fields.Created,
|
Created: tt.fields.Created,
|
||||||
|
@ -293,7 +293,7 @@ func TestNamespaceUser_Update(t *testing.T) {
|
||||||
func TestNamespaceUser_Delete(t *testing.T) {
|
func TestNamespaceUser_Delete(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
ID int64
|
ID int64
|
||||||
UserID int64
|
Username string
|
||||||
NamespaceID int64
|
NamespaceID int64
|
||||||
Right Right
|
Right Right
|
||||||
Created int64
|
Created int64
|
||||||
|
@ -310,7 +310,7 @@ func TestNamespaceUser_Delete(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "Try deleting some unexistant user",
|
name: "Try deleting some unexistant user",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
UserID: 1000,
|
Username: "user1000",
|
||||||
NamespaceID: 2,
|
NamespaceID: 2,
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
|
@ -319,7 +319,7 @@ func TestNamespaceUser_Delete(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "Try deleting a user which does not has access but exists",
|
name: "Try deleting a user which does not has access but exists",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
UserID: 1,
|
Username: "user1",
|
||||||
NamespaceID: 4,
|
NamespaceID: 4,
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
|
@ -328,7 +328,7 @@ func TestNamespaceUser_Delete(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "Try deleting normally",
|
name: "Try deleting normally",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
UserID: 1,
|
Username: "user1",
|
||||||
NamespaceID: 3,
|
NamespaceID: 3,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -337,7 +337,7 @@ func TestNamespaceUser_Delete(t *testing.T) {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
nu := &NamespaceUser{
|
nu := &NamespaceUser{
|
||||||
ID: tt.fields.ID,
|
ID: tt.fields.ID,
|
||||||
UserID: tt.fields.UserID,
|
Username: tt.fields.Username,
|
||||||
NamespaceID: tt.fields.NamespaceID,
|
NamespaceID: tt.fields.NamespaceID,
|
||||||
Right: tt.fields.Right,
|
Right: tt.fields.Right,
|
||||||
Created: tt.fields.Created,
|
Created: tt.fields.Created,
|
||||||
|
|
|
@ -41,10 +41,11 @@ func (tm *TeamMember) Create(a web.Auth) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the user exists
|
// Check if the user exists
|
||||||
_, err = GetUserByID(tm.UserID)
|
user, err := GetUserByUsername(tm.Username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
tm.UserID = user.ID
|
||||||
|
|
||||||
// Check if that user is already part of the team
|
// Check if that user is already part of the team
|
||||||
exists, err := x.Where("team_id = ? AND user_id = ?", tm.TeamID, tm.UserID).
|
exists, err := x.Where("team_id = ? AND user_id = ?", tm.TeamID, tm.UserID).
|
||||||
|
|
|
@ -37,6 +37,13 @@ func (tm *TeamMember) Delete() (err error) {
|
||||||
return ErrCannotDeleteLastTeamMember{tm.TeamID, tm.UserID}
|
return ErrCannotDeleteLastTeamMember{tm.TeamID, tm.UserID}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find the numeric user id
|
||||||
|
user, err := GetUserByUsername(tm.Username)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tm.UserID = user.ID
|
||||||
|
|
||||||
_, err = x.Where("team_id = ? AND user_id = ?", tm.TeamID, tm.UserID).Delete(&TeamMember{})
|
_, err = x.Where("team_id = ? AND user_id = ?", tm.TeamID, tm.UserID).Delete(&TeamMember{})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,8 @@ func TestTeamMember_Create(t *testing.T) {
|
||||||
|
|
||||||
// Dummy team member
|
// Dummy team member
|
||||||
dummyteammember := TeamMember{
|
dummyteammember := TeamMember{
|
||||||
TeamID: 1,
|
TeamID: 1,
|
||||||
UserID: 3,
|
Username: "user3",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Doer
|
// Doer
|
||||||
|
@ -57,24 +57,24 @@ func TestTeamMember_Create(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Delete the other one
|
// Delete the other one
|
||||||
tm := TeamMember{TeamID: 1, UserID: 2}
|
tm := TeamMember{TeamID: 1, Username: "user2"}
|
||||||
err = tm.Delete()
|
err = tm.Delete()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Try deleting the last one
|
// Try deleting the last one
|
||||||
tm = TeamMember{TeamID: 1, UserID: 1}
|
tm = TeamMember{TeamID: 1, Username: "user1"}
|
||||||
err = tm.Delete()
|
err = tm.Delete()
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.True(t, IsErrCannotDeleteLastTeamMember(err))
|
assert.True(t, IsErrCannotDeleteLastTeamMember(err))
|
||||||
|
|
||||||
// Try inserting a user which does not exist
|
// Try inserting a user which does not exist
|
||||||
dummyteammember.UserID = 9484
|
dummyteammember.Username = "user9484"
|
||||||
err = dummyteammember.Create(&doer)
|
err = dummyteammember.Create(&doer)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.True(t, IsErrUserDoesNotExist(err))
|
assert.True(t, IsErrUserDoesNotExist(err))
|
||||||
|
|
||||||
// Try adding a user to a team which does not exist
|
// Try adding a user to a team which does not exist
|
||||||
tm = TeamMember{TeamID: 94824, UserID: 1}
|
tm = TeamMember{TeamID: 94824, Username: "user1"}
|
||||||
err = tm.Create(&doer)
|
err = tm.Create(&doer)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.True(t, IsErrTeamDoesNotExist(err))
|
assert.True(t, IsErrTeamDoesNotExist(err))
|
||||||
|
|
|
@ -66,8 +66,10 @@ type TeamMember struct {
|
||||||
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id"`
|
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id"`
|
||||||
// The team id.
|
// The team id.
|
||||||
TeamID int64 `xorm:"int(11) not null INDEX" json:"-" param:"team"`
|
TeamID int64 `xorm:"int(11) not null INDEX" json:"-" param:"team"`
|
||||||
// The id of the member.
|
// The username of the member. We use this to prevent automated user id entering.
|
||||||
UserID int64 `xorm:"int(11) not null INDEX" json:"userID" param:"user"`
|
Username string `xorm:"-" json:"username" param:"user"`
|
||||||
|
// Used under the hood to manage team members
|
||||||
|
UserID int64 `xorm:"int(11) not null INDEX" json:"-"`
|
||||||
// Whether or not the member is an admin of the team. See the docs for more about what a team admin can do
|
// Whether or not the member is an admin of the team. See the docs for more about what a team admin can do
|
||||||
Admin bool `xorm:"tinyint(1) INDEX null" json:"admin"`
|
Admin bool `xorm:"tinyint(1) INDEX null" json:"admin"`
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,7 @@ func (t *Team) Create(a web.Auth) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert the current user as member and admin
|
// Insert the current user as member and admin
|
||||||
tm := TeamMember{TeamID: t.ID, UserID: doer.ID, Admin: true}
|
tm := TeamMember{TeamID: t.ID, Username: doer.Username, Admin: true}
|
||||||
if err = tm.Create(doer); err != nil {
|
if err = tm.Create(doer); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,6 +119,15 @@ func GetUserByID(id int64) (user User, err error) {
|
||||||
return GetUser(User{ID: id})
|
return GetUser(User{ID: id})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUserByUsername gets a user from its user name. This is an extra function to be able to add an extra error check.
|
||||||
|
func GetUserByUsername(username string) (user User, err error) {
|
||||||
|
if username == "" {
|
||||||
|
return User{}, ErrUserDoesNotExist{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetUser(User{Username: username})
|
||||||
|
}
|
||||||
|
|
||||||
// GetUser gets a user object
|
// GetUser gets a user object
|
||||||
func GetUser(user User) (userOut User, err error) {
|
func GetUser(user User) (userOut User, err error) {
|
||||||
userOut = user
|
userOut = user
|
||||||
|
@ -139,7 +148,7 @@ func CheckUserCredentials(u *UserLogin) (User, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the user exists
|
// Check if the user exists
|
||||||
user, err := GetUser(User{Username: u.Username})
|
user, err := GetUserByUsername(u.Username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// hashing the password takes a long time, so we hash something to not make it clear if the username was wrong
|
// hashing the password takes a long time, so we hash something to not make it clear if the username was wrong
|
||||||
bcrypt.GenerateFromPassword([]byte(u.Username), 14)
|
bcrypt.GenerateFromPassword([]byte(u.Username), 14)
|
||||||
|
|
|
@ -36,7 +36,7 @@ func CreateUser(user User) (newUser User, err error) {
|
||||||
|
|
||||||
// Check if the user already existst with that username
|
// Check if the user already existst with that username
|
||||||
exists := true
|
exists := true
|
||||||
existingUser, err := GetUser(User{Username: newUser.Username})
|
existingUser, err := GetUserByUsername(newUser.Username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if IsErrUserDoesNotExist(err) {
|
if IsErrUserDoesNotExist(err) {
|
||||||
exists = false
|
exists = false
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||||
// This file was generated by swaggo/swag at
|
// This file was generated by swaggo/swag at
|
||||||
// 2019-05-22 19:24:37.734465408 +0200 CEST m=+0.660846954
|
// 2019-05-25 11:35:41.214134069 +0200 CEST m=+0.124896065
|
||||||
|
|
||||||
package swagger
|
package swagger
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ import (
|
||||||
var doc = `{
|
var doc = `{
|
||||||
"swagger": "2.0",
|
"swagger": "2.0",
|
||||||
"info": {
|
"info": {
|
||||||
"description": "\u003c!-- ReDoc-Inject: \u003csecurity-definitions\u003e --\u003e",
|
"description": "This is the documentation for the [Vikunja](http://vikunja.io) API. Vikunja is a cross-plattform Todo-application with a lot of features, such as sharing lists with users or teams. \u003c!-- ReDoc-Inject: \u003csecurity-definitions\u003e --\u003e\n# Authorization\n**JWT-Auth:** Main authorization method, used for most of the requests. Needs ` + "`" + `Authorization: Bearer \u003cjwt-token\u003e` + "`" + `-header to authenticate successfully.\n\n**BasicAuth:** Only used when requesting tasks via caldav.\n\u003c!-- ReDoc-Inject: \u003csecurity-definitions\u003e --\u003e",
|
||||||
"title": "Vikunja API",
|
"title": "Vikunja API",
|
||||||
"contact": {
|
"contact": {
|
||||||
"name": "General Vikunja contact",
|
"name": "General Vikunja contact",
|
||||||
|
@ -3984,9 +3984,9 @@ var doc = `{
|
||||||
"description": "A unix timestamp when this relation was last updated. You cannot change this value.",
|
"description": "A unix timestamp when this relation was last updated. You cannot change this value.",
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"userID": {
|
"username": {
|
||||||
"description": "The user id.",
|
"description": "The username.",
|
||||||
"type": "integer"
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -4054,8 +4054,8 @@ var doc = `{
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"userID": {
|
"userID": {
|
||||||
"description": "The user id.",
|
"description": "The username.",
|
||||||
"type": "integer"
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -4203,9 +4203,9 @@ var doc = `{
|
||||||
"description": "The unique, numeric id of this team member relation.",
|
"description": "The unique, numeric id of this team member relation.",
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"userID": {
|
"username": {
|
||||||
"description": "The id of the member.",
|
"description": "The username of the member. We use this to prevent automated user id entering.",
|
||||||
"type": "integer"
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"swagger": "2.0",
|
"swagger": "2.0",
|
||||||
"info": {
|
"info": {
|
||||||
"description": "\u003c!-- ReDoc-Inject: \u003csecurity-definitions\u003e --\u003e",
|
"description": "This is the documentation for the [Vikunja](http://vikunja.io) API. Vikunja is a cross-plattform Todo-application with a lot of features, such as sharing lists with users or teams. \u003c!-- ReDoc-Inject: \u003csecurity-definitions\u003e --\u003e\n# Authorization\n**JWT-Auth:** Main authorization method, used for most of the requests. Needs ` + \"`\" + `Authorization: Bearer \u003cjwt-token\u003e` + \"`\" + `-header to authenticate successfully.\n\n**BasicAuth:** Only used when requesting tasks via caldav.\n\u003c!-- ReDoc-Inject: \u003csecurity-definitions\u003e --\u003e",
|
||||||
"title": "Vikunja API",
|
"title": "Vikunja API",
|
||||||
"contact": {
|
"contact": {
|
||||||
"name": "General Vikunja contact",
|
"name": "General Vikunja contact",
|
||||||
|
@ -3970,9 +3970,9 @@
|
||||||
"description": "A unix timestamp when this relation was last updated. You cannot change this value.",
|
"description": "A unix timestamp when this relation was last updated. You cannot change this value.",
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"userID": {
|
"username": {
|
||||||
"description": "The user id.",
|
"description": "The username.",
|
||||||
"type": "integer"
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -4040,8 +4040,8 @@
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"userID": {
|
"userID": {
|
||||||
"description": "The user id.",
|
"description": "The username.",
|
||||||
"type": "integer"
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -4189,9 +4189,9 @@
|
||||||
"description": "The unique, numeric id of this team member relation.",
|
"description": "The unique, numeric id of this team member relation.",
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"userID": {
|
"username": {
|
||||||
"description": "The id of the member.",
|
"description": "The username of the member. We use this to prevent automated user id entering.",
|
||||||
"type": "integer"
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -312,9 +312,9 @@ definitions:
|
||||||
description: A unix timestamp when this relation was last updated. You cannot
|
description: A unix timestamp when this relation was last updated. You cannot
|
||||||
change this value.
|
change this value.
|
||||||
type: integer
|
type: integer
|
||||||
userID:
|
username:
|
||||||
description: The user id.
|
description: The username.
|
||||||
type: integer
|
type: string
|
||||||
type: object
|
type: object
|
||||||
models.Message:
|
models.Message:
|
||||||
properties:
|
properties:
|
||||||
|
@ -369,8 +369,8 @@ definitions:
|
||||||
change this value.
|
change this value.
|
||||||
type: integer
|
type: integer
|
||||||
userID:
|
userID:
|
||||||
description: The user id.
|
description: The username.
|
||||||
type: integer
|
type: string
|
||||||
type: object
|
type: object
|
||||||
models.NamespaceWithLists:
|
models.NamespaceWithLists:
|
||||||
properties:
|
properties:
|
||||||
|
@ -487,9 +487,10 @@ definitions:
|
||||||
id:
|
id:
|
||||||
description: The unique, numeric id of this team member relation.
|
description: The unique, numeric id of this team member relation.
|
||||||
type: integer
|
type: integer
|
||||||
userID:
|
username:
|
||||||
description: The id of the member.
|
description: The username of the member. We use this to prevent automated
|
||||||
type: integer
|
user id entering.
|
||||||
|
type: string
|
||||||
type: object
|
type: object
|
||||||
models.TeamNamespace:
|
models.TeamNamespace:
|
||||||
properties:
|
properties:
|
||||||
|
@ -651,7 +652,13 @@ info:
|
||||||
email: hello@vikunja.io
|
email: hello@vikunja.io
|
||||||
name: General Vikunja contact
|
name: General Vikunja contact
|
||||||
url: http://vikunja.io/en/contact/
|
url: http://vikunja.io/en/contact/
|
||||||
description: '<!-- ReDoc-Inject: <security-definitions> -->'
|
description: |-
|
||||||
|
This is the documentation for the [Vikunja](http://vikunja.io) API. Vikunja is a cross-plattform Todo-application with a lot of features, such as sharing lists with users or teams. <!-- ReDoc-Inject: <security-definitions> -->
|
||||||
|
# Authorization
|
||||||
|
**JWT-Auth:** Main authorization method, used for most of the requests. Needs ` + "`" + `Authorization: Bearer <jwt-token>` + "`" + `-header to authenticate successfully.
|
||||||
|
|
||||||
|
**BasicAuth:** Only used when requesting tasks via caldav.
|
||||||
|
<!-- ReDoc-Inject: <security-definitions> -->
|
||||||
license:
|
license:
|
||||||
name: GPLv3
|
name: GPLv3
|
||||||
url: http://code.vikunja.io/api/src/branch/master/LICENSE
|
url: http://code.vikunja.io/api/src/branch/master/LICENSE
|
||||||
|
|
Loading…
Reference in a new issue