Compare commits
34 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
ea4f78e23b | |
|
|
8384410023 | |
|
|
5fa7fe5235 | |
|
|
a330e7df29 | |
|
|
a00eb69071 | |
|
|
6337ad74af | |
|
|
ce6a3a26af | |
|
|
a85f476600 | |
|
|
2f301ffc80 | |
|
|
c146c8dae2 | |
|
|
bed3e755f7 | |
|
|
90269e40e5 | |
|
|
88f7537bbd | |
|
|
dfa6cd1b32 | |
|
|
39d865b082 | |
|
|
a6f6ef86ac | |
|
|
a070b03c0f | |
|
|
a06062e714 | |
|
|
b61490b75c | |
|
|
89efb0137b | |
|
|
c788a29d46 | |
|
|
0b0611fc45 | |
|
|
a1cd475788 | |
|
|
178a9b4796 | |
|
|
7da25057cf | |
|
|
8847f220fd | |
|
|
4a5ff9f227 | |
|
|
2b101c1c1d | |
|
|
b63a944cb2 | |
|
|
e2fbb6c8e3 | |
|
|
7e9fd69879 | |
|
|
78c8aecb8e | |
|
|
c88ec7a32d | |
|
|
20dc1ee9e8 |
44
README.md
44
README.md
|
|
@ -1,52 +1,70 @@
|
||||||
# HTTPAuthentificationOverXMPP
|
# HTTPAuthenticationOverXMPP
|
||||||
|
|
||||||
Provide an HTTP anthentification over XMPP. Implementation of [XEP-0070](https://xmpp.org/extensions/xep-0070.html).
|
Provide an HTTP authentication over XMPP. Implementation of [XEP-0070](https://xmpp.org/extensions/xep-0070.html).
|
||||||
|
|
||||||
|
Can be run as a XMPP client or XMPP component.
|
||||||
|
|
||||||
|
|
||||||
## Compilation
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* [go-xmpp](https://git.kingpenguin.tk/chteufleur/go-xmpp) for the XMPP part.
|
* [go-xmpp](https://git.kingpenguin.tk/chteufleur/go-xmpp) for the XMPP part.
|
||||||
* [cfg](https://github.com/jimlawless/cfg) for the configuration file.
|
* [cfg](https://github.com/jimlawless/cfg) for the configuration file.
|
||||||
|
|
||||||
|
### Build and run
|
||||||
|
|
||||||
You must first [install go environment](https://golang.org/doc/install) on your system.
|
You must first [install go environment](https://golang.org/doc/install) on your system.
|
||||||
Then, go into your $GOPATH directory and go get the source code.
|
Then, go into your $GOPATH directory and go get the source code.
|
||||||
```sh
|
```sh
|
||||||
go get git.kingpenguin.tk/chteufleur/HTTPAuthentificationOverXMPP.git
|
go get git.kingpenguin.tk/chteufleur/HTTPAuthentificationOverXMPP.git
|
||||||
```
|
```
|
||||||
|
|
||||||
|
First, you need to go into directory ``$GOPATH/src/chteufleur/HTTPAuthentificationOverXMPP.git``.
|
||||||
|
Then, you can run the project directly by using command ``go run main.go``.
|
||||||
|
Or, in order to build the project you can run the command ``go build main.go``.
|
||||||
|
It will generate a binary that you can run as any binary file.
|
||||||
|
|
||||||
### Configure
|
### Configure
|
||||||
Configure the gateway by editing the ``httpAuth.cfg`` file in order to give all XMPP component and HTTP server informations.
|
Configure the gateway by editing the ``httpAuth.conf`` file in order to give all XMPP and HTTP server informations. This configuration file has to be placed following the [XDG specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html) (example ``/etc/xdg/http-auth/httpAuth.conf``).
|
||||||
An example of the config file can be found in [the repos](https://git.kingpenguin.tk/chteufleur/HTTPAuthentificationOverXMPP/src/master/httpAuth.cfg).
|
An example of the config file can be found in [the repos](https://git.kingpenguin.tk/chteufleur/HTTPAuthentificationOverXMPP/src/master/httpAuth.conf).
|
||||||
|
|
||||||
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)
|
||||||
* xmpp_server_port : Component server port connection (default: 5347)
|
* xmpp_server_port : Component server port connection (default: 5347)
|
||||||
* xmpp_hostname : Component hostname
|
* __xmpp_jid__ : Account JID
|
||||||
* xmpp_secret : Component password
|
* __xmpp_secret__ : Account password
|
||||||
* xmpp_debug : Enable debug log at true (default: false)
|
* xmpp_debug : Enable debug log at true (default: false)
|
||||||
|
* xmpp_verify_cert_validity : Enable certificate verification (default: true)
|
||||||
|
* xmpp_default_lang : Message default languages
|
||||||
|
|
||||||
HTTP
|
HTTP
|
||||||
* http_port : HTTP port to bind (default: 9090, desactive: -1)
|
* http_port : HTTP port to bind (default: -1, desactive: -1)
|
||||||
* https_port : HTTPS port to bind (default: 9093, desactive: -1)
|
* https_port : HTTPS port to bind (default: -1, desactive: -1)
|
||||||
* https_cert_path : Path to the certificate file (default: ./cert.pem)
|
* https_cert_path : Path to the certificate file (default: ./cert.pem)
|
||||||
* https_key_path : Path to the key file (default: ./key.pem)
|
* https_key_path : Path to the key file (default: ./key.pem)
|
||||||
* http_timeoute_sec : Define a timeout if user did not give an answer to the request (default: 60)
|
* http_timeout_sec : Define a timeout if user did not give an answer to the request (default: 60)
|
||||||
|
* http_bind_address_ipv4 : Bind address on IPv4 (default: 127.0.0.1)
|
||||||
|
* http_bind_address_ipv6 : Bind address on IPv6 (default: [::1])
|
||||||
|
|
||||||
|
__Bold config__ are mandatory.
|
||||||
|
|
||||||
### Utilization
|
If ``http_bind_address_ipv4`` is set to ``0.0.0.0``, it will bind all address on IPv4 __AND__ IPv6.
|
||||||
|
|
||||||
|
The lang messages file must be placed into the same directory than the configuration file.
|
||||||
|
An example of this file can be found in [the repos](https://git.kingpenguin.tk/chteufleur/HTTPAuthentificationOverXMPP/src/master/messages.lang)
|
||||||
|
|
||||||
|
### Usage
|
||||||
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.
|
__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, then return HTTP code depending on what appended.
|
This will send a request to the given JID, then return HTTP code depending on what appended.
|
||||||
|
|
|
||||||
22
cert.pem
22
cert.pem
|
|
@ -1,22 +0,0 @@
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIDmjCCAoICCQDTJ1wt8ibb0DANBgkqhkiG9w0BAQsFADCBjjELMAkGA1UEBhMC
|
|
||||||
RlIxEzARBgNVBAgMClNvbWUtU3RhdGUxEDAOBgNVBAcMB1ZhbGVuY2UxITAfBgNV
|
|
||||||
BAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEUMBIGA1UEAwwLZXhhbXBsZS5v
|
|
||||||
cmcxHzAdBgkqhkiG9w0BCQEWEHRvdG9AZXhhbXBsZS5vcmcwHhcNMTYwNzE0MDgx
|
|
||||||
MDA3WhcNMTcwNzE0MDgxMDA3WjCBjjELMAkGA1UEBhMCRlIxEzARBgNVBAgMClNv
|
|
||||||
bWUtU3RhdGUxEDAOBgNVBAcMB1ZhbGVuY2UxITAfBgNVBAoMGEludGVybmV0IFdp
|
|
||||||
ZGdpdHMgUHR5IEx0ZDEUMBIGA1UEAwwLZXhhbXBsZS5vcmcxHzAdBgkqhkiG9w0B
|
|
||||||
CQEWEHRvdG9AZXhhbXBsZS5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
|
|
||||||
AoIBAQCjNtyD6vXdVSj+Vmin/1t4JApafQ4475oJsvsNfd2cMgQ9856RZPyZFCCe
|
|
||||||
9veffUSV9ffYcgtPF8ZfRkOLZvSzYNYrrgI+Qsp2Y/Mw1hAupn2IadjdB0ZAFpZi
|
|
||||||
fnH5tuXSIiPbrl1sQZxSdIhgRPdj6scnBFwjbbm+DfyQYvjtm5LTOYbNOc/Sali+
|
|
||||||
lSdC22Z69nL7rscg3LFeBb8Oqx6MU8cvQ/nNsfxP+Ynimon3E28mP8VftyL81J6z
|
|
||||||
g8H6ly9R3kkapPrUg3CWP1z5rya+MdujcxkhEfO9oybSokaU/VkpMfrM4iBjs9m5
|
|
||||||
+hU3kWfVqB38OYUVEz2GagWrvDdJAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAD3/
|
|
||||||
fkCOvAhy38R10IgYEVH8MlrG3Rpy7CCYhUPgBrdFRgFAhwegDSc5my4rgiGmETkX
|
|
||||||
SlEppuHyoMNe8Sv70ibflqrCcwMPz/BWRlTDSWdwUqNjr4RfOT6LrO/9bZQT4tV8
|
|
||||||
Av4smslUaXiKuFyJLY1uFXbw2BqZTQQgaDQGdaqRNkCPRT00+VUbzR/qOEQ0Kv0U
|
|
||||||
QAX1tN1kmrJo5ccGB9WVvijp1ZlimMjtvh5v7Uxa6Wl4l7D9B1+5glEhPKunOlcK
|
|
||||||
MAHrHIuBRuuQUmtdk9zj3HYZtniChflPUO3+QWr3nfA6IhVEvz1wa4BQFzfwm4NY
|
|
||||||
eFcBWz6pr3zuawKjUlk=
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
73
http/http.go
73
http/http.go
|
|
@ -5,6 +5,7 @@ import (
|
||||||
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -28,23 +29,31 @@ const (
|
||||||
RETURN_VALUE_OK = "OK"
|
RETURN_VALUE_OK = "OK"
|
||||||
RETURN_VALUE_NOK = "NOK"
|
RETURN_VALUE_NOK = "NOK"
|
||||||
|
|
||||||
|
MAX_PORT_VAL = 65535
|
||||||
|
|
||||||
StatusUnknownError = 520
|
StatusUnknownError = 520
|
||||||
StatusUnreachable = 523
|
StatusUnreachable = 523
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
HttpPortBind = 9090
|
HttpPortBind = -1
|
||||||
HttpsPortBind = 9093
|
HttpsPortBind = -1
|
||||||
CertPath = "./cert.pem"
|
CertPath = "./cert.pem"
|
||||||
KeyPath = "./key.pem"
|
KeyPath = "./key.pem"
|
||||||
|
|
||||||
ChanRequest = make(chan interface{}, 5)
|
ChanRequest = make(chan interface{}, 5)
|
||||||
TimeoutSec = 60 // 1 min
|
TimeoutSec = 60 // 1 min
|
||||||
MaxTimeout = 300 // 5 min
|
MaxTimeout = 300 // 5 min
|
||||||
|
|
||||||
|
BindAddressIPv4 = "127.0.0.1"
|
||||||
|
BindAddressIPv6 = "[::1]"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rand.Seed(time.Now().UTC().UnixNano())
|
||||||
|
}
|
||||||
|
|
||||||
func indexHandler(w http.ResponseWriter, r *http.Request) {
|
func indexHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
// TODO
|
|
||||||
fmt.Fprintf(w, "Welcome to HTTP authentification over XMPP")
|
fmt.Fprintf(w, "Welcome to HTTP authentification over XMPP")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,15 +62,15 @@ func authHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
jid := strings.Join(r.Form[PARAM_JID], "")
|
jid := strings.Join(r.Form[PARAM_JID], "")
|
||||||
method := strings.Join(r.Form[METHOD_ACCESS], "")
|
method := strings.Join(r.Form[METHOD_ACCESS], "")
|
||||||
domain := strings.Join(r.Form[DOMAIN_ACCESS], "")
|
domain := strings.Join(r.Form[DOMAIN_ACCESS], "")
|
||||||
|
transaction := strings.Join(r.Form[TRANSACTION_ID], "")
|
||||||
|
|
||||||
if jid == "" || method == "" || domain == "" {
|
if jid == "" || method == "" || domain == "" || transaction == "" {
|
||||||
// If mandatory params is missing
|
// If mandatory params is missing
|
||||||
log.Printf("%sMandatory params is missing", LogInfo)
|
log.Printf("%sMandatory params is missing", LogInfo)
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
transaction := strings.Join(r.Form[TRANSACTION_ID], "")
|
|
||||||
timeoutStr := strings.Join(r.Form[TIMEOUTE], "")
|
timeoutStr := strings.Join(r.Form[TIMEOUTE], "")
|
||||||
log.Printf("%sAuth %s", LogInfo, jid)
|
log.Printf("%sAuth %s", LogInfo, jid)
|
||||||
timeout, err := strconv.Atoi(timeoutStr)
|
timeout, err := strconv.Atoi(timeoutStr)
|
||||||
|
|
@ -74,13 +83,13 @@ func authHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
chanAnswer := make(chan string)
|
chanAnswer := make(chan string)
|
||||||
|
|
||||||
client := new(xmpp.Client)
|
confirmation := new(xmpp.Confirmation)
|
||||||
client.JID = jid
|
confirmation.JID = jid
|
||||||
client.Method = method
|
confirmation.Method = method
|
||||||
client.Domain = domain
|
confirmation.Domain = domain
|
||||||
client.Transaction = transaction
|
confirmation.Transaction = transaction
|
||||||
client.ChanReply = chanAnswer
|
confirmation.ChanReply = chanAnswer
|
||||||
client.QueryClient()
|
confirmation.SendConfirmation()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case answer := <-chanAnswer:
|
case answer := <-chanAnswer:
|
||||||
|
|
@ -101,14 +110,14 @@ func authHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(http.StatusUnauthorized)
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch client.TypeSend {
|
switch confirmation.TypeSend {
|
||||||
case xmpp.TYPE_SEND_IQ:
|
case xmpp.TYPE_SEND_IQ:
|
||||||
log.Printf("%sDelete IQ", LogDebug)
|
log.Printf("%sDelete IQ", LogDebug)
|
||||||
delete(xmpp.WaitIqMessages, client.IdMap)
|
delete(xmpp.WaitIqMessages, confirmation.IdMap)
|
||||||
|
|
||||||
case xmpp.TYPE_SEND_MESSAGE:
|
case xmpp.TYPE_SEND_MESSAGE:
|
||||||
log.Printf("%sDelete Message", LogDebug)
|
log.Printf("%sDelete Message", LogDebug)
|
||||||
delete(xmpp.WaitMessageAnswers, client.IdMap)
|
delete(xmpp.WaitMessageAnswers, confirmation.IdMap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -119,26 +128,44 @@ func Run() {
|
||||||
http.HandleFunc(ROUTE_AUTH, authHandler)
|
http.HandleFunc(ROUTE_AUTH, authHandler)
|
||||||
|
|
||||||
if HttpPortBind > 0 {
|
if HttpPortBind > 0 {
|
||||||
go runHttp()
|
go runHttp(BindAddressIPv4)
|
||||||
|
if BindAddressIPv4 != "0.0.0.0" {
|
||||||
|
go runHttp(BindAddressIPv6)
|
||||||
|
}
|
||||||
|
} else if HttpPortBind == 0 {
|
||||||
|
HttpPortBind = rand.Intn(MAX_PORT_VAL)
|
||||||
|
go runHttp(BindAddressIPv4)
|
||||||
|
if BindAddressIPv4 != "0.0.0.0" {
|
||||||
|
go runHttp(BindAddressIPv6)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if HttpsPortBind > 0 {
|
if HttpsPortBind > 0 {
|
||||||
go runHttps()
|
go runHttps(BindAddressIPv4)
|
||||||
|
if BindAddressIPv6 != "0.0.0.0" {
|
||||||
|
go runHttps(BindAddressIPv6)
|
||||||
|
}
|
||||||
|
} else if HttpsPortBind == 0 {
|
||||||
|
HttpsPortBind = rand.Intn(MAX_PORT_VAL)
|
||||||
|
go runHttps(BindAddressIPv4)
|
||||||
|
if BindAddressIPv6 != "0.0.0.0" {
|
||||||
|
go runHttps(BindAddressIPv6)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func runHttp() {
|
func runHttp(bindAddress string) {
|
||||||
port := strconv.Itoa(HttpPortBind)
|
port := strconv.Itoa(HttpPortBind)
|
||||||
log.Printf("%sHTTP listenning on port %s", LogInfo, port)
|
log.Printf("%sHTTP listenning on %s:%s", LogInfo, bindAddress, port)
|
||||||
err := http.ListenAndServe(":"+port, nil)
|
err := http.ListenAndServe(bindAddress+":"+port, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("%sListenAndServe: ", LogError, err)
|
log.Fatal("%sListenAndServe: ", LogError, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func runHttps() {
|
func runHttps(bindAddress string) {
|
||||||
port := strconv.Itoa(HttpsPortBind)
|
port := strconv.Itoa(HttpsPortBind)
|
||||||
log.Printf("%sHTTPS listenning on port %s", LogInfo, port)
|
log.Printf("%sHTTPS listenning on %s:%s", LogInfo, bindAddress, port)
|
||||||
err := http.ListenAndServeTLS(":"+port, CertPath, KeyPath, nil)
|
err := http.ListenAndServeTLS(bindAddress+":"+port, CertPath, KeyPath, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("%sListenAndServe: ", LogError, err)
|
log.Fatal("%sListenAndServe: ", LogError, err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
# XMPP informations
|
# XMPP informations (component)
|
||||||
xmpp_server_address=192.168.1.2
|
xmpp_server_address=192.168.1.2
|
||||||
xmpp_server_port=5347
|
xmpp_server_port=5347
|
||||||
xmpp_hostname=xmppsteam.kingpenguin.tk
|
xmpp_jid=xmppsteam.kingpenguin.tk
|
||||||
xmpp_secret=xmpp4steam_password
|
xmpp_secret=xmpp4steam_password
|
||||||
xmpp_debug=true
|
xmpp_debug=true
|
||||||
|
xmpp_verify_cert_validity=true
|
||||||
|
|
||||||
# HTTP informations
|
# HTTP informations
|
||||||
|
http_bind_address_ipv4=127.0.0.1
|
||||||
|
http_bind_address_ipv6=[::1]
|
||||||
http_port=9090
|
http_port=9090
|
||||||
https_port=9093
|
https_port=9093
|
||||||
https_cert_path=./cert.pem
|
https_cert_path=./cert.pem
|
||||||
https_key_path=./key.pem
|
https_key_path=./key.pem
|
||||||
http_timeoute_sec=60
|
http_timeout_sec=60
|
||||||
27
key.pem
27
key.pem
|
|
@ -1,27 +0,0 @@
|
||||||
-----BEGIN RSA PRIVATE KEY-----
|
|
||||||
MIIEpQIBAAKCAQEAozbcg+r13VUo/lZop/9beCQKWn0OOO+aCbL7DX3dnDIEPfOe
|
|
||||||
kWT8mRQgnvb3n31ElfX32HILTxfGX0ZDi2b0s2DWK64CPkLKdmPzMNYQLqZ9iGnY
|
|
||||||
3QdGQBaWYn5x+bbl0iIj265dbEGcUnSIYET3Y+rHJwRcI225vg38kGL47ZuS0zmG
|
|
||||||
zTnP0mpYvpUnQttmevZy+67HINyxXgW/DqsejFPHL0P5zbH8T/mJ4pqJ9xNvJj/F
|
|
||||||
X7ci/NSes4PB+pcvUd5JGqT61INwlj9c+a8mvjHbo3MZIRHzvaMm0qJGlP1ZKTH6
|
|
||||||
zOIgY7PZufoVN5Fn1agd/DmFFRM9hmoFq7w3SQIDAQABAoIBAQCAI6lbVJP1Uk/d
|
|
||||||
5v9BrkUk/L64LmiFIPAB32glPoVHhSk5blQ2+F8s29WEmIbuy42WYsdUQq1ISnUv
|
|
||||||
Bd4vywQg9M0Q/Au8z/lem7gpxlZsGcCC4f8mAPkRhepJp9ZZ5FNo9+7JIYstXBGb
|
|
||||||
1uvfESZdZs02f8DK+/GRGjAJN/sRqAo4MvwZzrX9DIMnTt5MiujCAWOMX4rXT316
|
|
||||||
epSyUAxMnNzC3On32TmGqrHXTF7KMGPjbSupAHDS4F8iL9ntFF2QPcW5um8jEneZ
|
|
||||||
ln/oJo2+2LYFJPcj0BbvCAcRaIumvHNQgfC0ispStCR37IlEZKyQXUtX/z/BPQO/
|
|
||||||
d8KzylqRAoGBANl1tMVz4d20kkQzyXOCmQ7C/y98AHWDiFH2Iq/QMV21TFomit66
|
|
||||||
SR8RVwWH3G6C8cQRHFa504iDP3Lo/jq+rf4SMwEJq+X5aVkThCq5DDZech/qslyF
|
|
||||||
FTA4IIkcqxF5/UOQg3UxGKU2I7mKLLgnhaPvPqcd7t/PzXAgJqV45ElFAoGBAMAk
|
|
||||||
AHPZFpb5IMPbVSen/jZ6cbNtRsOyUv2Vlz+Pkjbfl0SsxOInPZxwwEcEgLC2micN
|
|
||||||
CH1rTZXpMLKOr/VBdkbp4uMMR8X2kro02b9zlmBmAXleyuAPbQAbRn4epnnfzqxb
|
|
||||||
TcJNHQJT4uQRhtshTQ2GqnHXei/ZMBrI35VCN5w1AoGBAM9c3LqE3Fbrv6Zls64A
|
|
||||||
VS+sZmbDWjS07qMpkL4SS2DOZzZ4Fmh5PwzvHgpaGasQFrcekeVpYfuFHFXZM8SU
|
|
||||||
25mxhQ1ySYcNJJYadCfBOZIG0dD5nod3KFNI0k2tFrudlhJ9lb2EybmRPNPKnQYm
|
|
||||||
OduvYhE+C/FEWOSY5AFanGX5AoGARxWKrVFlUBl/C7a7fF5kaFdIdW86PPBeT77m
|
|
||||||
I/fDylVSK3AXruuBmb0FBcEes0H7KfNibrQiEhIhmA29/2hmj7m73PAQJachhY5D
|
|
||||||
+NaUjblvVi3BtL9APkfY/pPsVy570bw9umK5FsFeMa5iS/O4BAcMS+3CIK2jZGVo
|
|
||||||
glnrJPkCgYEAiI0nFwbd5oP4bP41zDKU7lrnwrmbCKZr5ZYScw1NtSqKo7aJnhEi
|
|
||||||
fBYMT8aXHVt4ALcI/VzR8LnoTQcJ6Pibn0pZ91sE8FRk99qfnF0rK4LLKF6R+UJH
|
|
||||||
X+dmoMOcHQ9MEyPhNvdgsTo72KMPzJn8q58VTq/6I4we+VCOlZNwlCA=
|
|
||||||
-----END RSA PRIVATE KEY-----
|
|
||||||
84
main.go
84
main.go
|
|
@ -10,16 +10,17 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Version = "v0.3.1"
|
Version = "v0.5-dev"
|
||||||
configurationFilePath = "httpAuth.cfg"
|
configurationFilePath = "http-auth/httpAuth.conf"
|
||||||
|
langFilePath = "http-auth/messages.lang"
|
||||||
default_xmpp_server_address = "127.0.0.1"
|
PathConfEnvVariable = "XDG_CONFIG_DIRS"
|
||||||
default_xmpp_server_port = "5347"
|
DefaultXdgConfigDirs = "/etc/xdg"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -29,13 +30,13 @@ var (
|
||||||
func init() {
|
func init() {
|
||||||
log.Printf("Running HTTP-Auth %v", Version)
|
log.Printf("Running HTTP-Auth %v", Version)
|
||||||
|
|
||||||
err := cfg.Load(configurationFilePath, mapConfig)
|
if !loadConfigFile() {
|
||||||
if err != nil {
|
log.Fatal("Failed to load configuration file.")
|
||||||
log.Fatal("Failed to load configuration file.", err)
|
|
||||||
}
|
}
|
||||||
|
loadLangFile()
|
||||||
|
|
||||||
// HTTP config
|
// HTTP config
|
||||||
httpTimeout, err := strconv.Atoi(mapConfig["http_timeoute_sec"])
|
httpTimeout, err := strconv.Atoi(mapConfig["http_timeout_sec"])
|
||||||
if err == nil && httpTimeout > 0 && httpTimeout < http.MaxTimeout {
|
if err == nil && httpTimeout > 0 && httpTimeout < http.MaxTimeout {
|
||||||
log.Println("Define HTTP timeout to " + strconv.Itoa(httpTimeout) + " second")
|
log.Println("Define HTTP timeout to " + strconv.Itoa(httpTimeout) + " second")
|
||||||
http.TimeoutSec = httpTimeout
|
http.TimeoutSec = httpTimeout
|
||||||
|
|
@ -52,28 +53,71 @@ func init() {
|
||||||
http.CertPath = mapConfig["https_cert_path"]
|
http.CertPath = mapConfig["https_cert_path"]
|
||||||
http.KeyPath = mapConfig["https_key_path"]
|
http.KeyPath = mapConfig["https_key_path"]
|
||||||
}
|
}
|
||||||
|
bindAddressIPv4 := mapConfig["http_bind_address_ipv4"]
|
||||||
|
if bindAddressIPv4 != "" {
|
||||||
|
http.BindAddressIPv4 = bindAddressIPv4
|
||||||
|
}
|
||||||
|
bindAddressIPv6 := mapConfig["http_bind_address_ipv6"]
|
||||||
|
if bindAddressIPv6 != "" {
|
||||||
|
http.BindAddressIPv6 = bindAddressIPv6
|
||||||
|
}
|
||||||
|
|
||||||
// XMPP config
|
// XMPP config
|
||||||
xmpp_server_address := mapConfig["xmpp_server_address"]
|
xmpp_server_address := mapConfig["xmpp_server_address"]
|
||||||
if xmpp_server_address == "" {
|
if xmpp_server_address != "" {
|
||||||
xmpp_server_address = default_xmpp_server_address
|
xmpp.Addr = xmpp_server_address
|
||||||
}
|
}
|
||||||
xmpp_server_port := mapConfig["xmpp_server_port"]
|
xmpp_server_port := mapConfig["xmpp_server_port"]
|
||||||
if xmpp_server_port == "" {
|
if xmpp_server_port != "" {
|
||||||
xmpp_server_port = default_xmpp_server_port
|
xmpp.Port = xmpp_server_port
|
||||||
}
|
}
|
||||||
|
|
||||||
xmpp.Addr = xmpp_server_address + ":" + xmpp_server_port
|
xmpp.JidStr = mapConfig["xmpp_jid"]
|
||||||
xmpp.JidStr = mapConfig["xmpp_hostname"]
|
|
||||||
xmpp.Secret = mapConfig["xmpp_secret"]
|
xmpp.Secret = mapConfig["xmpp_secret"]
|
||||||
xmpp.Debug = mapConfig["xmpp_debug"] == "true"
|
xmpp.Debug = mapConfig["xmpp_debug"] == "true"
|
||||||
|
xmpp.VerifyCertValidity = mapConfig["xmpp_verify_cert_validity"] != "false" // Default TRUE
|
||||||
}
|
}
|
||||||
|
|
||||||
func getChanString(c chan interface{}) string {
|
func loadConfigFile() bool {
|
||||||
ret := ""
|
ret := false
|
||||||
i := <-c
|
envVariable := os.Getenv(PathConfEnvVariable)
|
||||||
if v, ok := i.(string); ok {
|
if envVariable == "" {
|
||||||
ret = v
|
envVariable = DefaultXdgConfigDirs
|
||||||
|
}
|
||||||
|
for _, path := range strings.Split(envVariable, ":") {
|
||||||
|
log.Println("Try to find configuration file into " + path)
|
||||||
|
configFile := path + "/" + configurationFilePath
|
||||||
|
if _, err := os.Stat(configFile); err == nil {
|
||||||
|
// The config file exist
|
||||||
|
if cfg.Load(configFile, mapConfig) == nil {
|
||||||
|
// And has been loaded succesfully
|
||||||
|
log.Println("Find configuration file at " + configFile)
|
||||||
|
ret = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadLangFile() bool {
|
||||||
|
ret := false
|
||||||
|
envVariable := os.Getenv(PathConfEnvVariable)
|
||||||
|
if envVariable == "" {
|
||||||
|
envVariable = DefaultXdgConfigDirs
|
||||||
|
}
|
||||||
|
for _, path := range strings.Split(envVariable, ":") {
|
||||||
|
log.Println("Try to find messages lang file into " + path)
|
||||||
|
langFile := path + "/" + langFilePath
|
||||||
|
if _, err := os.Stat(langFile); err == nil {
|
||||||
|
// The config file exist
|
||||||
|
if cfg.Load(langFile, xmpp.MapLangs) == nil {
|
||||||
|
// And has been loaded succesfully
|
||||||
|
log.Println("Find messages lang file at " + langFile)
|
||||||
|
ret = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
en=_DOMAIN_ (with method _METHOD_) need to validate your identity, do you agree?\nValidation code: _VALIDE_CODE_\nPlease check that this code is the same as on _DOMAIN_.\n\nIf your client doesn't support that functionality, please send back the validation code to confirm the request.
|
||||||
|
fr=_DOMAIN_ (avec la methode _METHOD_) a besoin de valider votre identitée, acceptez-vous ?\nCode de validation : _VALIDE_CODE_\nVeuillez vérifier que ce code est le même que sur _DOMAIN_.\n\nSi votre client ne support pas cette fonctionnalitée, veuilez renvoyer le code de validation pour confirmer la requête.
|
||||||
|
ca=_DOMAIN_ (via _METHOD_) validarà la teva identitat, hi estàs d'acord?\nCodi de validació: _VALIDE_CODE_ \nComprova que aquest codi sigui el mateix que a _DOMAIN_.\n\nSi la teva aplicació no contempla aquesta funcionalitat, respon a aquest missatge amb el codi per a confirmar la petició.
|
||||||
|
es=_DOMAIN_ (via _METHOD_) validará tu identitad, estás de acuerdo?\nCódigo de validación: _VALIDE_CODE_ \nComprueba que el código sea el mismo que en _DOMAIN_.\n\nSi tu aplicación no contempla esta funcionalidad, responde a este mensaje con el código para confirmar la petición.
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
# Packaging
|
||||||
|
|
||||||
|
## Build libraries as shared
|
||||||
|
|
||||||
|
In order to packaged the project, you will need to follow those different steps.
|
||||||
|
We will build libraries as shared libraries.
|
||||||
|
|
||||||
|
The first step is to buid the GO standard library as shared.
|
||||||
|
```
|
||||||
|
$ cd $GOPATH
|
||||||
|
$ go install -buildmode=shared std
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, we will build the 2 libraries to make them shared…
|
||||||
|
```
|
||||||
|
$ cd $GOPATH
|
||||||
|
$ go install -buildmode=shared -linkshared github.com/jimlawless/cfg
|
||||||
|
$ go install -buildmode=shared -linkshared git.kingpenguin.tk/chteufleur/go-xmpp.git/src/xmpp
|
||||||
|
```
|
||||||
|
|
||||||
|
Then finally, build the project with the option that tell to use libraries as shared and not static.
|
||||||
|
```
|
||||||
|
$ cd $GOPATH
|
||||||
|
$ go install -linkshared git.kingpenguin.tk/chteufleur/HTTPAuthentificationOverXMPP.git
|
||||||
|
```
|
||||||
|
|
||||||
|
You will find shared libraries over here :
|
||||||
|
* ``$GOROOT/pkg/linux_amd64_dynlink/libstd.so``
|
||||||
|
* ``$GOPATH/pkg/linux_amd64_dynlink/libgithub.com-jimlawless-cfg.so``
|
||||||
|
* ``$GOPATH/pkg/linux_amd64_dynlink/libgit.kingpenguin.tk-chteufleur-go-xmpp.git-src-xmpp.so``
|
||||||
|
|
||||||
|
And the binary file here :
|
||||||
|
* ``$GOPATH/bin/HTTPAuthentificationOverXMPP.git``
|
||||||
|
|
||||||
|
An example can be found [here](https://github.com/jbuberel/buildmodeshared/tree/master/gofromgo).
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
At the installation, the folder ``$XDG_CONFIG_DIRS/http-auth/`` need to be created (example ``/etc/xdg/http-auth``) where to place the configuration file named ``httpAuth.conf`` and the file lang configuration named ``messages.lang``.
|
||||||
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
[Unit]
|
||||||
|
Description=Provide an HTTP authentication over XMPP (https://xmpp.org/extensions/xep-0070.html)
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=/etc/init.d/http-auth.sh start
|
||||||
|
ExecStop=/etc/init.d/http-auth.sh stop
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
#######################
|
||||||
|
name="httpAuth"
|
||||||
|
dirPath="/usr/local/sbin"
|
||||||
|
#######################
|
||||||
|
sh="/bin/bash"
|
||||||
|
pidPath="/var/run/http-auth/http-auth.pid"
|
||||||
|
#######################
|
||||||
|
|
||||||
|
do_start() {
|
||||||
|
nohup $dirPath/$name &
|
||||||
|
/bin/echo $! > $pidPath
|
||||||
|
}
|
||||||
|
|
||||||
|
do_stop() {
|
||||||
|
if [ -e $pidPath ]
|
||||||
|
then
|
||||||
|
pid=$(/bin/cat $pidPath)
|
||||||
|
kill $pid
|
||||||
|
rm $pidPath
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
get_status() {
|
||||||
|
if [ -e $pidPath ] ; then
|
||||||
|
pid=$(/bin/cat $pidPath) ;
|
||||||
|
/bin/echo "$name is running with PID $pid" ;
|
||||||
|
else
|
||||||
|
/bin/echo "$name is stopped" ;
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
case $1 in
|
||||||
|
start)
|
||||||
|
do_start
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
do_stop
|
||||||
|
;;
|
||||||
|
restart)
|
||||||
|
do_stop
|
||||||
|
do_start
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
get_status
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
/bin/echo "Usage: $0 {start|stop|restart|status}"
|
||||||
|
exit 2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
@ -1,93 +0,0 @@
|
||||||
package xmpp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.kingpenguin.tk/chteufleur/go-xmpp.git/src/xmpp"
|
|
||||||
|
|
||||||
"crypto/rand"
|
|
||||||
"log"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
dictionary = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
|
||||||
|
|
||||||
REPLY_UNREACHABLE = "reply_unreachable"
|
|
||||||
REPLY_DENY = "reply_deny"
|
|
||||||
REPLY_OK = "reply_ok"
|
|
||||||
|
|
||||||
TYPE_SEND_MESSAGE = "type_send_message"
|
|
||||||
TYPE_SEND_IQ = "type_send_iq"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Client struct {
|
|
||||||
JID string
|
|
||||||
Method string
|
|
||||||
Domain string
|
|
||||||
Transaction string
|
|
||||||
|
|
||||||
TypeSend string
|
|
||||||
IdMap string
|
|
||||||
|
|
||||||
ChanReply chan string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *Client) QueryClient() {
|
|
||||||
log.Printf("%sQuery JID %s", LogInfo, client.JID)
|
|
||||||
isAutoGeneratedTranctionID := false
|
|
||||||
if client.Transaction == "" {
|
|
||||||
// Random transaction ID generation
|
|
||||||
client.Transaction = TransactionID()
|
|
||||||
isAutoGeneratedTranctionID = true
|
|
||||||
}
|
|
||||||
clientJID, _ := xmpp.ParseJID(client.JID)
|
|
||||||
if clientJID.Resource == "" {
|
|
||||||
client.askViaMessage(isAutoGeneratedTranctionID)
|
|
||||||
} else {
|
|
||||||
client.askViaIQ()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *Client) askViaIQ() {
|
|
||||||
stanzaID++
|
|
||||||
stanzaIDstr := strconv.Itoa(stanzaID)
|
|
||||||
m := xmpp.Iq{Type: xmpp.IQTypeGet, To: client.JID, From: jid.Domain, Id: stanzaIDstr}
|
|
||||||
confirm := &xmpp.Confirm{Id: client.Transaction, Method: client.Method, URL: client.Domain}
|
|
||||||
m.PayloadEncode(confirm)
|
|
||||||
WaitIqMessages[stanzaIDstr] = client
|
|
||||||
comp.Out <- m
|
|
||||||
|
|
||||||
client.TypeSend = TYPE_SEND_IQ
|
|
||||||
client.IdMap = stanzaIDstr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *Client) askViaMessage(isAutoGeneratedTranctionID bool) {
|
|
||||||
m := xmpp.Message{From: jid.Domain, To: client.JID, Type: xmpp.MessageTypeNormal}
|
|
||||||
|
|
||||||
m.Thread = xmpp.SessionID()
|
|
||||||
m.Body = client.Domain + " (with method " + client.Method + ") need to validate your identity, do you agree ?"
|
|
||||||
m.Body += "\nValidation code : " + client.Transaction
|
|
||||||
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}
|
|
||||||
|
|
||||||
log.Printf("%sSend message %v", LogInfo, m)
|
|
||||||
WaitMessageAnswers[client.Transaction] = client
|
|
||||||
comp.Out <- m
|
|
||||||
|
|
||||||
client.TypeSend = TYPE_SEND_MESSAGE
|
|
||||||
client.IdMap = client.Transaction
|
|
||||||
}
|
|
||||||
|
|
||||||
func TransactionID() string {
|
|
||||||
var bytes = make([]byte, 8)
|
|
||||||
if _, err := rand.Read(bytes); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
for k, v := range bytes {
|
|
||||||
bytes[k] = dictionary[v%byte(len(dictionary))]
|
|
||||||
}
|
|
||||||
return string(bytes)
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
package xmpp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.kingpenguin.tk/chteufleur/go-xmpp.git/src/xmpp"
|
||||||
|
|
||||||
|
"log"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
REPLY_UNREACHABLE = "reply_unreachable"
|
||||||
|
REPLY_DENY = "reply_deny"
|
||||||
|
REPLY_OK = "reply_ok"
|
||||||
|
|
||||||
|
TYPE_SEND_MESSAGE = "type_send_message"
|
||||||
|
TYPE_SEND_IQ = "type_send_iq"
|
||||||
|
|
||||||
|
TEMPLATE_DOMAIN = "_DOMAIN_"
|
||||||
|
TEMPLATE_METHOD = "_METHOD_"
|
||||||
|
TEMPLATE_VALIDATION_CODE = "_VALIDE_CODE_"
|
||||||
|
DEFAULT_MESSAGE = "_DOMAIN_ (with method _METHOD_) need to validate your identity, do you agree ?\nValidation code : _VALIDE_CODE_\nPlease check that this code is the same as on _DOMAIN_.\n\nIf your client doesn't support that functionnality, please send back the validation code to confirm the request."
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
MapLangs = make(map[string]string)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Confirmation struct {
|
||||||
|
JID string
|
||||||
|
Method string
|
||||||
|
Domain string
|
||||||
|
Transaction string
|
||||||
|
|
||||||
|
TypeSend string
|
||||||
|
IdMap string
|
||||||
|
|
||||||
|
ChanReply chan string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (confirmation *Confirmation) SendConfirmation() {
|
||||||
|
log.Printf("%sQuery JID %s", LogInfo, confirmation.JID)
|
||||||
|
clientJID, _ := xmpp.ParseJID(confirmation.JID)
|
||||||
|
if clientJID.Resource == "" {
|
||||||
|
confirmation.askViaMessage()
|
||||||
|
} else {
|
||||||
|
confirmation.askViaIQ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (confirmation *Confirmation) askViaIQ() {
|
||||||
|
stanzaID++
|
||||||
|
stanzaIDstr := strconv.Itoa(stanzaID)
|
||||||
|
m := xmpp.IQ{Type: xmpp.IQTypeGet, To: confirmation.JID, From: jid.Full(), ID: stanzaIDstr}
|
||||||
|
confirm := &xmpp.Confirm{ID: confirmation.Transaction, Method: confirmation.Method, URL: confirmation.Domain}
|
||||||
|
m.PayloadEncode(confirm)
|
||||||
|
WaitIqMessages[stanzaIDstr] = confirmation
|
||||||
|
comp.Out <- m
|
||||||
|
|
||||||
|
confirmation.TypeSend = TYPE_SEND_IQ
|
||||||
|
confirmation.IdMap = stanzaIDstr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (confirmation *Confirmation) askViaMessage() {
|
||||||
|
m := xmpp.Message{From: jid.Full(), To: confirmation.JID, Type: xmpp.MessageTypeNormal}
|
||||||
|
m.Thread = xmpp.SessionID()
|
||||||
|
confirmation.setBodies(&m)
|
||||||
|
m.Confirm = &xmpp.Confirm{ID: confirmation.Transaction, Method: confirmation.Method, URL: confirmation.Domain}
|
||||||
|
|
||||||
|
log.Printf("%sSend message %v", LogInfo, m)
|
||||||
|
WaitMessageAnswers[confirmation.Transaction] = confirmation
|
||||||
|
comp.Out <- m
|
||||||
|
|
||||||
|
confirmation.TypeSend = TYPE_SEND_MESSAGE
|
||||||
|
confirmation.IdMap = confirmation.Transaction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (confirmation *Confirmation) setBodies(message *xmpp.Message) {
|
||||||
|
msg := DEFAULT_MESSAGE
|
||||||
|
if len(MapLangs) == 0 {
|
||||||
|
msg = strings.Replace(msg, TEMPLATE_DOMAIN, confirmation.Domain, -1)
|
||||||
|
msg = strings.Replace(msg, TEMPLATE_METHOD, confirmation.Method, -1)
|
||||||
|
msg = strings.Replace(msg, TEMPLATE_VALIDATION_CODE, confirmation.Transaction, -1)
|
||||||
|
message.Body = append(message.Body, xmpp.MessageBody{Lang: "en", Value: msg})
|
||||||
|
} else {
|
||||||
|
for key, val := range MapLangs {
|
||||||
|
msg = val
|
||||||
|
msg = strings.Replace(msg, TEMPLATE_DOMAIN, confirmation.Domain, -1)
|
||||||
|
msg = strings.Replace(msg, TEMPLATE_METHOD, confirmation.Method, -1)
|
||||||
|
msg = strings.Replace(msg, TEMPLATE_VALIDATION_CODE, confirmation.Transaction, -1)
|
||||||
|
msg = strings.Replace(msg, "\\n", "\n", -1)
|
||||||
|
msg = strings.Replace(msg, "\\r", "\r", -1)
|
||||||
|
msg = strings.Replace(msg, "\\t", "\t", -1)
|
||||||
|
message.Body = append(message.Body, xmpp.MessageBody{Lang: key, Value: msg})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
157
xmpp/xmpp.go
157
xmpp/xmpp.go
|
|
@ -8,13 +8,17 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
LogInfo = "\t[XMPP COMPONENT INFO]\t"
|
LogInfo = "\t[XMPP INFO]\t"
|
||||||
LogError = "\t[XMPP COMPONENT ERROR]\t"
|
LogError = "\t[XMPP ERROR]\t"
|
||||||
LogDebug = "\t[XMPP COMPONENT DEBUG]\t"
|
LogDebug = "\t[XMPP DEBUG]\t"
|
||||||
|
|
||||||
|
DEFAULT_SERVER_ADDRESS = "127.0.0.1"
|
||||||
|
DEFAULT_SERVER_PORT = "5347"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
Addr = "127.0.0.1:5347"
|
Addr = ""
|
||||||
|
Port = ""
|
||||||
JidStr = ""
|
JidStr = ""
|
||||||
Secret = ""
|
Secret = ""
|
||||||
|
|
||||||
|
|
@ -28,18 +32,55 @@ var (
|
||||||
|
|
||||||
ChanAction = make(chan string)
|
ChanAction = make(chan string)
|
||||||
|
|
||||||
WaitMessageAnswers = make(map[string]*Client)
|
WaitMessageAnswers = make(map[string]*Confirmation)
|
||||||
WaitIqMessages = make(map[string]*Client)
|
WaitIqMessages = make(map[string]*Confirmation)
|
||||||
|
|
||||||
Debug = true
|
Debug = true
|
||||||
|
VerifyCertValidity = true
|
||||||
|
|
||||||
|
identity = &xmpp.DiscoIdentity{Category: "client", Type: "bot", Name: "HTTP authentication over XMPP"}
|
||||||
)
|
)
|
||||||
|
|
||||||
func Run() {
|
func Run() {
|
||||||
|
var addr string
|
||||||
|
var isComponent bool
|
||||||
|
|
||||||
log.Printf("%sRunning", LogInfo)
|
log.Printf("%sRunning", LogInfo)
|
||||||
// Create stream and configure it as a component connection.
|
// Create stream and configure it as a component connection.
|
||||||
jid = must(xmpp.ParseJID(JidStr)).(xmpp.JID)
|
jid = must(xmpp.ParseJID(JidStr)).(xmpp.JID)
|
||||||
stream = must(xmpp.NewStream(Addr, &xmpp.StreamConfig{LogStanzas: Debug})).(*xmpp.Stream)
|
isComponent = jid.Node == ""
|
||||||
comp = must(xmpp.NewComponentXMPP(stream, jid, Secret)).(*xmpp.XMPP)
|
|
||||||
|
if isComponent {
|
||||||
|
// component
|
||||||
|
if Addr == "" {
|
||||||
|
Addr = DEFAULT_SERVER_ADDRESS
|
||||||
|
}
|
||||||
|
if Port == "" {
|
||||||
|
Port = DEFAULT_SERVER_PORT
|
||||||
|
}
|
||||||
|
addr = Addr + ":" + Port
|
||||||
|
} else {
|
||||||
|
// client
|
||||||
|
if Addr == "" {
|
||||||
|
addrs := must(xmpp.HomeServerAddrs(jid)).([]string)
|
||||||
|
addr = addrs[0]
|
||||||
|
} else {
|
||||||
|
if Port == "" {
|
||||||
|
Port = DEFAULT_SERVER_PORT
|
||||||
|
}
|
||||||
|
addr = Addr + ":" + Port
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("%sConnecting to %s", LogInfo, addr)
|
||||||
|
stream = must(xmpp.NewStream(addr, &xmpp.StreamConfig{LogStanzas: Debug, ConnectionDomain: jid.Domain})).(*xmpp.Stream)
|
||||||
|
|
||||||
|
if isComponent {
|
||||||
|
comp = must(xmpp.NewComponentXMPP(stream, jid, Secret)).(*xmpp.XMPP)
|
||||||
|
} else {
|
||||||
|
comp = must(xmpp.NewClientXMPP(stream, jid, Secret, &xmpp.ClientConfig{InsecureSkipVerify: !VerifyCertValidity})).(*xmpp.XMPP)
|
||||||
|
comp.Out <- xmpp.Presence{}
|
||||||
|
}
|
||||||
|
|
||||||
mainXMPP()
|
mainXMPP()
|
||||||
log.Printf("%sReach main method's end", LogInfo)
|
log.Printf("%sReach main method's end", LogInfo)
|
||||||
|
|
@ -52,24 +93,29 @@ func mainXMPP() {
|
||||||
case *xmpp.Presence:
|
case *xmpp.Presence:
|
||||||
|
|
||||||
case *xmpp.Message:
|
case *xmpp.Message:
|
||||||
confirm := v.Confir
|
confirm := v.Confirm
|
||||||
if confirm != nil {
|
if confirm != nil {
|
||||||
client := WaitMessageAnswers[confirm.Id]
|
confirmation := WaitMessageAnswers[confirm.ID]
|
||||||
processConfirm(v, client)
|
processConfirm(v, confirmation)
|
||||||
} else {
|
} else {
|
||||||
// If body is the confirmation id, it will be considerated as accepted.
|
// If body is the confirmation id, it will be considerated as accepted.
|
||||||
// In order to be compatible with all clients.
|
// In order to be compatible with all confirmations.
|
||||||
client := WaitMessageAnswers[v.Body]
|
if len(v.Body) > 0 {
|
||||||
jidFrom, _ := xmpp.ParseJID(v.From)
|
confirmation := WaitMessageAnswers[v.Body[0].Value]
|
||||||
if client != nil && client.JID == jidFrom.Bare() {
|
jidFrom, _ := xmpp.ParseJID(v.From)
|
||||||
processConfirm(v, client)
|
if confirmation != nil && confirmation.JID == jidFrom.Bare() {
|
||||||
|
processConfirm(v, confirmation)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case *xmpp.Iq:
|
case *xmpp.IQ:
|
||||||
switch v.PayloadName().Space {
|
switch v.PayloadName().Space {
|
||||||
|
case xmpp.NSDiscoInfo:
|
||||||
|
execDisco(v)
|
||||||
|
|
||||||
case xmpp.NSDiscoItems:
|
case xmpp.NSDiscoItems:
|
||||||
execDiscoCommand(v)
|
execDisco(v)
|
||||||
|
|
||||||
case xmpp.NSVCardTemp:
|
case xmpp.NSVCardTemp:
|
||||||
reply := v.Response(xmpp.IQTypeResult)
|
reply := v.Response(xmpp.IQTypeResult)
|
||||||
|
|
@ -85,17 +131,17 @@ func mainXMPP() {
|
||||||
case xmpp.NSHTTPAuth:
|
case xmpp.NSHTTPAuth:
|
||||||
confirm := &xmpp.Confirm{}
|
confirm := &xmpp.Confirm{}
|
||||||
v.PayloadDecode(confirm)
|
v.PayloadDecode(confirm)
|
||||||
client := WaitIqMessages[v.Id]
|
confirmation := WaitIqMessages[v.ID]
|
||||||
processConfirm(v, client)
|
processConfirm(v, confirmation)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// Handle reply iq that doesn't contain HTTP-Auth namespace
|
// Handle reply iq that doesn't contain HTTP-Auth namespace
|
||||||
client := WaitIqMessages[v.Id]
|
confirmation := WaitIqMessages[v.ID]
|
||||||
processConfirm(v, client)
|
processConfirm(v, confirmation)
|
||||||
|
|
||||||
if client == nil {
|
if confirmation == nil {
|
||||||
reply := v.Response(xmpp.IQTypeError)
|
reply := v.Response(xmpp.IQTypeError)
|
||||||
reply.PayloadEncode(xmpp.NewError("cancel", xmpp.FeatureNotImplemented, ""))
|
reply.PayloadEncode(xmpp.NewError("cancel", xmpp.ErrorFeatureNotImplemented, ""))
|
||||||
comp.Out <- reply
|
comp.Out <- reply
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -106,38 +152,38 @@ func mainXMPP() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func processConfirm(x interface{}, client *Client) {
|
func processConfirm(x interface{}, confirmation *Confirmation) {
|
||||||
mes, mesOK := x.(*xmpp.Message)
|
mes, mesOK := x.(*xmpp.Message)
|
||||||
iq, iqOK := x.(*xmpp.Iq)
|
iq, iqOK := x.(*xmpp.IQ)
|
||||||
|
|
||||||
if client != nil {
|
if confirmation != nil {
|
||||||
if mesOK && mes.Error != nil {
|
if mesOK && mes.Error != nil {
|
||||||
// Message error
|
// Message error
|
||||||
errCondition := mes.Error.Condition()
|
errCondition := mes.Error.Condition()
|
||||||
if errCondition == xmpp.ServiceUnavailable {
|
if errCondition == xmpp.ErrorServiceUnavailable {
|
||||||
// unreachable
|
// unreachable
|
||||||
client.ChanReply <- REPLY_UNREACHABLE
|
confirmation.ChanReply <- REPLY_UNREACHABLE
|
||||||
} else {
|
} else {
|
||||||
client.ChanReply <- REPLY_DENY
|
confirmation.ChanReply <- REPLY_DENY
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if iqOK && iq.Error != nil {
|
} else if iqOK && iq.Error != nil {
|
||||||
// IQ error
|
// IQ error
|
||||||
errCondition := iq.Error.Condition()
|
errCondition := iq.Error.Condition()
|
||||||
if errCondition == xmpp.ServiceUnavailable || errCondition == xmpp.FeatureNotImplemented {
|
if errCondition == xmpp.ErrorServiceUnavailable || errCondition == xmpp.ErrorFeatureNotImplemented {
|
||||||
// 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]
|
confirmation.JID = strings.SplitN(confirmation.JID, "/", 2)[0]
|
||||||
go client.QueryClient()
|
go confirmation.SendConfirmation()
|
||||||
} else if errCondition == xmpp.RemoteServerNotFound {
|
} else if errCondition == xmpp.ErrorRemoteServerNotFound {
|
||||||
// unreachable
|
// unreachable
|
||||||
client.ChanReply <- REPLY_UNREACHABLE
|
confirmation.ChanReply <- REPLY_UNREACHABLE
|
||||||
} else {
|
} else {
|
||||||
client.ChanReply <- REPLY_DENY
|
confirmation.ChanReply <- REPLY_DENY
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// No error
|
// No error
|
||||||
client.ChanReply <- REPLY_OK
|
confirmation.ChanReply <- REPLY_OK
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -149,8 +195,39 @@ func must(v interface{}, err error) interface{} {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func execDiscoCommand(iq *xmpp.Iq) {
|
func execDisco(iq *xmpp.IQ) {
|
||||||
log.Printf("%sDiscovery item iq received", LogInfo)
|
log.Printf("%sDisco Feature", LogInfo)
|
||||||
|
|
||||||
|
discoInfoReceived := &xmpp.DiscoItems{}
|
||||||
|
iq.PayloadDecode(discoInfoReceived)
|
||||||
|
|
||||||
|
switch iq.PayloadName().Space {
|
||||||
|
case xmpp.NSDiscoInfo:
|
||||||
|
reply := iq.Response(xmpp.IQTypeResult)
|
||||||
|
|
||||||
|
discoInfo := &xmpp.DiscoInfo{}
|
||||||
|
discoInfo.Identity = append(discoInfo.Identity, *identity)
|
||||||
|
discoInfo.Feature = append(discoInfo.Feature, xmpp.DiscoFeature{Var: xmpp.NSDiscoInfo})
|
||||||
|
discoInfo.Feature = append(discoInfo.Feature, xmpp.DiscoFeature{Var: xmpp.NSDiscoItems})
|
||||||
|
discoInfo.Feature = append(discoInfo.Feature, xmpp.DiscoFeature{Var: xmpp.NSJabberClient})
|
||||||
|
|
||||||
|
reply.PayloadEncode(discoInfo)
|
||||||
|
comp.Out <- reply
|
||||||
|
|
||||||
|
case xmpp.NSDiscoItems:
|
||||||
|
if discoInfoReceived.Node == xmpp.NodeAdHocCommand {
|
||||||
|
execDiscoCommand(iq)
|
||||||
|
} else {
|
||||||
|
reply := iq.Response(xmpp.IQTypeResult)
|
||||||
|
discoItems := &xmpp.DiscoItems{}
|
||||||
|
reply.PayloadEncode(discoItems)
|
||||||
|
comp.Out <- reply
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func execDiscoCommand(iq *xmpp.IQ) {
|
||||||
|
log.Printf("%sAd-Hoc Command", LogInfo)
|
||||||
reply := iq.Response(xmpp.IQTypeResult)
|
reply := iq.Response(xmpp.IQTypeResult)
|
||||||
discoItem := &xmpp.DiscoItems{Node: xmpp.NodeAdHocCommand}
|
discoItem := &xmpp.DiscoItems{Node: xmpp.NodeAdHocCommand}
|
||||||
reply.PayloadEncode(discoItem)
|
reply.PayloadEncode(discoItem)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue