Browse Source

Added realm to userids, tokens and nonces.

pull/28/head
Simon Eisenmann 11 years ago
parent
commit
a1c1405c61
  1. 1
      doc/REST-API.txt
  2. 4
      server.conf.in
  3. 12
      src/app/spreed-speakfreely-server/hub.go
  4. 24
      src/app/spreed-speakfreely-server/main.go
  5. 2
      src/app/spreed-speakfreely-server/server.go
  6. 9
      src/app/spreed-speakfreely-server/session.go
  7. 2
      src/app/spreed-speakfreely-server/sessions.go
  8. 17
      src/app/spreed-speakfreely-server/users.go
  9. 30
      static/js/controllers/mediastreamcontroller.js

1
doc/REST-API.txt

@ -92,6 +92,7 @@ Available end points with request methods and content-type: @@ -92,6 +92,7 @@ Available end points with request methods and content-type:
"success": true,
"userid": "user-id",
"useridcombo": "authorization-id",
"timestamp": 1430688014,
"secret": "authorization-secret-for-authorization-id",
"nonce": "authorization-nonce"
}

4
server.conf.in

@ -75,6 +75,10 @@ sessionSecret = the-default-secret-do-not-keep-me @@ -75,6 +75,10 @@ sessionSecret = the-default-secret-do-not-keep-me
; server generated security tokens. When the serverToken is changed all existing
; nonces become invalid. Use 32 or 64 byte random data.
;serverToken = i-did-not-change-the-public-token-boo
; The server realm is part of the validation chain of tokens and nonces and is
; added as suffix to server created user ids if user creation is enabled. When
; the realm is changed, all existing tokens and nonces become invalid.
;serverRealm = local
; Full path to an extra templates directory. Templates in this directory ending
; with .html will be parsed on startup and can be used to fill the supported
; extra-* template slots. If the extra folder has a sub folder "static", the

12
src/app/spreed-speakfreely-server/hub.go

