ripple-api/app/v1/token.go

126 lines
3.1 KiB
Go
Raw Normal View History

2016-04-05 20:22:13 +00:00
package v1
import (
"crypto/md5"
"database/sql"
"fmt"
2016-04-19 14:07:27 +00:00
"git.zxq.co/ripple/rippleapi/common"
2016-04-05 20:22:13 +00:00
"golang.org/x/crypto/bcrypt"
)
type tokenNewInData struct {
// either username or userid must be given in the request.
// if none is given, the request is trashed.
Username string `json:"username"`
UserID int `json:"id"`
Password string `json:"password"`
Privileges int `json:"privileges"`
Description string `json:"description"`
}
2016-04-16 16:05:24 +00:00
type tokenNewResponse struct {
common.ResponseBase
2016-04-05 20:22:13 +00:00
Username string `json:"username"`
ID int `json:"id"`
Privileges int `json:"privileges"`
Token string `json:"token,omitempty"`
Banned bool `json:"banned"`
}
// TokenNewPOST is the handler for POST /token/new.
2016-04-16 16:05:24 +00:00
func TokenNewPOST(md common.MethodData) common.CodeMessager {
var r tokenNewResponse
2016-04-05 20:22:13 +00:00
data := tokenNewInData{}
err := md.RequestData.Unmarshal(&data)
2016-04-05 20:22:13 +00:00
if err != nil {
2016-04-16 16:05:24 +00:00
return ErrBadJSON
2016-04-05 20:22:13 +00:00
}
var miss []string
if data.Username == "" && data.UserID == 0 {
miss = append(miss, "username|id")
}
if data.Password == "" {
miss = append(miss, "password")
}
if len(miss) != 0 {
2016-04-16 16:05:24 +00:00
return ErrMissingField(miss...)
2016-04-05 20:22:13 +00:00
}
var q *sql.Row
const base = "SELECT id, username, rank, password_md5, password_version, allowed FROM users "
if data.UserID != 0 {
q = md.DB.QueryRow(base+"WHERE id = ? LIMIT 1", data.UserID)
} else {
q = md.DB.QueryRow(base+"WHERE username = ? LIMIT 1", data.Username)
}
var (
rank int
pw string
pwVersion int
allowed int
)
2016-04-16 16:05:24 +00:00
err = q.Scan(&r.ID, &r.Username, &rank, &pw, &pwVersion, &allowed)
2016-04-05 20:22:13 +00:00
switch {
case err == sql.ErrNoRows:
2016-04-16 16:05:24 +00:00
return common.SimpleResponse(404, "No user with that username/id was found.")
2016-04-05 20:22:13 +00:00
case err != nil:
md.Err(err)
2016-04-16 16:05:24 +00:00
return Err500
2016-04-05 20:22:13 +00:00
}
2016-04-16 16:05:24 +00:00
if nFailedAttempts(r.ID) > 20 {
return common.SimpleResponse(429, "You've made too many login attempts. Try again later.")
2016-04-10 09:51:34 +00:00
}
2016-04-05 20:22:13 +00:00
if pwVersion == 1 {
2016-04-16 16:05:24 +00:00
return common.SimpleResponse(418, "That user still has a password in version 1. Unfortunately, in order for the API to check for the password to be OK, the user has to first log in through the website.")
2016-04-05 20:22:13 +00:00
}
if err := bcrypt.CompareHashAndPassword([]byte(pw), []byte(fmt.Sprintf("%x", md5.Sum([]byte(data.Password))))); err != nil {
if err == bcrypt.ErrMismatchedHashAndPassword {
2016-04-16 16:05:24 +00:00
go addFailedAttempt(r.ID)
return common.SimpleResponse(403, "That password doesn't match!")
2016-04-05 20:22:13 +00:00
}
md.Err(err)
2016-04-16 16:05:24 +00:00
return Err500
2016-04-05 20:22:13 +00:00
}
if allowed == 0 {
r.Code = 200
r.Message = "That user is banned."
2016-04-16 16:05:24 +00:00
r.Banned = true
return r
2016-04-05 20:22:13 +00:00
}
2016-04-16 16:05:24 +00:00
r.Privileges = int(common.Privileges(data.Privileges).CanOnly(rank))
2016-04-05 20:22:13 +00:00
var (
tokenStr string
tokenMD5 string
)
for {
tokenStr = common.RandomString(32)
tokenMD5 = fmt.Sprintf("%x", md5.Sum([]byte(tokenStr)))
2016-04-16 16:05:24 +00:00
r.Token = tokenStr
2016-04-05 20:22:13 +00:00
id := 0
2016-04-07 10:43:39 +00:00
err := md.DB.QueryRow("SELECT id FROM tokens WHERE token=? LIMIT 1", tokenMD5).Scan(&id)
2016-04-05 20:22:13 +00:00
if err == sql.ErrNoRows {
break
}
if err != nil {
md.Err(err)
2016-04-16 16:05:24 +00:00
return Err500
2016-04-05 20:22:13 +00:00
}
}
2016-04-16 16:05:24 +00:00
_, err = md.DB.Exec("INSERT INTO tokens(user, privileges, description, token) VALUES (?, ?, ?, ?)", r.ID, r.Privileges, data.Description, tokenMD5)
2016-04-05 20:22:13 +00:00
if err != nil {
md.Err(err)
2016-04-16 16:05:24 +00:00
return Err500
2016-04-05 20:22:13 +00:00
}
r.Code = 200
2016-04-16 16:05:24 +00:00
return r
2016-04-05 20:22:13 +00:00
}