Send HTTP error code depending error that append.

Change message content with something more user friendly
This commit is contained in:
Chteufleur 2016-07-18 20:09:17 +02:00
parent 4a4bcb418b
commit af21b300ea
5 changed files with 77 additions and 27 deletions

View File

@ -16,7 +16,8 @@ go get git.kingpenguin.tk/chteufleur/HTTPAuthentificationOverXMPP.git
``` ```
### Configure ### Configure
Configure the gateway by editing the ``httpAuth.cfg`` file in order to give all XMPP component and HTTP server informations. An example of the config file can be found in [the repos](https://git.kingpenguin.tk/chteufleur/HTTPAuthentificationOverXMPP/src/master/httpAuth.cfg). Configure the gateway by editing the ``httpAuth.cfg`` file in order to give all XMPP component and HTTP server informations.
An example of the config file can be found in [the repos](https://git.kingpenguin.tk/chteufleur/HTTPAuthentificationOverXMPP/src/master/httpAuth.cfg).
XMPP XMPP
* xmpp_server_address : Component server address connection (default: 127.0.0.1) * xmpp_server_address : Component server address connection (default: 127.0.0.1)
@ -35,18 +36,32 @@ HTTP
### Utilization ### Utilization
To ask authorization, just send an HTTP request to the path ``/auth`` with parameters: To ask authorization, just send an HTTP request to the path ``/auth`` with parameters:
* jid : JID of the user (user@host/resource or user@host) * __jid__ : JID of the user (user@host/resource or user@host)
* domain : Domain you want to access * __domain__ : Domain you want to access
* method : Method you access the domain * __method__ : Method you access the domain
* transaction_id : Transaction identifier (auto generated if not provide) * transaction_id : Transaction identifier (auto generated if not provide)
* timeout : Timeout of the request in second (default : 60, max : 300) * timeout : Timeout of the request in second (default : 60, max : 300)
__Bold parameters__ are mandatory.
Example: Example:
``` ```
GET /auth?jid=user%40host%2fresource&domain=example.org&method=POST&transaction_id=WhatEverYouWant&timeout=120 HTTP/1.1 GET /auth?jid=user%40host%2fresource&domain=example.org&method=POST&transaction_id=WhatEverYouWant&timeout=120 HTTP/1.1
``` ```
This will send a request to the given JID. If the user accept, the server will return HTTP code 200, otherwise it will return HTTP code 401. This will send a request to the given JID, then return HTTP code depending on what appended.
* 200 : User accept the request
* 401 : User deny the request
* 504 : User do not provide an answer (timeout)
* 520 : Unknown error append
* 523 : Server is unreachable
If the provided JID contain a resource, it will try to send an ``iq`` stanza.
If the answer to this ``iq`` is a ``feature-not-implemented`` or ``service-unavailable`` error,
it will automatically send a ``message`` stanza. Unfortunately, if a ``message`` stanza is used,
their is probably no way to get the error if the JID does not exist or is unreachable.
A demo version can be found at [auth.xmpp.kingpenguin.tk](http://auth.xmpp.kingpenguin.tk) for test purpose only. A demo version can be found at [auth.xmpp.kingpenguin.tk](http://auth.xmpp.kingpenguin.tk) for test purpose only.

View File

@ -27,6 +27,9 @@ const (
RETURN_VALUE_OK = "OK" RETURN_VALUE_OK = "OK"
RETURN_VALUE_NOK = "NOK" RETURN_VALUE_NOK = "NOK"
StatusUnknownError = 520
StatusUnreachable = 523
) )
var ( var (
@ -62,7 +65,7 @@ func authHandler(w http.ResponseWriter, r *http.Request) {
timeout = MaxTimeout timeout = MaxTimeout
} }
chanAnswer := make(chan bool) chanAnswer := make(chan string)
ChanRequest <- jid ChanRequest <- jid
ChanRequest <- method ChanRequest <- method
@ -72,13 +75,21 @@ func authHandler(w http.ResponseWriter, r *http.Request) {
select { select {
case answer := <-chanAnswer: case answer := <-chanAnswer:
if answer { switch answer {
case xmpp.REPLY_OK:
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
} else {
case xmpp.REPLY_DENY:
w.WriteHeader(http.StatusUnauthorized) w.WriteHeader(http.StatusUnauthorized)
case xmpp.REPLY_UNREACHABLE:
w.WriteHeader(StatusUnreachable)
default:
w.WriteHeader(StatusUnknownError)
} }
case <-time.After(time.Duration(timeout) * time.Second): case <-time.After(time.Duration(timeout) * time.Second):
w.WriteHeader(http.StatusUnauthorized) w.WriteHeader(http.StatusGatewayTimeout)
delete(xmpp.WaitMessageAnswers, transaction) delete(xmpp.WaitMessageAnswers, transaction)
} }
} }

View File

@ -76,13 +76,8 @@ func request() {
client.Domain = getChanString(http.ChanRequest) client.Domain = getChanString(http.ChanRequest)
client.Transaction = getChanString(http.ChanRequest) client.Transaction = getChanString(http.ChanRequest)
if client.Transaction == "" {
// Random transaction ID generation
client.Transaction = xmpp.SessionID()
}
chanResult := <-http.ChanRequest chanResult := <-http.ChanRequest
if v, ok := chanResult.(chan bool); ok { if v, ok := chanResult.(chan string); ok {
client.ChanReply = v client.ChanReply = v
} }

View File

@ -10,6 +10,10 @@ import (
const ( const (
dictionary = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" dictionary = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
REPLY_UNREACHABLE = "reply_unreachable"
REPLY_DENY = "reply_deny"
REPLY_OK = "reply_ok"
) )
type Client struct { type Client struct {
@ -18,14 +22,20 @@ type Client struct {
Domain string Domain string
Transaction string Transaction string
ChanReply chan bool ChanReply chan string
} }
func (client *Client) QueryClient() { func (client *Client) QueryClient() {
log.Printf("%sQuery JID %s", LogInfo, client.JID) log.Printf("%sQuery JID %s", LogInfo, client.JID)
isAutoGeneratedTranctionID := false
if client.Transaction == "" {
// Random transaction ID generation
client.Transaction = xmpp.SessionID()
isAutoGeneratedTranctionID = true
}
clientJID, _ := xmpp.ParseJID(client.JID) clientJID, _ := xmpp.ParseJID(client.JID)
if clientJID.Resource == "" { if clientJID.Resource == "" {
client.askViaMessage() client.askViaMessage(isAutoGeneratedTranctionID)
} else { } else {
client.askViaIQ() client.askViaIQ()
} }
@ -41,16 +51,20 @@ func (client *Client) askViaIQ() {
comp.Out <- m comp.Out <- m
} }
func (client *Client) askViaMessage() { func (client *Client) askViaMessage(isAutoGeneratedTranctionID bool) {
m := xmpp.Message{From: jid.Domain, To: client.JID, Type: "normal"} m := xmpp.Message{From: jid.Domain, To: client.JID, Type: xmpp.MessageTypeNormal}
m.Thread = xmpp.SessionID() m.Thread = xmpp.SessionID()
m.Body = "HTTP (" + client.Method + ") Authorization for " + client.Domain + " (id: " + client.Transaction + ")." m.Body = client.Domain + " (with method " + client.Method + ") need to validate your identity, do you agree ?"
m.Body += "\nReply to this message to confirm the request." m.Body += "\nValidation code : " + client.Transaction
m.Body += "\nIf your client doesn't support that functionnality, please send back the identifier to confirm the request." if !isAutoGeneratedTranctionID {
// Send only if the transaction ID is not autogenerated
m.Body += "\nPlease check that this code is the same as on " + client.Domain
}
m.Body += "\n\nIf your client doesn't support that functionnality, please send back the validation code to confirm the request."
m.Confir = &xmpp.Confirm{Id: client.Transaction, Method: client.Method, URL: client.Domain} m.Confir = &xmpp.Confirm{Id: client.Transaction, Method: client.Method, URL: client.Domain}
log.Printf("%sSenp message %v", LogInfo, m) log.Printf("%sSend message %v", LogInfo, m)
WaitMessageAnswers[client.Transaction] = client WaitMessageAnswers[client.Transaction] = client
comp.Out <- m comp.Out <- m
} }

View File

@ -116,17 +116,32 @@ func processConfirm(x interface{}, client *Client) {
if client != nil { if client != nil {
if mesOK && mes.Error != nil { if mesOK && mes.Error != nil {
client.ChanReply <- false // Message error
errCondition := mes.Error.Condition()
if errCondition == xmpp.ServiceUnavailable {
// unreachable
client.ChanReply <- REPLY_UNREACHABLE
} else {
client.ChanReply <- REPLY_DENY
}
} else if iqOK && iq.Error != nil { } else if iqOK && iq.Error != nil {
if iq.Error.Condition().Local == "service-unavailable" { // IQ error
errCondition := iq.Error.Condition()
if errCondition == xmpp.ServiceUnavailable || errCondition == xmpp.FeatureNotImplemented {
// send by message if client doesn't implemente it // send by message if client doesn't implemente it
client.JID = strings.SplitN(client.JID, "/", 2)[0] client.JID = strings.SplitN(client.JID, "/", 2)[0]
go client.QueryClient() go client.QueryClient()
} else if errCondition == xmpp.RemoteServerNotFound {
// unreachable
client.ChanReply <- REPLY_UNREACHABLE
} else { } else {
client.ChanReply <- false client.ChanReply <- REPLY_DENY
} }
} else { } else {
client.ChanReply <- true // No error
client.ChanReply <- REPLY_OK
} }
} }
} }