@ -79,9 +79,11 @@ type Hub struct { @@ -79,9 +79,11 @@ type Hub struct {
broadcastChatMessages uint64
unicastChatMessages uint64
buddyImages ImageCache
realm string
tokenName string
}
func NewHub(version string, config *Config, sessionSecret, turnSecret string) *Hub {
func NewHub(version string, config *Config, sessionSecret, turnSecret, realm string) *Hub {
h := &Hub{
connectionTable: make(map[string]*Connection),
@ -91,6 +93,7 @@ func NewHub(version string, config *Config, sessionSecret, turnSecret string) *H @@ -91,6 +93,7 @@ func NewHub(version string, config *Config, sessionSecret, turnSecret string) *H
config: config,
sessionSecret: []byte(sessionSecret),
turnSecret: []byte(turnSecret),
realm: realm,
}
if len(h.sessionSecret) < 32 {
@ -100,6 +103,7 @@ func NewHub(version string, config *Config, sessionSecret, turnSecret string) *H @@ -100,6 +103,7 @@ func NewHub(version string, config *Config, sessionSecret, turnSecret string) *H
h.tickets = securecookie.New(h.sessionSecret, nil)
h.buffers = NewBufferCache(1024, bytes.MinRead)
h.buddyImages = NewImageCache()
h.tokenName = fmt.Sprintf("token@%s", h.realm)
return h
}
@ -203,14 +207,14 @@ func (h *Hub) ValidateSession(id, sid string) bool { @@ -203,14 +207,14 @@ func (h *Hub) ValidateSession(id, sid string) bool {
func (h *Hub) EncodeSessionToken(st *SessionToken) (string, error) {
return h.tickets.Encode("token", st)
return h.tickets.Encode(h.tokenName, st)
}
func (h *Hub) DecodeSessionToken(token string) (*SessionToken, error) {
st := &SessionToken{}
err := h.tickets.Decode("token", token, st)
err := h.tickets.Decode(h.tokenName, token, st)
return st, err
}
@ -401,7 +405,7 @@ func (h *Hub) sessiontokenHandler(st *SessionToken) (string, error) { @@ -401,7 +405,7 @@ func (h *Hub) sessiontokenHandler(st *SessionToken) (string, error) {
return "", errors.New("no such connection")
}
nonce, err := c.Session.Authorize(st)
nonce, err := c.Session.Authorize(h.realm, st)
if err != nil {
return "", err
}

24
src/app/spreed-speakfreely-server/main.go

@ -263,12 +263,6 @@ func runner(runtime phoenix.Runtime) error { @@ -263,12 +263,6 @@ func runner(runtime phoenix.Runtime) error {
defaultRoomEnabled = defaultRoomEnabledString == "true"
}
serverToken, err := runtime.GetString("app", "serverToken")
if err == nil {
//TODO(longsleep): When we have a database, generate this once from random source and store it.
serverToken = "i-did-not-change-the-public-token-boo"
}
usersEnabled := false
usersEnabledString, err := runtime.GetString("users", "enabled")
if err == nil {
@ -281,6 +275,17 @@ func runner(runtime phoenix.Runtime) error { @@ -281,6 +275,17 @@ func runner(runtime phoenix.Runtime) error {
usersAllowRegistration = usersAllowRegistrationString == "true"
}
serverToken, err := runtime.GetString("app", "serverToken")
if err != nil {
//TODO(longsleep): When we have a database, generate this once from random source and store it.
serverToken = "i-did-not-change-the-public-token-boo"
}
serverRealm, err := runtime.GetString("app", "serverRealm")
if err != nil {
serverRealm = "local"
}
// Create token provider.
var tokenProvider TokenProvider
if tokenFile != "" {
@ -313,8 +318,11 @@ func runner(runtime phoenix.Runtime) error { @@ -313,8 +318,11 @@ func runner(runtime phoenix.Runtime) error {
log.Printf("Loaded extra templates from: %s", extraFolder)
}
// Create realm string from config.
computedRealm := fmt.Sprintf("%s.%s", serverRealm, serverToken)
// Create our hub instance.
hub := NewHub(runtimeVersion, config, sessionSecret, turnSecret)
hub := NewHub(runtimeVersion, config, sessionSecret, turnSecret, computedRealm)
// Set number of go routines if it is 1
if goruntime.GOMAXPROCS(0) == 1 {
@ -363,7 +371,7 @@ func runner(runtime phoenix.Runtime) error { @@ -363,7 +371,7 @@ func runner(runtime phoenix.Runtime) error {
api.AddResourceWithWrapper(&Tokens{tokenProvider}, httputils.MakeGzipHandler, "/tokens")
if usersEnabled {
// Create Users handler.
users := NewUsers(hub, runtime)
users := NewUsers(hub, serverRealm, runtime)
api.AddResource(&Sessions{hub: hub, users: users}, "/sessions/{id}/")
if usersAllowRegistration {
api.AddResource(users, "/users")

2
src/app/spreed-speakfreely-server/server.go

@ -228,7 +228,7 @@ func (s *Server) Users(c *Connection) { @@ -228,7 +228,7 @@ func (s *Server) Users(c *Connection) {
func (s *Server) Authenticate(c *Connection, st *SessionToken) bool {
err := c.Session.Authenticate(st)
err := c.Session.Authenticate(c.h.realm, st)
if err == nil {
log.Println("Authentication success", c.Id, c.Idx, st.Userid)
return true

9
src/app/spreed-speakfreely-server/session.go

@ -23,6 +23,7 @@ package main @@ -23,6 +23,7 @@ package main
import (
"errors"
"fmt"
"github.com/gorilla/securecookie"
"sync"
)
@ -88,7 +89,7 @@ func (s *Session) Apply(st *SessionToken) uint64 { @@ -88,7 +89,7 @@ func (s *Session) Apply(st *SessionToken) uint64 {
}
func (s *Session) Authorize(st *SessionToken) (string, error) {
func (s *Session) Authorize(realm string, st *SessionToken) (string, error) {
s.mutex.Lock()
defer s.mutex.Unlock()
@ -102,13 +103,13 @@ func (s *Session) Authorize(st *SessionToken) (string, error) { @@ -102,13 +103,13 @@ func (s *Session) Authorize(st *SessionToken) (string, error) {
// Create authentication nonce.
var err error
s.Nonce, err = sessionNonces.Encode(s.Sid, st.Userid)
s.Nonce, err = sessionNonces.Encode(fmt.Sprintf("%s@%s", s.Sid, realm), st.Userid)
return s.Nonce, err
}
func (s *Session) Authenticate(st *SessionToken) error {
func (s *Session) Authenticate(realm string, st *SessionToken) error {
s.mutex.Lock()
defer s.mutex.Unlock()
@ -120,7 +121,7 @@ func (s *Session) Authenticate(st *SessionToken) error { @@ -120,7 +121,7 @@ func (s *Session) Authenticate(st *SessionToken) error {
return errors.New("nonce validation failed")
}
var userid string
err := sessionNonces.Decode(s.Sid, st.Nonce, &userid)
err := sessionNonces.Decode(fmt.Sprintf("%s@%s", s.Sid, realm), st.Nonce, &userid)
if err != nil {
return err
}

2
src/app/spreed-speakfreely-server/sessions.go

@ -84,7 +84,7 @@ func (sessions *Sessions) Patch(request *http.Request) (int, interface{}, http.H @@ -84,7 +84,7 @@ func (sessions *Sessions) Patch(request *http.Request) (int, interface{}, http.H
}
// Validate with users handler.
userid, err := sessions.users.Handler.Validate(&snr)
userid, err := sessions.users.handler.Validate(&snr)
if err != nil {
error = true
log.Println("Session patch failed - users validation failed.", err)

17
src/app/spreed-speakfreely-server/users.go

@ -83,8 +83,10 @@ func (uh *UsersSharedsecretHandler) Create(un *UserNonce) (*UserNonce, error) { @@ -83,8 +83,10 @@ 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.Timestamp = expiration.Unix()
un.UseridCombo = fmt.Sprintf("%d:%s", un.Timestamp, un.Userid)
un.Secret = uh.createHMAC(un.UseridCombo)
return un, nil
}
@ -94,15 +96,17 @@ type UserNonce struct { @@ -94,15 +96,17 @@ type UserNonce struct {
Userid string `json:"userid"`
UseridCombo string `json:"useridcombo"`
Secret string `json:"secret"`
Timestamp int64 `json:"timestamp"`
Success bool `json:"success"`
}
type Users struct {
hub *Hub
Handler UsersHandler
realm string
handler UsersHandler
}
func NewUsers(hub *Hub, runtime phoenix.Runtime) *Users {
func NewUsers(hub *Hub, realm string, runtime phoenix.Runtime) *Users {
var handler UsersHandler
@ -125,7 +129,8 @@ func NewUsers(hub *Hub, runtime phoenix.Runtime) *Users { @@ -125,7 +129,8 @@ func NewUsers(hub *Hub, runtime phoenix.Runtime) *Users {
return &Users{
hub: hub,
Handler: handler,
realm: realm,
handler: handler,
}
}
@ -146,7 +151,7 @@ func (users *Users) Post(request *http.Request) (int, interface{}, http.Header) @@ -146,7 +151,7 @@ func (users *Users) Post(request *http.Request) (int, interface{}, http.Header)
}
// Do this before session validation to avoid timing information.
userid := uuid.NewV4().String()
userid := fmt.Sprintf("%s@%s", uuid.NewV4().String(), users.realm)
// Make sure Sid matches session and is valid.
if !users.hub.ValidateSession(snr.Id, snr.Sid) {
@ -158,7 +163,7 @@ func (users *Users) Post(request *http.Request) (int, interface{}, http.Header) @@ -158,7 +163,7 @@ func (users *Users) Post(request *http.Request) (int, interface{}, http.Header)
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})
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"}}
}

30
static/js/controllers/mediastreamcontroller.js

@ -432,16 +432,26 @@ define(['underscore', 'bigscreen', 'moment', 'sjcl', 'webrtc.adapter'], function @@ -432,16 +432,26 @@ define(['underscore', 'bigscreen', 'moment', 'sjcl', 'webrtc.adapter'], function
if (!login && mediaStream.config.UsersAllowRegistration) {
console.log("No userid - creating one ...");
mediaStream.users.register(function(data) {
var login = sjcl.encrypt(key, JSON.stringify({
v: 1,
t: data.timestamp || "",
a: data.useridcombo,
b: data.secret,
}));
localStorage.setItem("mediastream-login", login);
console.info("Created new userid:", data.userid);
mediaStream.api.requestAuthentication(data.userid, data.nonce);
delete data.nonce;
console.info("Created new userid:", data.userid);
if (data.nonce) {
// If the server provided us a nonce, we can do everthing on our own.
// So we store the stuff in localStorage for later use and directly
// authenticate ourselves with the provided nonce.
var login = sjcl.encrypt(key, JSON.stringify({
v: 1,
t: data.timestamp || "",
a: data.useridcombo,
b: data.secret,
}));
localStorage.setItem("mediastream-login", login);
mediaStream.api.requestAuthentication(data.userid, data.nonce);
delete data.nonce;
} else {
// No nonce received. So this means something we cannot do on our own.
// Make are GET request and retrieve nonce that way and let the
// browser/server do the rest.
// TODO(longsleep): Implement me.
}
}, function(data, status) {
console.error("Failed to create userid", status, data);
});

Loading…
Cancel
Save