Skip to content

Commit

Permalink
making sure a user may still change the password even after auto-login
Browse files Browse the repository at this point in the history
  • Loading branch information
or-else committed Apr 27, 2018
1 parent 16693d1 commit c1eee80
Show file tree
Hide file tree
Showing 6 changed files with 222 additions and 111 deletions.
28 changes: 18 additions & 10 deletions server/auth/basic/auth_basic.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func parseSecret(bsecret []byte) (uname, password string, err error) {
secret := string(bsecret)

splitAt := strings.Index(secret, ":")
if splitAt < 1 {
if splitAt < 0 {
err = types.ErrMalformed
return
}
Expand Down Expand Up @@ -77,25 +77,25 @@ func (BasicAuth) UpdateRecord(rec *auth.Rec, secret []byte) error {
return err
}

storedUID, _, _, _, err := store.Users.GetAuthRecord("basic", uname)
login, _, _, _, err := store.Users.GetAuthRecord(rec.Uid, "basic")
if err != nil {
return err
}
// Record not found - probably invalid login
if storedUID.IsZero() {
// User does not have a record.
if login == "" {
return types.ErrNotFound
}
if storedUID != rec.Uid {
// User is trying to change login to one that is already taken
return types.ErrDuplicate
if uname == "" {
// User is changing just the password.
uname = login
}
passhash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return types.ErrInternal
}
var expires time.Time
if rec.Lifetime > 0 {
expires = time.Now().Add(rec.Lifetime)
expires = types.TimeNow().Add(rec.Lifetime)
}
_, err = store.Users.UpdateAuthRecord(rec.Uid, auth.LevelAuth, "basic", uname, passhash, expires)
if err != nil {
Expand All @@ -111,7 +111,11 @@ func (BasicAuth) Authenticate(secret []byte) (*auth.Rec, error) {
return nil, err
}

uid, authLvl, passhash, expires, err := store.Users.GetAuthRecord("basic", uname)
if len(uname) < minLoginLength || len(uname) > maxLoginLength {
return nil, types.ErrFailed
}

uid, authLvl, passhash, expires, err := store.Users.GetAuthUniqueRecord("basic", uname)
if err != nil {
return nil, err
} else if uid.IsZero() {
Expand Down Expand Up @@ -146,7 +150,11 @@ func (BasicAuth) IsUnique(secret []byte) (bool, error) {
return false, err
}

uid, _, _, _, err := store.Users.GetAuthRecord("basic", uname)
if len(uname) < minLoginLength || len(uname) > maxLoginLength {
return false, types.ErrPolicy
}

uid, _, _, _, err := store.Users.GetAuthUniqueRecord("basic", uname)
if err != nil {
return false, err
}
Expand Down
176 changes: 102 additions & 74 deletions server/db/mysql/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ func (a *adapter) CreateDb(reset bool) error {

if _, err = tx.Exec(
`CREATE TABLE kvmeta(` +
"`key` CHAR(32)," +
"`key` CHAR(32)," +
"`value` TEXT," +
"PRIMARY KEY(`key`)" +
`)`); err != nil {
Expand All @@ -198,16 +198,16 @@ func (a *adapter) CreateDb(reset bool) error {

if _, err = tx.Exec(
`CREATE TABLE users(
id BIGINT NOT NULL,
createdat DATETIME(3) NOT NULL,
updatedat DATETIME(3) NOT NULL,
deletedat DATETIME(3),
state INT DEFAULT 0,
access JSON,
lastseen DATETIME,
useragent VARCHAR(255) DEFAULT '',
public JSON,
tags JSON,
id BIGINT NOT NULL,
createdat DATETIME(3) NOT NULL,
updatedat DATETIME(3) NOT NULL,
deletedat DATETIME(3),
state INT DEFAULT 0,
access JSON,
lastseen DATETIME,
useragent VARCHAR(255) DEFAULT '',
public JSON,
tags JSON,
PRIMARY KEY(id)
)`); err != nil {
return err
Expand All @@ -216,9 +216,9 @@ func (a *adapter) CreateDb(reset bool) error {
// Indexed user tags.
if _, err = tx.Exec(
`CREATE TABLE usertags(
id INT NOT NULL AUTO_INCREMENT,
userid BIGINT NOT NULL,
tag VARCHAR(96) NOT NULL,
id INT NOT NULL AUTO_INCREMENT,
userid BIGINT NOT NULL,
tag VARCHAR(96) NOT NULL,
PRIMARY KEY(id),
FOREIGN KEY(userid) REFERENCES users(id),
INDEX usertags_tag (tag)
Expand All @@ -229,13 +229,13 @@ func (a *adapter) CreateDb(reset bool) error {
// Indexed devices. Normalized into a separate table.
if _, err = tx.Exec(
`CREATE TABLE devices(
id INT NOT NULL AUTO_INCREMENT,
userid BIGINT NOT NULL,
hash CHAR(16) NOT NULL,
deviceid TEXT NOT NULL,
platform VARCHAR(32),
lastseen DATETIME NOT NULL,
lang VARCHAR(8),
id INT NOT NULL AUTO_INCREMENT,
userid BIGINT NOT NULL,
hash CHAR(16) NOT NULL,
deviceid TEXT NOT NULL,
platform VARCHAR(32),
lastseen DATETIME NOT NULL,
lang VARCHAR(8),
PRIMARY KEY(id),
FOREIGN KEY(userid) REFERENCES users(id),
UNIQUE INDEX devices_hash (hash)
Expand All @@ -245,35 +245,37 @@ func (a *adapter) CreateDb(reset bool) error {

// Authentication records for the basic authentication scheme.
if _, err = tx.Exec(
`CREATE TABLE basicauth(
id INT NOT NULL AUTO_INCREMENT,
login VARCHAR(32) NOT NULL,
userid BIGINT NOT NULL,
authlvl INT NOT NULL,
secret VARCHAR(255) NOT NULL,
expires DATETIME,
`CREATE TABLE auth(
id INT NOT NULL AUTO_INCREMENT,
uname VARCHAR(32) NOT NULL,
userid BIGINT NOT NULL,
scheme VARCHAR(16) NOT NULL,
authlvl INT NOT NULL,
secret VARCHAR(255) NOT NULL,
expires DATETIME,
PRIMARY KEY(id),
FOREIGN KEY(userid) REFERENCES users(id),
UNIQUE INDEX basicauth_login(login)
UNIQUE INDEX auth_userid_scheme(userid, scheme),
UNIQUE INDEX auth_uname(uname)
)`); err != nil {
return err
}

// Topics
if _, err = tx.Exec(
`CREATE TABLE topics(
id INT NOT NULL AUTO_INCREMENT,
createdat DATETIME(3) NOT NULL,
updatedat DATETIME(3) NOT NULL,
deletedat DATETIME(3),
touchedat DATETIME(3),
name CHAR(25) NOT NULL,
usebt INT DEFAULT 0,
access JSON,
seqid INT NOT NULL DEFAULT 0,
delid INT DEFAULT 0,
public JSON,
tags JSON,
id INT NOT NULL AUTO_INCREMENT,
createdat DATETIME(3) NOT NULL,
updatedat DATETIME(3) NOT NULL,
deletedat DATETIME(3),
touchedat DATETIME(3),
name CHAR(25) NOT NULL,
usebt INT DEFAULT 0,
access JSON,
seqid INT NOT NULL DEFAULT 0,
delid INT DEFAULT 0,
public JSON,
tags JSON,
PRIMARY KEY(id),
UNIQUE INDEX topics_name(name)
)`); err != nil {
Expand All @@ -283,9 +285,9 @@ func (a *adapter) CreateDb(reset bool) error {
// Indexed topic tags.
if _, err = tx.Exec(
`CREATE TABLE topictags(
id INT NOT NULL AUTO_INCREMENT,
topic CHAR(25) NOT NULL,
tag VARCHAR(96) NOT NULL,
id INT NOT NULL AUTO_INCREMENT,
topic CHAR(25) NOT NULL,
tag VARCHAR(96) NOT NULL,
PRIMARY KEY(id),
FOREIGN KEY(topic) REFERENCES topics(name),
INDEX topictags_tag(tag)
Expand Down Expand Up @@ -319,16 +321,16 @@ func (a *adapter) CreateDb(reset bool) error {
// Messages
if _, err = tx.Exec(
`CREATE TABLE messages(
id INT NOT NULL AUTO_INCREMENT,
createdat DATETIME(3) NOT NULL,
updatedat DATETIME(3) NOT NULL,
deletedat DATETIME(3),
delid INT DEFAULT 0,
seqid INT NOT NULL,
topic CHAR(25) NOT NULL,` +
"`from` BIGINT NOT NULL," +
`head JSON,
content JSON,
id INT NOT NULL AUTO_INCREMENT,
createdat DATETIME(3) NOT NULL,
updatedat DATETIME(3) NOT NULL,
deletedat DATETIME(3),
delid INT DEFAULT 0,
seqid INT NOT NULL,
topic CHAR(25) NOT NULL,` +
"`from` BIGINT NOT NULL," +
`head JSON,
content JSON,
PRIMARY KEY(id),` +
"FOREIGN KEY(`from`) REFERENCES users(id)," +
`FOREIGN KEY(topic) REFERENCES topics(name),
Expand All @@ -340,12 +342,12 @@ func (a *adapter) CreateDb(reset bool) error {
// Deletion log
if _, err = tx.Exec(
`CREATE TABLE dellog(
id INT NOT NULL AUTO_INCREMENT,
topic VARCHAR(25) NOT NULL,
deletedfor BIGINT NOT NULL DEFAULT 0,
delid INT NOT NULL,
low INT NOT NULL,
hi INT NOT NULL,
id INT NOT NULL AUTO_INCREMENT,
topic VARCHAR(25) NOT NULL,
deletedfor BIGINT NOT NULL DEFAULT 0,
delid INT NOT NULL,
low INT NOT NULL,
hi INT NOT NULL,
PRIMARY KEY(id),
FOREIGN KEY(topic) REFERENCES topics(name),
UNIQUE INDEX dellog_topic_delid_deletedfor(topic,delid,deletedfor),
Expand Down Expand Up @@ -435,15 +437,15 @@ func (a *adapter) UserCreate(user *t.User) error {
}

// Add user's authentication record
func (a *adapter) AuthAddRecord(uid t.Uid, authLvl auth.Level, unique string,
func (a *adapter) AuthAddRecord(uid t.Uid, scheme, unique string, authLvl auth.Level,
secret []byte, expires time.Time) (bool, error) {

var exp *time.Time
if !expires.IsZero() {
exp = &expires
}
_, err := a.db.Exec("INSERT INTO basicauth(login,userid,authLvl,secret,expires) VALUES(?,?,?,?,?)",
unique, store.DecodeUid(uid), authLvl, secret, exp)
_, err := a.db.Exec("INSERT INTO auth(uname,userid,scheme,authLvl,secret,expires) VALUES(?,?,?,?,?,?)",
unique, store.DecodeUid(uid), scheme, authLvl, secret, exp)
if err != nil {
if isDupe(err) {
return true, t.ErrDuplicate
Expand All @@ -455,13 +457,13 @@ func (a *adapter) AuthAddRecord(uid t.Uid, authLvl auth.Level, unique string,

// Delete user's authentication record
func (a *adapter) AuthDelRecord(user t.Uid, unique string) error {
_, err := a.db.Exec("DELETE FROM basicauth WHERE userid=? AND login=?", store.DecodeUid(user), unique)
_, err := a.db.Exec("DELETE FROM auth WHERE userid=? AND uname=?", store.DecodeUid(user), unique)
return err
}

// Delete user's all authentication records
func (a *adapter) AuthDelAllRecords(uid t.Uid) (int, error) {
res, err := a.db.Exec("DELETE FROM basicauth WHERE userid=?", store.DecodeUid(uid))
res, err := a.db.Exec("DELETE FROM auth WHERE userid=?", store.DecodeUid(uid))
if err != nil {
return 0, err
}
Expand All @@ -471,25 +473,52 @@ func (a *adapter) AuthDelAllRecords(uid t.Uid) (int, error) {
}

// Update user's authentication secret
func (a *adapter) AuthUpdRecord(unique string, authLvl auth.Level, secret []byte, expires time.Time) (int, error) {
func (a *adapter) AuthUpdRecord(uid t.Uid, scheme, unique string, authLvl auth.Level,
secret []byte, expires time.Time) (bool, error) {
var exp *time.Time
if !expires.IsZero() {
exp = &expires
}

res, err := a.db.Exec("UPDATE basicauth SET authLvl=?,secret=?,expires=? WHERE login=?",
authLvl, secret, exp, unique)
_, err := a.db.Exec("UPDATE auth SET uname=?,authLvl=?,secret=?,expires=? WHERE uname=?",
unique, authLvl, secret, exp, unique)
if isDupe(err) {
return true, t.ErrDuplicate
}

return false, err
}

// Retrieve user's authentication record
func (a *adapter) AuthGetRecord(uid t.Uid, scheme string) (string, auth.Level, []byte, time.Time, error) {
var expires time.Time

var record struct {
Uname string
Authlvl auth.Level
Secret []byte
Expires *time.Time
}

err := a.db.Get(&record, "SELECT uname,secret,expires,authlvl FROM auth WHERE userid=? AND scheme=?",
store.DecodeUid(uid), scheme)
if err != nil {
return 0, err
if err == sql.ErrNoRows {
// Nothing found - clear the error
err = nil
}
return "", 0, nil, expires, err
}

count, _ := res.RowsAffected()
return int(count), nil
if record.Expires != nil {
expires = *record.Expires
}

return record.Uname, record.Authlvl, record.Secret, expires, nil
}

// Retrieve user's authentication record
func (a *adapter) AuthGetRecord(unique string) (t.Uid, auth.Level, []byte, time.Time, error) {
func (a *adapter) AuthGetUniqueRecord(unique string) (t.Uid, auth.Level, []byte, time.Time, error) {
var expires time.Time

var record struct {
Expand All @@ -499,7 +528,7 @@ func (a *adapter) AuthGetRecord(unique string) (t.Uid, auth.Level, []byte, time.
Expires *time.Time
}

err := a.db.Get(&record, "SELECT userid, secret, expires, authlvl FROM basicauth WHERE login=?", unique)
err := a.db.Get(&record, "SELECT userid,secret,expires,authlvl FROM auth WHERE uname=?", unique)
if err != nil {
if err == sql.ErrNoRows {
// Nothing found - clear the error
Expand All @@ -512,7 +541,6 @@ func (a *adapter) AuthGetRecord(unique string) (t.Uid, auth.Level, []byte, time.
expires = *record.Expires
}

// log.Println("loggin in user Id=", user.Uid(), user.Id)
return store.EncodeUid(record.Userid), record.Authlvl, record.Secret, expires, nil
}

Expand Down
18 changes: 10 additions & 8 deletions server/db/mysql/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,19 @@ CREATE TABLE devices(
);

# Authentication records for the basic authentication scheme.
CREATE TABLE basicauth(
id INT NOT NULL AUTO_INCREMENT,
login VARCHAR(32) NOT NULL,
userid BIGINT NOT NULL,
authlvl INT NOT NULL,
secret VARCHAR(255) NOT NULL,
expires DATETIME,
CREATE TABLE auth(
id INT NOT NULL AUTO_INCREMENT,
uname VARCHAR(32) NOT NULL,
userid BIGINT NOT NULL,
scheme VARCHAR(16) NOT NULL,
authlvl INT NOT NULL,
secret VARCHAR(255) NOT NULL,
expires DATETIME,

PRIMARY KEY(id),
FOREIGN KEY(userid) REFERENCES users(id),
UNIQUE INDEX basicauth_login (login)
UNIQUE INDEX auth_userid_scheme(userid, scheme),
UNIQUE INDEX auth_uname (uname)
);


Expand Down
Loading

0 comments on commit c1eee80

Please sign in to comment.