Browse Source

Add guards for NPE's when processing channelling API messages.

This also refactors the channelling API for better clarity
as well as introducing the following functional changes:

 * All Hello requests will now receive a Welcome or Error return.
 * The message sent in response to a successful authentication
   request now uses the Iid provided with the originating request.
 * Responses to requests for the Self document now use the Iid
   provided with the originating request.
 * Failed authentication requests now receive an error return.
 * Malformed requests for the Hello, Authentication, Sessions,
   and Rooms documents will now receive an error return.
 * Requests for the Sessions document which cannot be processed
   now receive an error return.
 * Requests for the Users document will receive an error return
   if the requesting session has not joined a room.
pull/189/head
Lance Cooper 10 years ago
parent
commit
56aa603293
  1. 37
      doc/CHANNELING-API.txt
  2. 313
      src/app/spreed-webrtc-server/channelling_api.go
  3. 81
      src/app/spreed-webrtc-server/channelling_api_test.go
  4. 31
      src/app/spreed-webrtc-server/client.go
  5. 16
      src/app/spreed-webrtc-server/session.go

37
doc/CHANNELING-API.txt

@ -98,6 +98,13 @@ Error returns @@ -98,6 +98,13 @@ Error returns
"Message": "A description of the error condition"
}
The following predefined error codes may implicitly be returned by any call
which returns an error document:
unknown: An internal server error, the message may provide more information.
bad_request: The structure or content of the client's request was invalid,
the message may contain specifics.
Special purpose documents for channling
Self
@ -427,9 +434,10 @@ Additional types for session listing and notifications @@ -427,9 +434,10 @@ Additional types for session listing and notifications
Rev is the status update sequence for this status update entry. It
is a positive integer. Higher numbers are later status updates.
When the current session is in a room (means sent Hello), a Users request
can be sent, to receive a list of sessions in that particular room. This
always returns the sessions in the same room as the calling session.
When the current session has successfully joined a room (see Hello for more
details), a Users request will return a Users document containing session
details for the current room. An Error document will be returned if no room
has been joined or session information cannot be retrieved.
Users (Request uses empty data)
@ -470,6 +478,10 @@ Additional types for session listing and notifications @@ -470,6 +478,10 @@ Additional types for session listing and notifications
Note: The Userid field is only present, if that session belongs to a known user.
Error codes:
not_in_room: Clients must join a room before requesting users.
Alive
{
@ -506,12 +518,19 @@ User authorization and session authentication @@ -506,12 +518,19 @@ User authorization and session authentication
The Authentication document binds a userid to the current session. The
Nonce and Userid need to be validateable by the server. If Authentication
was successfull, a new Self document will be sent. The Nonce value can
be generated by using the REST API (sessions end point).
was successful, a new Self document will be sent. Otherwise an Error
document will be returned describing why authentication failed. Note that
the Nonce value can be generated by using the REST API (sessions end point).
There is no way to undo authentication for a session. For log out, close
the session (disconnect) and forget the token.
Error codes:
already_authenticated: This session has already authenticated, follow
the reauthentication procedure above.
invalid_session_token: The provided session token information is invalid,
the error message may contain more information.
Information retrieval
@ -532,6 +551,9 @@ Information retrieval @@ -532,6 +551,9 @@ Information retrieval
Token data retrieved on incoming messages as A field (attestation
token).
If session information retrieval fails, an Error document with one of the
listed codes will be returned.
Sessions (Response with Id, Token and Type from request and
populated Session list).
@ -558,6 +580,11 @@ Information retrieval @@ -558,6 +580,11 @@ Information retrieval
]
}
Error codes:
contacts_not_enabled: Requests with subtype `contact` are not enabled.
bad_attestation: The requested session attestation is invalid.
no_such_session: The requested session could not be found.
Chat messages and status information

313
src/app/spreed-webrtc-server/channelling_api.go

