Browse Source

Merge branch 'attestation'

pull/51/head
Simon Eisenmann 12 years ago
parent
commit
e3a749e5d6
  1. 18
      doc/CHANNELING-API.txt
  2. 1
      src/app/spreed-webrtc-server/channeling.go
  3. 46
      src/app/spreed-webrtc-server/hub.go
  4. 52
      src/app/spreed-webrtc-server/server.go
  5. 104
      src/app/spreed-webrtc-server/session.go
  6. 2
      static/js/filters/buddyimagesrc.js
  7. 2
      static/js/filters/displayname.js
  8. 6
      static/js/mediastream/api.js
  9. 106
      static/js/services/buddydata.js

18
doc/CHANNELING-API.txt

@ -68,7 +68,8 @@ Sending vs receiving document data encapsulation @@ -68,7 +68,8 @@ Sending vs receiving document data encapsulation
"From": "4",
"To": "5",
"Data": {},
"Iid": "request-identifier-unique-to-client"
"Iid": "request-identifier-unique-to-client",
"A": "attestation-session-token"
}
The Data key contains the real Document.
@ -80,9 +81,11 @@ Sending vs receiving document data encapsulation @@ -80,9 +81,11 @@ Sending vs receiving document data encapsulation
To : The Id, the server send this Document to. Should be the same as
your current Self Id.
Data : Contains the payload.
Iid : Optional request identifier to match this response to the calling
Iid : Request identifier to match this response to the calling
request. Only available when sent by the client and the requested
type implementation does support it.
type implementation does support it (optional).
A : Session attestation token. Only available for incoming data
created by other sessions (optional).
Special purpose documents for channling
@ -400,11 +403,16 @@ Information retrieval @@ -400,11 +403,16 @@ Information retrieval
"Type": "Sessions",
"Sessions": {
"Type": "Token type",
"Token": "Request token"
"Token": "Token data"
}
}
Valid known token types are: "contact".
Valid known token types are:
contact:
Token data retrieved when a contact request is accepted.
session:
Token data retrieved on incoming messages as A field (attestation
token).
Sessions (Response with Id, Token and Type from request and
populated Session list).

1
src/app/spreed-webrtc-server/channeling.go

