Add getting or creating a third party user

This commit is contained in:
kolaente 2020-10-25 20:28:28 +01:00
parent 52d9c25125
commit 0c382f2ee6
Signed by: konrad
GPG Key ID: F40E70337AB24C9B
6 changed files with 134 additions and 10 deletions

View File

@ -233,7 +233,6 @@ auth:
# A list of enabled providers
providers:
# The name of the provider as it will appear in the frontend.
# **If you change this after users already authenticated with this provider, users will loose access to their accounts!**
- name:
# The auth url to send users to if they want to authenticate using OpenID Connect.
authurl:

1
go.mod
View File

@ -30,6 +30,7 @@ require (
github.com/d4l3k/messagediff v1.2.1 // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/disintegration/imaging v1.6.2
github.com/dustinkirkland/golang-petname v0.0.0-20191129215211-8e5a1ed0cff0
github.com/fzipp/gocyclo v0.3.1
github.com/gabriel-vasile/mimetype v1.1.1
github.com/getsentry/sentry-go v0.7.0

2
go.sum
View File

@ -140,6 +140,8 @@ github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustinkirkland/golang-petname v0.0.0-20191129215211-8e5a1ed0cff0 h1:90Ly+6UfUypEF6vvvW5rQIv9opIL8CbmW9FT20LDQoY=
github.com/dustinkirkland/golang-petname v0.0.0-20191129215211-8e5a1ed0cff0/go.mod h1:V+Qd57rJe8gd4eiGzZyg4h54VLHmYVVw54iMnlAMrF8=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=

View File

@ -0,0 +1,50 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2020 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 migration
import (
"src.techknowlogick.com/xormigrate"
"xorm.io/xorm"
)
type user20201025195822 struct {
Issuer string `xorm:"text null" json:"-"`
Subject string `xorm:"text null" json:"-"`
}
func (user20201025195822) TableName() string {
return "user"
}
func init() {
migrations = append(migrations, &xormigrate.Migration{
ID: "20201025195822",
Description: "",
Migrate: func(tx *xorm.Engine) error {
err := tx.Sync2(user20201025195822{})
if err != nil {
return err
}
_, err = tx.Cols("issuer").Update(&user20201025195822{Issuer: "local"})
return err
},
Rollback: func(tx *xorm.Engine) error {
return nil
},
})
}

View File

@ -20,9 +20,12 @@ import (
apiv1 "code.vikunja.io/api/pkg/routes/api/v1"
"context"
"encoding/json"
petname "github.com/dustinkirkland/golang-petname"
"math/rand"
"net/http"
"regexp"
"strings"
"time"
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/log"
@ -55,6 +58,10 @@ type claims struct {
PreferredUsername string `json:"preferred_username"`
}
func init() {
rand.Seed(time.Now().UTC().UnixNano())
}
func getKeyFromName(name string) string {
reg, _ := regexp.Compile("[^a-z0-9]+")
return reg.ReplaceAllString(strings.ToLower(name), "")
@ -194,6 +201,51 @@ func HandleCallback(c echo.Context) error {
return apiv1.NewUserAuthTokenResponse(u, c)
}
func getOrCreateUser() (u *user.User, err error) {
func getOrCreateUser(cl *claims, issuer, subject string) (u *user.User, err error) {
// Check if the user exists for that issuer and subject
u, err = user.GetUser(&user.User{
Issuer: issuer,
Subject: subject,
})
if err != nil && !user.IsErrUserDoesNotExist(err) {
return nil, err
}
// If no user exists, create one with the preferred username if it is not already taken
if user.IsErrUserDoesNotExist(err) {
uu := &user.User{
Username: cl.PreferredUsername,
Email: cl.Email,
IsActive: true,
Issuer: issuer,
Subject: subject,
}
u, err = user.CreateUser(uu)
if err != nil && !user.IsErrUsernameExists(err) {
return nil, err
}
// If their preferred username is already taken, create some random one from the email and subject
if user.IsErrUsernameExists(err) {
uu.Username = petname.Generate(3, "-")
u, err = user.CreateUser(uu)
}
} else {
// If it exists, check if the email address changed and change it if not
if cl.Email != u.Email {
u.Email = cl.Email
u, err = user.UpdateUser(&user.User{
ID: u.ID,
Email: cl.Email,
Issuer: issuer,
Subject: subject,
})
if err != nil {
return nil, err
}
}
}
return
}

View File

@ -49,7 +49,7 @@ type User struct {
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id"`
// The username of the user. Is always unique.
Username string `xorm:"varchar(250) not null unique" json:"username" valid:"length(1|250)" minLength:"1" maxLength:"250"`
Password string `xorm:"varchar(250) not null" json:"-"`
Password string `xorm:"varchar(250) null" json:"-"`
// The user's email address.
Email string `xorm:"varchar(250) null" json:"email,omitempty" valid:"email,length(0|250)" maxLength:"250"`
IsActive bool `xorm:"null" json:"-"`
@ -60,6 +60,10 @@ type User struct {
AvatarProvider string `xorm:"varchar(255) null" json:"-"`
AvatarFileID int64 `xorn:"null" json:"-"`
// Issuer and Subject contain the issuer and subject from the source the user authenticated with.
Issuer string `xorm:"text null" json:"-"`
Subject string `xorm:"text null" json:"-"`
// A timestamp when this task was created. You cannot change this value.
Created time.Time `xorm:"created not null" json:"created"`
// A timestamp when this task was last updated. You cannot change this value.
@ -248,7 +252,17 @@ func CreateUser(user *User) (newUser *User, err error) {
// Check if the user already existst with that email
exists = true
_, err = GetUser(&User{Email: newUser.Email})
userToCheck := &User{
Email: newUser.Email,
Issuer: newUser.Issuer,
Subject: newUser.Subject,
}
if newUser.Issuer == "local" {
userToCheck.Email = ""
}
_, err = GetUser(userToCheck)
if err != nil {
if IsErrUserDoesNotExist(err) {
exists = false
@ -260,14 +274,16 @@ func CreateUser(user *User) (newUser *User, err error) {
return &User{}, ErrUserEmailExists{newUser.ID, newUser.Email}
}
// Hash the password
newUser.Password, err = hashPassword(user.Password)
if err != nil {
return &User{}, err
if newUser.Issuer == "local" {
// Hash the password
newUser.Password, err = hashPassword(user.Password)
if err != nil {
return &User{}, err
}
}
newUser.IsActive = true
if config.MailerEnabled.GetBool() {
if config.MailerEnabled.GetBool() && newUser.Issuer == "local" {
// The new user should not be activated until it confirms his mail address
newUser.IsActive = false
// Generate a confirm token
@ -340,7 +356,11 @@ func UpdateUser(user *User) (updatedUser *User, err error) {
if user.Email == "" {
user.Email = theUser.Email
} else {
uu, err := getUser(&User{Email: user.Email}, true)
uu, err := getUser(&User{
Email: user.Email,
Issuer: user.Issuer,
Subject: user.Subject,
}, true)
if err != nil && !IsErrUserDoesNotExist(err) {
return nil, err
}