|
|
@ -25,9 +25,13 @@ import ( |
|
|
|
"crypto/hmac" |
|
|
|
"crypto/hmac" |
|
|
|
"crypto/sha256" |
|
|
|
"crypto/sha256" |
|
|
|
"encoding/base64" |
|
|
|
"encoding/base64" |
|
|
|
|
|
|
|
"encoding/json" |
|
|
|
"errors" |
|
|
|
"errors" |
|
|
|
|
|
|
|
"fmt" |
|
|
|
|
|
|
|
"github.com/satori/go.uuid" |
|
|
|
"github.com/strukturag/phoenix" |
|
|
|
"github.com/strukturag/phoenix" |
|
|
|
"log" |
|
|
|
"log" |
|
|
|
|
|
|
|
"net/http" |
|
|
|
"strconv" |
|
|
|
"strconv" |
|
|
|
"strings" |
|
|
|
"strings" |
|
|
|
"time" |
|
|
|
"time" |
|
|
@ -35,12 +39,21 @@ import ( |
|
|
|
|
|
|
|
|
|
|
|
type UsersHandler interface { |
|
|
|
type UsersHandler interface { |
|
|
|
Validate(snr *SessionNonceRequest) (string, error) |
|
|
|
Validate(snr *SessionNonceRequest) (string, error) |
|
|
|
|
|
|
|
Create(snr *UserNonce) (*UserNonce, error) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
type UsersSharedsecretHandler struct { |
|
|
|
type UsersSharedsecretHandler struct { |
|
|
|
secret []byte |
|
|
|
secret []byte |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (uh *UsersSharedsecretHandler) createHMAC(useridCombo string) string { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
m := hmac.New(sha256.New, uh.secret) |
|
|
|
|
|
|
|
m.Write([]byte(useridCombo)) |
|
|
|
|
|
|
|
return base64.StdEncoding.EncodeToString(m.Sum(nil)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (uh *UsersSharedsecretHandler) Validate(snr *SessionNonceRequest) (string, error) { |
|
|
|
func (uh *UsersSharedsecretHandler) Validate(snr *SessionNonceRequest) (string, error) { |
|
|
|
|
|
|
|
|
|
|
|
// Parse UseridCombo.
|
|
|
|
// Parse UseridCombo.
|
|
|
@ -57,23 +70,41 @@ func (uh *UsersSharedsecretHandler) Validate(snr *SessionNonceRequest) (string, |
|
|
|
return "", errors.New("expired secret") |
|
|
|
return "", errors.New("expired secret") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Check HMAC.
|
|
|
|
secret := uh.createHMAC(snr.UseridCombo) |
|
|
|
foo := hmac.New(sha256.New, uh.secret) |
|
|
|
if snr.Secret != secret { |
|
|
|
foo.Write([]byte(snr.UseridCombo)) |
|
|
|
|
|
|
|
fooSecret := base64.StdEncoding.EncodeToString(foo.Sum(nil)) |
|
|
|
|
|
|
|
if snr.Secret != fooSecret { |
|
|
|
|
|
|
|
return "", errors.New("invalid secret") |
|
|
|
return "", errors.New("invalid secret") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return userid, nil |
|
|
|
return userid, nil |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (uh *UsersSharedsecretHandler) Create(un *UserNonce) (*UserNonce, error) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO(longsleep): Make this configureable - One year for now ...
|
|
|
|
|
|
|
|
expiration := time.Now().Add(time.Duration(1) * time.Hour * 24 * 31 * 12) |
|
|
|
|
|
|
|
un.UseridCombo = fmt.Sprintf("%d:%s", expiration.Unix(), un.Userid) |
|
|
|
|
|
|
|
un.Secret = uh.createHMAC(un.UseridCombo) |
|
|
|
|
|
|
|
return un, nil |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type UserNonce struct { |
|
|
|
|
|
|
|
Nonce string `json:"nonce"` |
|
|
|
|
|
|
|
Userid string `json:"userid"` |
|
|
|
|
|
|
|
UseridCombo string `json:"useridcombo"` |
|
|
|
|
|
|
|
Secret string `json:"secret"` |
|
|
|
|
|
|
|
Success bool `json:"success"` |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
type Users struct { |
|
|
|
type Users struct { |
|
|
|
|
|
|
|
hub *Hub |
|
|
|
Enabled bool |
|
|
|
Enabled bool |
|
|
|
|
|
|
|
Create bool |
|
|
|
Handler UsersHandler |
|
|
|
Handler UsersHandler |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func NewUsers(runtime phoenix.Runtime) *Users { |
|
|
|
func NewUsers(hub *Hub, runtime phoenix.Runtime) *Users { |
|
|
|
|
|
|
|
|
|
|
|
enabled := false |
|
|
|
enabled := false |
|
|
|
enabledString, err := runtime.GetString("users", "enabled") |
|
|
|
enabledString, err := runtime.GetString("users", "enabled") |
|
|
@ -81,6 +112,12 @@ func NewUsers(runtime phoenix.Runtime) *Users { |
|
|
|
enabled = enabledString == "true" |
|
|
|
enabled = enabledString == "true" |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
create := false |
|
|
|
|
|
|
|
createString, err := runtime.GetString("users", "allowRegistration") |
|
|
|
|
|
|
|
if err == nil { |
|
|
|
|
|
|
|
create = createString == "true" |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
var handler UsersHandler |
|
|
|
var handler UsersHandler |
|
|
|
|
|
|
|
|
|
|
|
if enabled { |
|
|
|
if enabled { |
|
|
@ -99,14 +136,61 @@ func NewUsers(runtime phoenix.Runtime) *Users { |
|
|
|
if handler == nil { |
|
|
|
if handler == nil { |
|
|
|
enabled = false |
|
|
|
enabled = false |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
log.Printf("Enabled users handler '%s'.\n", mode) |
|
|
|
log.Printf("Enabled users handler '%s'\n", mode) |
|
|
|
|
|
|
|
if create { |
|
|
|
|
|
|
|
log.Println("Enabled users registration") |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return &Users{ |
|
|
|
return &Users{ |
|
|
|
|
|
|
|
hub: hub, |
|
|
|
Enabled: enabled, |
|
|
|
Enabled: enabled, |
|
|
|
|
|
|
|
Create: create, |
|
|
|
Handler: handler, |
|
|
|
Handler: handler, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Post is used to create new userids for this server.
|
|
|
|
|
|
|
|
func (users *Users) Post(request *http.Request) (int, interface{}, http.Header) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if !users.Create { |
|
|
|
|
|
|
|
return 404, "404 page not found", http.Header{"Content-Type": {"text/plain"}} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
decoder := json.NewDecoder(request.Body) |
|
|
|
|
|
|
|
var snr SessionNonceRequest |
|
|
|
|
|
|
|
err := decoder.Decode(&snr) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return 400, NewApiError("users_bad_request", "Failed to parse request"), http.Header{"Content-Type": {"application/json"}} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Make sure that we have a Sid.
|
|
|
|
|
|
|
|
if snr.Sid == "" || snr.Id == "" { |
|
|
|
|
|
|
|
return 400, NewApiError("users_bad_request", "Incomplete request"), http.Header{"Content-Type": {"application/json"}} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Do this before session validation to avoid timing information.
|
|
|
|
|
|
|
|
userid := uuid.NewV4().String() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Make sure Sid matches session and is valid.
|
|
|
|
|
|
|
|
if !users.hub.ValidateSession(snr.Id, snr.Sid) { |
|
|
|
|
|
|
|
return 403, NewApiError("users_invalid_session", "Invalid session"), http.Header{"Content-Type": {"application/json"}} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
nonce, err := users.hub.sessiontokenHandler(&SessionToken{Id: snr.Id, Sid: snr.Sid, Userid: userid}) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return 400, NewApiError("users_request_failed", fmt.Sprintf("Error: %q", err)), http.Header{"Content-Type": {"application/json"}} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
un, err := users.Handler.Create(&UserNonce{Nonce: nonce, Userid: userid, Success: true}) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return 400, NewApiError("users_create_failed", fmt.Sprintf("Error: %q", err)), http.Header{"Content-Type": {"application/json"}} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log.Printf("Users create successfull %s -> %s\n", snr.Id, un.Userid) |
|
|
|
|
|
|
|
return 200, un, http.Header{"Content-Type": {"application/json"}} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|