@ -158,6 +158,7 @@ type DataOutgoing struct { @@ -158,6 +158,7 @@ type DataOutgoing struct {
From string
To string
Iid string `json:",omitempty"`
A string `json:",omitempty"`
}
type DataSessions struct {

46
src/app/spreed-webrtc-server/hub.go

@ -46,6 +46,7 @@ const ( @@ -46,6 +46,7 @@ const (
maxUsersLength = 5000
)
// TODO(longsleep): Get rid of MessageRequest type.
type MessageRequest struct {
From string
To string
@ -79,6 +80,7 @@ type Hub struct { @@ -79,6 +80,7 @@ type Hub struct {
encryptionSecret []byte
turnSecret []byte
tickets *securecookie.SecureCookie
attestations *securecookie.SecureCookie
count uint64
mutex sync.RWMutex
buffers BufferCache
@ -114,11 +116,14 @@ func NewHub(version string, config *Config, sessionSecret, encryptionSecret, tur @@ -114,11 +116,14 @@ func NewHub(version string, config *Config, sessionSecret, encryptionSecret, tur
h.tickets.MaxAge(86400 * 30) // 30 days
h.tickets.HashFunc(sha256.New)
h.tickets.BlockFunc(aes.NewCipher)
h.attestations = securecookie.New(h.sessionSecret, nil)
h.attestations.MaxAge(300) // 5 minutes
h.tickets.HashFunc(sha256.New)
h.buffers = NewBufferCache(1024, bytes.MinRead)
h.buddyImages = NewImageCache()
h.tokenName = fmt.Sprintf("token@%s", h.realm)
h.contacts = securecookie.New(h.sessionSecret, h.encryptionSecret)
h.contacts.MaxAge(0)
h.contacts.MaxAge(0) // Forever
h.contacts.HashFunc(sha256.New)
h.contacts.BlockFunc(aes.NewCipher)
return h
@ -214,7 +219,7 @@ func (h *Hub) CreateSession(request *http.Request, st *SessionToken) *Session { @@ -214,7 +219,7 @@ func (h *Hub) CreateSession(request *http.Request, st *SessionToken) *Session {
if st == nil {
sid := NewRandomString(32)
id, _ := h.tickets.Encode("id", sid)
session = NewSession(id, sid)
session = NewSession(h, id, sid)
log.Println("Created new session id", len(id), id, sid)
} else {
if userid == "" {
@ -223,7 +228,7 @@ func (h *Hub) CreateSession(request *http.Request, st *SessionToken) *Session { @@ -223,7 +228,7 @@ func (h *Hub) CreateSession(request *http.Request, st *SessionToken) *Session {
if !usersEnabled {
userid = ""
}
session = NewSession(st.Id, st.Sid)
session = NewSession(h, st.Id, st.Sid)
}
if userid != "" {
@ -250,6 +255,25 @@ func (h *Hub) ValidateSession(id, sid string) bool { @@ -250,6 +255,25 @@ func (h *Hub) ValidateSession(id, sid string) bool {
}
/*
func (h *Hub) EncodeAttestation(session *Session) (string, error) {
attestation, err := h.attestations.Encode("attestation", session.Id)
if err == nil {
session.UpdateAttestation(attestation)
}
return attestation, err
}
func (h *Hub) DecodeAttestation(token string) (string, error) {
var id string
err := h.attestations.Decode("attestation", token, &id)
return id, err
}*/
func (h *Hub) EncodeSessionToken(st *SessionToken) (string, error) {
return h.tickets.Encode(h.tokenName, st)
@ -448,13 +472,27 @@ func (h *Hub) sessionsHandler(c *Connection, srq *DataSessionsRequest, iid strin @@ -448,13 +472,27 @@ func (h *Hub) sessionsHandler(c *Connection, srq *DataSessionsRequest, iid strin
}
// Find foreign user.
h.mutex.RLock()
defer h.mutex.RUnlock()
user, ok := h.userTable[userid]
h.mutex.RUnlock()
if !ok {
return
}
// Add sessions for forein user.
users = user.SessionsData()
case "session":
id, err := c.Session.attestation.Decode(srq.Token)
if err != nil {
log.Println("Failed to decode incoming attestation", err, srq.Token)
return
}
h.mutex.RLock()
session, ok := h.sessionTable[id]
h.mutex.RUnlock()
if !ok {
return
}
users = make([]*DataSession, 1, 1)
users[0] = session.Data()
default:
log.Println("Unkown incoming sessions request type", srq.Type)
}

52
src/app/spreed-webrtc-server/server.go

@ -180,7 +180,7 @@ func (s *Server) Unicast(c *Connection, to string, m interface{}) { @@ -180,7 +180,7 @@ func (s *Server) Unicast(c *Connection, to string, m interface{}) {
b := c.h.buffers.New()
encoder := json.NewEncoder(b)
err := encoder.Encode(&DataOutgoing{From: c.Id, To: to, Data: m})
err := encoder.Encode(&DataOutgoing{From: c.Id, To: to, Data: m, A: c.Session.Attestation()})
if err != nil {
b.Decref()
log.Println("Unicast error while encoding JSON", err)
@ -192,6 +192,31 @@ func (s *Server) Unicast(c *Connection, to string, m interface{}) { @@ -192,6 +192,31 @@ func (s *Server) Unicast(c *Connection, to string, m interface{}) {
b.Decref()
}
func (s *Server) Broadcast(c *Connection, m interface{}) {
b := c.h.buffers.New()
encoder := json.NewEncoder(b)
err := encoder.Encode(&DataOutgoing{From: c.Id, Data: m, A: c.Session.Attestation()})
if err != nil {
b.Decref()
log.Println("Broadcast error while encoding JSON", err)
return
}
if c.h.isGlobalRoomid(c.Roomid) {
c.h.RunForAllRooms(func(room *RoomWorker) {
var msg = &MessageRequest{From: c.Id, Message: b, Id: room.Id}
room.broadcastHandler(msg)
})
} else {
var msg = &MessageRequest{From: c.Id, Message: b, Id: c.Roomid}
room := c.h.GetRoom(c.Roomid)
room.broadcastHandler(msg)
}
b.Decref()
}
func (s *Server) Alive(c *Connection, alive *DataAlive, iid string) {
c.h.aliveHandler(c, alive, iid)
@ -217,31 +242,6 @@ func (s *Server) ContactRequest(c *Connection, to string, cr *DataContactRequest @@ -217,31 +242,6 @@ func (s *Server) ContactRequest(c *Connection, to string, cr *DataContactRequest
}
func (s *Server) Broadcast(c *Connection, m interface{}) {
b := c.h.buffers.New()
encoder := json.NewEncoder(b)
err := encoder.Encode(&DataOutgoing{From: c.Id, Data: m})
if err != nil {
b.Decref()
log.Println("Broadcast error while encoding JSON", err)
return
}
if c.h.isGlobalRoomid(c.Roomid) {
c.h.RunForAllRooms(func(room *RoomWorker) {
var msg = &MessageRequest{From: c.Id, Message: b, Id: room.Id}
room.broadcastHandler(msg)
})
} else {
var msg = &MessageRequest{From: c.Id, Message: b, Id: c.Roomid}
room := c.h.GetRoom(c.Roomid)
room.broadcastHandler(msg)
}
b.Decref()
}
func (s *Server) Users(c *Connection) {
room := c.h.GetRoom(c.Roomid)

104
src/app/spreed-webrtc-server/session.go

@ -32,26 +32,31 @@ import ( @@ -32,26 +32,31 @@ import (
var sessionNonces *securecookie.SecureCookie
type Session struct {
Id string
Sid string
Ua string
UpdateRev uint64
Status interface{}
Nonce string
Prio int
mutex sync.RWMutex
userid string
stamp int64
Id string
Sid string
Ua string
UpdateRev uint64
Status interface{}
Nonce string
Prio int
mutex sync.RWMutex
userid string
stamp int64
attestation *SessionAttestation
h *Hub
}
func NewSession(id, sid string) *Session {
func NewSession(h *Hub, id, sid string) *Session {
return &Session{
session := &Session{
Id: id,
Sid: sid,
Prio: 100,
stamp: time.Now().Unix(),
h: h,
}
session.NewAttestation()
return session
}
@ -153,9 +158,12 @@ func (s *Session) Data() *DataSession { @@ -153,9 +158,12 @@ func (s *Session) Data() *DataSession {
}
func (s *Session) Userid() string {
func (s *Session) Userid() (userid string) {
return s.userid
s.mutex.RLock()
userid = s.userid
s.mutex.RUnlock()
return
}
@ -203,6 +211,32 @@ func (s *Session) DataSessionStatus() *DataSession { @@ -203,6 +211,32 @@ func (s *Session) DataSessionStatus() *DataSession {
}
func (s *Session) NewAttestation() {
s.attestation = &SessionAttestation{
s: s,
}
s.attestation.Update()
}
func (s *Session) Attestation() (attestation string) {
s.mutex.RLock()
attestation = s.attestation.Token()
s.mutex.RUnlock()
return
}
func (s *Session) UpdateAttestation() {
s.mutex.Lock()
s.attestation.Update()
s.mutex.Unlock()
}
type SessionUpdate struct {
Id string
Types []string
@ -219,6 +253,48 @@ type SessionToken struct { @@ -219,6 +253,48 @@ type SessionToken struct {
Nonce string `json:"Nonce,omitempty"` // User autentication nonce.
}
type SessionAttestation struct {
refresh int64
token string
s *Session
}
func (sa *SessionAttestation) Update() (string, error) {
token, err := sa.Encode()
if err == nil {
sa.token = token
sa.refresh = time.Now().Unix() + 180 // expires after 3 minutes
}
return token, err
}
func (sa *SessionAttestation) Token() (token string) {
if sa.refresh < time.Now().Unix() {
token, _ = sa.Update()
} else {
token = sa.token
}
return
}
func (sa *SessionAttestation) Encode() (string, error) {
return sa.s.h.attestations.Encode("attestation", sa.s.Id)
}
func (sa *SessionAttestation) Decode(token string) (string, error) {
var id string
err := sa.s.h.attestations.Decode("attestation", token, &id)
return id, err
}
func init() {
// Create nonce generator.
sessionNonces = securecookie.New(securecookie.GenerateRandomKey(64), nil)

2
static/js/filters/buddyimagesrc.js

@ -77,7 +77,7 @@ define(["underscore"], function(_) { @@ -77,7 +77,7 @@ define(["underscore"], function(_) {
return function(id) {
var scope = buddyData.lookup(id);
var scope = buddyData.lookup(id, false, true);
if (scope) {
var display = scope.display;
if (display) {

2
static/js/filters/displayname.js

@ -33,7 +33,7 @@ define([], function() { @@ -33,7 +33,7 @@ define([], function() {
if (id === group_chat_id) {
return "";
}
var scope = buddyData.lookup(id);
var scope = buddyData.lookup(id, false, true);
if (scope) {
if (scope.display.displayName) {
return scope.display.displayName;

6
static/js/mediastream/api.js

@ -121,10 +121,16 @@ define(['jquery', 'underscore'], function($, _) { @@ -121,10 +121,16 @@ define(['jquery', 'underscore'], function($, _) {
this.last_receive = now;
this.last_receive_overdue = false;
var attestation = d.A;
var iid = d.Iid;
var data = d.Data;
var dataType = data.Type;
if (attestation && d.From) {
// Trigger received attestations.
this.e.triggerHandler("received.attestation", [d.From, attestation]);
}
if (iid) {
// Shortcut for iid registered responses.
this.e.triggerHandler(iid+".request", [dataType, data]);

106
static/js/services/buddydata.js

@ -21,11 +21,13 @@ @@ -21,11 +21,13 @@
define(['underscore'], function(underscore) {
// buddyData
return ["contactData", function(contactData) {
return ["contactData", "mediaStream", "$rootScope", function(contactData, mediaStream, $rootScope) {
var scopes = {};
var brain = {};
var pushed = {};
var attestations = {};
var fakes = {};
var count = 0;
var buddyData = {
@ -39,10 +41,15 @@ define(['underscore'], function(underscore) { @@ -39,10 +41,15 @@ define(['underscore'], function(underscore) {
push: function(id) {
var entry = pushed[id];
if (!entry) {
entry = pushed[id] = {
count: 1,
scope: scopes[id]
};
var scope = scopes[id];
if (scope) {
entry = pushed[id] = {
count: 1,
scope: scopes[id]
};
} else {
return 0;
}
} else {
entry.count++;
}
@ -104,18 +111,48 @@ define(['underscore'], function(underscore) { @@ -104,18 +111,48 @@ define(['underscore'], function(underscore) {
}
}
},
lookup: function(id, onlyactive) {
var scope = null;
lookup: function(id, onlyactive, withfakes) {
if (!id) {
return;
}
if (scopes.hasOwnProperty(id)) {
scope = scopes[id];
return scopes[id];
} else if (!onlyactive) {
if (brain.hasOwnProperty(id)) {
scope = brain[id];
return brain[id];
} else if (pushed.hasOwnProperty(id)) {
scope = pushed[id].scope;
return pushed[id].scope;
}
if (withfakes) {
var fake = fakes[id];
//console.log("check fake", id, fake);
if (fake) {
//console.log("found fake", fake);
if (fake.display) {
return fake;
}
} else {
if (attestations.hasOwnProperty(id)) {
// Fetch with help of session attestation token.
fake = fakes[id] = {};
var token = attestations[id].a;
console.log("attestation request", id);
mediaStream.api.sendSessions(token, "session", function(event, type, data) {
console.log("attestation session response", id, type, data);
if (data.Users && data.Users.length > 0) {
var s = data.Users[0];
fake.display = {
displayName: s.Status.displayName
}
// TODO(longsleep): Find a better way to apply this than digest on root scope.
$rootScope.$digest();
}
});
}
}
}
}
return scope;
return null;
},
del: function(id, hard) {
var scope = scopes[id];
@ -131,8 +168,55 @@ define(['underscore'], function(underscore) { @@ -131,8 +168,55 @@ define(['underscore'], function(underscore) {
},
set: function(id, scope) {
scopes[id] = scope;
},
attestation: function(id) {
var data = attestations[id];
if (data) {
return data.a;
}
return null;
}
};
// attestation support
(function() {
// Listen for attestation events.
mediaStream.api.e.on("received.attestation", function(event, from, attestation) {
var current = attestations[from];
var create = false;
if (!current) {
create = true;
} else {
if (current.a !== attestation) {
create = true;
}
}
if (create) {
console.log("Created attestation entry", from);
attestations[from] = {
a: attestation,
t: (new Date().getTime())
}
}
});
var expire = function() {
var expired = (new Date().getTime()) - 240000;
_.each(attestations, function(data, id) {
if (data.t < expired) {
delete attestations[id];
//console.log("expired attestation", id);
}
})
setTimeout(expire, 120000);
};
expire();
})();
return buddyData;
}];

Loading…
Cancel
Save