@ -31,9 +31,9 @@ const ( @@ -31,9 +31,9 @@ const (
)
type ChannellingAPI interface {
OnConnect(Client, *Session)
OnConnect(Client, *Session) (interface{}, error)
OnDisconnect(Client, *Session)
OnIncoming(ResponseSender, *Session, *DataIncoming)
OnIncoming(Sender, *Session, *DataIncoming) (interface{}, error)
}
type channellingAPI struct {
@ -60,169 +60,113 @@ func NewChannellingAPI(config *Config, roomStatus RoomStatusManager, sessionEnco @@ -60,169 +60,113 @@ func NewChannellingAPI(config *Config, roomStatus RoomStatusManager, sessionEnco
}
}
func (api *channellingAPI) OnConnect(client Client, session *Session) {
func (api *channellingAPI) OnConnect(client Client, session *Session) (interface{}, error) {
api.Unicaster.OnConnect(client, session)
api.SendSelf(client, session)
return api.MakeSelf(session)
}
func (api *channellingAPI) OnDisconnect(client Client, session *Session) {
api.Unicaster.OnDisconnect(client, session)
}
func (api *channellingAPI) OnIncoming(c ResponseSender, session *Session, msg *DataIncoming) {
func (api *channellingAPI) OnIncoming(sender Sender, session *Session, msg *DataIncoming) (interface{}, error) {
switch msg.Type {
case "Self":
api.SendSelf(c, session)
return api.MakeSelf(session)
case "Hello":
//log.Println("Hello", msg.Hello, c.Index())
// TODO(longsleep): Filter room id and user agent.
session.Update(&SessionUpdate{Types: []string{"Ua"}, Ua: msg.Hello.Ua})
room, err := session.JoinRoom(msg.Hello.Id, msg.Hello.Credentials, c)
// NOTE(lcooper): Iid filtered for compatibility's sake.
// Evaluate sending unconditionally when supported by all clients.
if msg.Iid != "" {
if err == nil {
c.Reply(msg.Iid, &DataWelcome{
Type: "Welcome",
Room: room,
Users: api.RoomUsers(session),
})
} else {
c.Reply(msg.Iid, err)
}
if msg.Hello == nil {
return nil, NewDataError("bad_request", "message did not contain Hello")
}
return api.HandleHello(session, msg.Hello, sender)
case "Offer":
if msg.Offer == nil {
log.Println("Received invalid offer message.", msg)
break
}
// TODO(longsleep): Validate offer
session.Unicast(msg.Offer.To, msg.Offer)
case "Candidate":
if msg.Candidate == nil {
log.Println("Received invalid candidate message.", msg)
break
}
// TODO(longsleep): Validate candidate
session.Unicast(msg.Candidate.To, msg.Candidate)
case "Answer":
if msg.Answer == nil {
log.Println("Received invalid answer message.", msg)
break
}
// TODO(longsleep): Validate Answer
session.Unicast(msg.Answer.To, msg.Answer)
case "Users":
if session.Hello {
sessions := &DataSessions{Type: "Users", Users: api.RoomUsers(session)}
c.Reply(msg.Iid, sessions)
}
return api.HandleUsers(session)
case "Authentication":
st := msg.Authentication.Authentication
if st == nil {
return
if msg.Authentication == nil || msg.Authentication.Authentication == nil {
return nil, NewDataError("bad_request", "message did not contain Authentication")
}
if err := api.Authenticate(session, st, ""); err == nil {
log.Println("Authentication success", session.Userid())
api.SendSelf(c, session)
session.BroadcastStatus()
} else {
log.Println("Authentication failed", err, st.Userid, st.Nonce)
}
return api.HandleAuthentication(session, msg.Authentication.Authentication)
case "Bye":
if msg.Bye == nil {
log.Println("Received invalid bye message.", msg)
break
}
session.Unicast(msg.Bye.To, msg.Bye)
case "Status":
if msg.Status == nil {
log.Println("Received invalid status message.", msg)
break
}
//log.Println("Status", msg.Status)
session.Update(&SessionUpdate{Types: []string{"Status"}, Status: msg.Status.Status})
session.BroadcastStatus()
case "Chat":
// TODO(longsleep): Limit sent chat messages per incoming connection.
if !msg.Chat.Chat.NoEcho {
session.Unicast(session.Id, msg.Chat)
}
msg.Chat.Chat.Time = time.Now().Format(time.RFC3339)
if msg.Chat.To == "" {
// TODO(longsleep): Check if chat broadcast is allowed.
if session.Hello {
api.CountBroadcastChat()
session.Broadcast(msg.Chat)
}
} else {
if msg.Chat.Chat.Status != nil && msg.Chat.Chat.Status.ContactRequest != nil {
if !api.Config.WithModule("contacts") {
return
}
if err := api.contactrequestHandler(session, msg.Chat.To, msg.Chat.Chat.Status.ContactRequest); err != nil {
log.Println("Ignoring invalid contact request.", err)
return
}
msg.Chat.Chat.Status.ContactRequest.Userid = session.Userid()
}
if msg.Chat.Chat.Status == nil {
api.CountUnicastChat()
}
session.Unicast(msg.Chat.To, msg.Chat)
if msg.Chat.Chat.Mid != "" {
// Send out delivery confirmation status chat message.
session.Unicast(session.Id, &DataChat{To: msg.Chat.To, Type: "Chat", Chat: &DataChatMessage{Mid: msg.Chat.Chat.Mid, Status: &DataChatStatus{State: "sent"}}})
}
if msg.Chat == nil || msg.Chat.Chat == nil {
log.Println("Received invalid chat message.", msg)
break
}
api.HandleChat(session, msg.Chat.To, msg.Chat.Chat)
case "Conference":
// Check conference maximum size.
if len(msg.Conference.Conference) > maxConferenceSize {
log.Println("Refusing to create conference above limit.", len(msg.Conference.Conference))
} else {
// Send conference update to anyone.
for _, id := range msg.Conference.Conference {
if id != session.Id {
session.Unicast(id, msg.Conference)
}
}
if msg.Conference == nil {
log.Println("Received invalid conference message.", msg)
break
}
api.HandleConference(session, msg.Conference)
case "Alive":
c.Reply(msg.Iid, msg.Alive)
return msg.Alive, nil
case "Sessions":
var users []*DataSession
switch msg.Sessions.Sessions.Type {
case "contact":
if api.Config.WithModule("contacts") {
if userID, err := api.getContactID(session, msg.Sessions.Sessions.Token); err == nil {
users = api.GetUserSessions(session, userID)
} else {
log.Printf(err.Error())
}
} else {
log.Printf("Incoming contacts session request with contacts disabled")
}
case "session":
id, err := session.attestation.Decode(msg.Sessions.Sessions.Token)
if err != nil {
log.Printf("Failed to decode incoming attestation", err, msg.Sessions.Sessions.Token)
break
}
session, ok := api.GetSession(id)
if !ok {
log.Printf("Cannot retrieve session for id %s", id)
break
}
users = make([]*DataSession, 1, 1)
users[0] = session.Data()
default:
log.Printf("Unkown incoming sessions request type %s", msg.Sessions.Sessions.Type)
if msg.Sessions == nil || msg.Sessions.Sessions == nil {
return nil, NewDataError("bad_request", "message did not contain Sessions")
}
// TODO(lcooper): We ought to reply with a *DataError here if failed.
if users != nil {
c.Reply(msg.Iid, &DataSessions{Type: "Sessions", Users: users, Sessions: msg.Sessions.Sessions})
}
return api.HandleSessions(session, msg.Sessions.Sessions)
case "Room":
if room, err := api.UpdateRoom(session, msg.Room); err == nil {
session.Broadcast(room)
c.Reply(msg.Iid, room)
} else {
c.Reply(msg.Iid, err)
if msg.Room == nil {
return nil, NewDataError("bad_request", "message did not contain Room")
}
return api.HandleRoom(session, msg.Room)
default:
log.Println("OnText unhandled message type", msg.Type)
}
return nil, nil
}
func (api *channellingAPI) SendSelf(c Responder, session *Session) {
func (api *channellingAPI) MakeSelf(session *Session) (*DataSelf, error) {
token, err := api.EncodeSessionToken(session)
if err != nil {
log.Println("Error in OnRegister", err)
return
return nil, err
}
log.Println("Created new session token", len(token), token)
@ -237,5 +181,138 @@ func (api *channellingAPI) SendSelf(c Responder, session *Session) { @@ -237,5 +181,138 @@ func (api *channellingAPI) SendSelf(c Responder, session *Session) {
Turn: api.CreateTurnData(session),
Stun: api.StunURIs,
}
c.Reply("", self)
return self, nil
}
func (api *channellingAPI) HandleHello(session *Session, hello *DataHello, sender Sender) (*DataWelcome, error) {
// TODO(longsleep): Filter room id and user agent.
session.Update(&SessionUpdate{Types: []string{"Ua"}, Ua: hello.Ua})
room, err := session.JoinRoom(hello.Id, hello.Credentials, sender)
if err != nil {
return nil, err
}
return &DataWelcome{
Type: "Welcome",
Room: room,
Users: api.RoomUsers(session),
}, nil
}
func (api *channellingAPI) HandleUsers(session *Session) (sessions *DataSessions, err error) {
if session.Hello {
sessions = &DataSessions{Type: "Users", Users: api.RoomUsers(session)}
} else {
err = NewDataError("not_in_room", "Cannot list users without a current room")
}
return
}
func (api *channellingAPI) HandleAuthentication(session *Session, st *SessionToken) (*DataSelf, error) {
if err := api.Authenticate(session, st, ""); err != nil {
log.Println("Authentication failed", err, st.Userid, st.Nonce)
return nil, err
}
log.Println("Authentication success", session.Userid())
self, err := api.MakeSelf(session)
if err == nil {
session.BroadcastStatus()
}
return self, err
}
func (api *channellingAPI) HandleChat(session *Session, to string, chat *DataChatMessage) {
// TODO(longsleep): Limit sent chat messages per incoming connection.
if !chat.NoEcho {
session.Unicast(session.Id, chat)
}
chat.Time = time.Now().Format(time.RFC3339)
if to == "" {
// TODO(longsleep): Check if chat broadcast is allowed.
if session.Hello {
api.CountBroadcastChat()
session.Broadcast(chat)
}
} else {
if chat.Status != nil {
if chat.Status.ContactRequest != nil {
if !api.Config.WithModule("contacts") {
return
}
if err := api.contactrequestHandler(session, to, chat.Status.ContactRequest); err != nil {
log.Println("Ignoring invalid contact request.", err)
return
}
chat.Status.ContactRequest.Userid = session.Userid()
}
} else {
api.CountUnicastChat()
}
session.Unicast(to, chat)
if chat.Mid != "" {
// Send out delivery confirmation status chat message.
session.Unicast(session.Id, &DataChat{To: to, Type: "Chat", Chat: &DataChatMessage{Mid: chat.Mid, Status: &DataChatStatus{State: "sent"}}})
}
}
}
func (api *channellingAPI) HandleConference(session *Session, conference *DataConference) {
// Check conference maximum size.
if len(conference.Conference) > maxConferenceSize {
log.Println("Refusing to create conference above limit.", len(conference.Conference))
return
}
// Send conference update to anyone.
for _, id := range conference.Conference {
if id != session.Id {
session.Unicast(id, conference)
}
}
}
func (api *channellingAPI) HandleSessions(session *Session, sessions *DataSessionsRequest) (*DataSessions, error) {
switch sessions.Type {
case "contact":
if !api.Config.WithModule("contacts") {
return nil, NewDataError("contacts_not_enabled", "incoming contacts session request with contacts disabled")
}
userID, err := api.getContactID(session, sessions.Token)
if err != nil {
return nil, err
}
return &DataSessions{
Type: "Sessions",
Users: api.GetUserSessions(session, userID),
Sessions: sessions,
}, nil
case "session":
id, err := session.attestation.Decode(sessions.Token)
if err != nil {
return nil, NewDataError("bad_attestation", err.Error())
}
session, ok := api.GetSession(id)
if !ok {
return nil, NewDataError("no_such_session", "cannot retrieve session")
}
return &DataSessions{
Type: "Sessions",
Users: []*DataSession{session.Data()},
Sessions: sessions,
}, nil
default:
return nil, NewDataError("bad_request", "unknown sessions request type")
}
}
func (api *channellingAPI) HandleRoom(session *Session, room *DataRoom) (*DataRoom, error) {
room, err := api.UpdateRoom(session, room)
if err == nil {
session.Broadcast(room)
}
return room, err
}

81
src/app/spreed-webrtc-server/channelling_api_test.go

@ -27,20 +27,11 @@ import ( @@ -27,20 +27,11 @@ import (
)
type fakeClient struct {
replies map[string]interface{}
}
func (fake *fakeClient) Send(_ Buffer) {
}
func (fake *fakeClient) Reply(iid string, msg interface{}) {
if fake.replies == nil {
fake.replies = make(map[string]interface{})
}
fake.replies[iid] = msg
}
type fakeRoomManager struct {
joinedRoomID string
leftRoomID string
@ -74,29 +65,6 @@ func (fake *fakeRoomManager) UpdateRoom(_ *Session, _ *DataRoom) (*DataRoom, err @@ -74,29 +65,6 @@ func (fake *fakeRoomManager) UpdateRoom(_ *Session, _ *DataRoom) (*DataRoom, err
return fake.updatedRoom, fake.updateError
}
func assertReply(t *testing.T, client *fakeClient, iid string) interface{} {
msg, ok := client.replies[iid]
if !ok {
t.Fatalf("No response received for Iid %v", iid)
}
return msg
}
func assertErrorReply(t *testing.T, client *fakeClient, iid, code string) {
err, ok := assertReply(t, client, iid).(*DataError)
if !ok {
t.Fatalf("Expected response message to be an Error")
}
if err.Type != "Error" {
t.Error("Message did not have the correct type")
}
if err.Code != code {
t.Errorf("Expected error code to be %v, but was %v", code, err.Code)
}
}
func NewTestChannellingAPI() (ChannellingAPI, *fakeClient, *Session, *fakeRoomManager) {
client, roomManager := &fakeClient{}, &fakeRoomManager{}
session := &Session{
@ -168,21 +136,19 @@ func Test_ChannellingAPI_OnIncoming_HelloMessage_DoesNotJoinIfNotPermitted(t *te @@ -168,21 +136,19 @@ func Test_ChannellingAPI_OnIncoming_HelloMessage_DoesNotJoinIfNotPermitted(t *te
}
}
func Test_ChannellingAPI_OnIncoming_HelloMessageWithAnIid_RespondsWithAWelcome(t *testing.T) {
iid, roomID := "foo", "a-room"
func Test_ChannellingAPI_OnIncoming_HelloMessage_RespondsWithAWelcome(t *testing.T) {
roomID := "a-room"
api, client, session, roomManager := NewTestChannellingAPI()
roomManager.roomUsers = []*DataSession{&DataSession{}}
api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Iid: iid, Hello: &DataHello{Id: roomID}})
msg, ok := client.replies[iid]
if !ok {
t.Fatalf("No response received for Iid %v", iid)
reply, err := api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Hello: &DataHello{Id: roomID}})
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
welcome, ok := msg.(*DataWelcome)
welcome, ok := reply.(*DataWelcome)
if !ok {
t.Fatalf("Expected response message %#v to be a Welcome", msg)
t.Fatalf("Expected response %#v to be a Welcome", reply)
}
if welcome.Type != "Welcome" {
@ -198,25 +164,31 @@ func Test_ChannellingAPI_OnIncoming_HelloMessageWithAnIid_RespondsWithAWelcome(t @@ -198,25 +164,31 @@ func Test_ChannellingAPI_OnIncoming_HelloMessageWithAnIid_RespondsWithAWelcome(t
}
}
func Test_ChannellingAPI_OnIncoming_HelloMessageWithAnIid_RespondsWithAnErrorIfTheRoomCannotBeJoined(t *testing.T) {
iid := "foo"
func Test_ChannellingAPI_OnIncoming_HelloMessage_RespondsWithAnErrorIfTheRoomCannotBeJoined(t *testing.T) {
api, client, session, roomManager := NewTestChannellingAPI()
roomManager.joinError = NewDataError("bad_join", "")
api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Iid: iid, Hello: &DataHello{}})
_, err := api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Hello: &DataHello{}})
assertErrorReply(t, client, iid, "bad_join")
assertDataError(t, err, "bad_join")
}
func Test_ChannellingAPI_OnIncoming_RoomMessage_RespondsWithAndBroadcastsTheUpdatedRoom(t *testing.T) {
iid, roomName := "123", "foo"
roomName := "foo"
api, client, session, roomManager := NewTestChannellingAPI()
roomManager.updatedRoom = &DataRoom{Name: "FOO"}
api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Iid: "0", Hello: &DataHello{Id: roomName}})
api.OnIncoming(client, session, &DataIncoming{Type: "Room", Iid: iid, Room: &DataRoom{Name: roomName}})
_, err := api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Hello: &DataHello{Id: roomName}})
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
room, ok := assertReply(t, client, iid).(*DataRoom)
reply, err := api.OnIncoming(client, session, &DataIncoming{Type: "Room", Room: &DataRoom{Name: roomName}})
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
room, ok := reply.(*DataRoom)
if !ok {
t.Fatalf("Expected response message to be a Room")
}
@ -235,12 +207,15 @@ func Test_ChannellingAPI_OnIncoming_RoomMessage_RespondsWithAndBroadcastsTheUpda @@ -235,12 +207,15 @@ func Test_ChannellingAPI_OnIncoming_RoomMessage_RespondsWithAndBroadcastsTheUpda
}
func Test_ChannellingAPI_OnIncoming_RoomMessage_RespondsWithAnErrorIfUpdatingTheRoomFails(t *testing.T) {
iid, roomName := "123", "foo"
roomName := "foo"
api, client, session, roomManager := NewTestChannellingAPI()
roomManager.updateError = NewDataError("a_room_error", "")
api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Iid: "0", Hello: &DataHello{Id: roomName}})
api.OnIncoming(client, session, &DataIncoming{Type: "Room", Iid: iid, Room: &DataRoom{Name: roomName}})
_, err := api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Hello: &DataHello{Id: roomName}})
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
_, err = api.OnIncoming(client, session, &DataIncoming{Type: "Room", Room: &DataRoom{Name: roomName}})
assertErrorReply(t, client, iid, "a_room_error")
assertDataError(t, err, "a_room_error")
}

31
src/app/spreed-webrtc-server/client.go

@ -29,17 +29,8 @@ type Sender interface { @@ -29,17 +29,8 @@ type Sender interface {
Send(Buffer)
}
type ResponseSender interface {
Sender
Responder
}
type Responder interface {
Reply(iid string, m interface{})
}
type Client interface {
ResponseSender
Sender
Session() *Session
Index() uint64
Close()
@ -59,7 +50,11 @@ func NewClient(codec Codec, api ChannellingAPI, session *Session) *client { @@ -59,7 +50,11 @@ func NewClient(codec Codec, api ChannellingAPI, session *Session) *client {
func (client *client) OnConnect(conn Connection) {
client.Connection = conn
client.ChannellingAPI.OnConnect(client, client.session)
if reply, err := client.ChannellingAPI.OnConnect(client, client.session); err == nil {
client.reply("", reply)
} else {
log.Println("OnConnect error", err)
}
}
func (client *client) OnDisconnect() {
@ -68,14 +63,20 @@ func (client *client) OnDisconnect() { @@ -68,14 +63,20 @@ func (client *client) OnDisconnect() {
}
func (client *client) OnText(b Buffer) {
if incoming, err := client.DecodeIncoming(b); err == nil {
client.OnIncoming(client, client.session, incoming)
} else {
incoming, err := client.DecodeIncoming(b)
if err != nil {
log.Println("OnText error while processing incoming message", err)
return
}
if reply, err := client.OnIncoming(client, client.session, incoming); err != nil {
client.reply(incoming.Iid, err)
} else if reply != nil {
client.reply(incoming.Iid, reply)
}
}
func (client *client) Reply(iid string, m interface{}) {
func (client *client) reply(iid string, m interface{}) {
outgoing := &DataOutgoing{From: client.session.Id, Iid: iid, Data: m}
if b, err := client.EncodeOutgoing(outgoing); err == nil {
client.Send(b)

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

@ -22,7 +22,6 @@ @@ -22,7 +22,6 @@
package main
import (
"errors"
"fmt"
"github.com/gorilla/securecookie"
"strings"
@ -310,15 +309,18 @@ func (s *Session) Authorize(realm string, st *SessionToken) (string, error) { @@ -310,15 +309,18 @@ func (s *Session) Authorize(realm string, st *SessionToken) (string, error) {
defer s.mutex.Unlock()
if s.Id != st.Id || s.Sid != st.Sid {
return "", errors.New("session id mismatch")
return "", NewDataError("invalid_session_token", "session id mismatch")
}
if s.userid != "" {
return "", errors.New("session already authenticated")
return "", NewDataError("already_authenticated", "session already authenticated")
}
// Create authentication nonce.
var err error
s.Nonce, err = sessionNonces.Encode(fmt.Sprintf("%s@%s", s.Sid, realm), st.Userid)
if err != nil {
err = NewDataError("unknown", err.Error())
}
return s.Nonce, err
@ -330,18 +332,18 @@ func (s *Session) Authenticate(realm string, st *SessionToken, userid string) er @@ -330,18 +332,18 @@ func (s *Session) Authenticate(realm string, st *SessionToken, userid string) er
defer s.mutex.Unlock()
if s.userid != "" {
return errors.New("session already authenticated")
return NewDataError("already_authenticated", "session already authenticated")
}
if userid == "" {
if s.Nonce == "" || s.Nonce != st.Nonce {
return errors.New("nonce validation failed")
return NewDataError("invalid_session_token", "nonce validation failed")
}
err := sessionNonces.Decode(fmt.Sprintf("%s@%s", s.Sid, realm), st.Nonce, &userid)
if err != nil {
return err
return NewDataError("invalid_session_token", err.Error())
}
if st.Userid != userid {
return errors.New("user id mismatch")
return NewDataError("invalid_session_token", "user id mismatch")
}
s.Nonce = ""
}

Loading…
Cancel
Save