Compare commits

..

No commits in common. "master" and "v0.3.1" have entirely different histories.

13 changed files with 237 additions and 461 deletions

View File

@ -1,66 +1,52 @@
# HTTPAuthenticationOverXMPP
# HTTPAuthentificationOverXMPP
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.
Provide an HTTP anthentification over XMPP. Implementation of [XEP-0070](https://xmpp.org/extensions/xep-0070.html).
## Compilation
### Dependencies
* [go-xmpp](https://git.kingpenguin.tk/chteufleur/go-xmpp) for the XMPP part.
* [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.
Then, go into your $GOPATH directory and go get the source code.
```sh
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 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.conf).
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_server_address : Component server address connection (default: 127.0.0.1)
* xmpp_server_port : Component server port connection (default: 5347)
* __xmpp_jid__ : Account JID
* __xmpp_secret__ : Account password
* xmpp_hostname : Component hostname
* xmpp_secret : Component password
* xmpp_debug : Enable debug log at true (default: false)
* xmpp_verify_cert_validity : Enable certificate verification (default: true)
HTTP
* http_port : HTTP port to bind (default: -1, desactive: -1)
* https_port : HTTPS port to bind (default: -1, desactive: -1)
* http_port : HTTP port to bind (default: 9090, desactive: -1)
* https_port : HTTPS port to bind (default: 9093, desactive: -1)
* https_cert_path : Path to the certificate file (default: ./cert.pem)
* https_key_path : Path to the key file (default: ./key.pem)
* 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])
* http_timeoute_sec : Define a timeout if user did not give an answer to the request (default: 60)
__Bold config__ are mandatory.
If ``http_bind_address_ipv4`` is set to ``0.0.0.0``, it will bind all address on IPv4 __AND__ IPv6.
### Usage
### Utilization
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)
* __domain__ : Domain you want to access
* __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)
__Bold parameters__ are mandatory.
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.

22
cert.pem Normal file
View File

@ -0,0 +1,22 @@
-----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-----

View File

@ -5,7 +5,6 @@ import (
"fmt"
"log"
"math/rand"
"net/http"
"strconv"
"strings"
@ -29,31 +28,23 @@ const (
RETURN_VALUE_OK = "OK"
RETURN_VALUE_NOK = "NOK"
MAX_PORT_VAL = 65535
StatusUnknownError = 520
StatusUnreachable = 523
)
var (
HttpPortBind = -1
HttpsPortBind = -1
HttpPortBind = 9090
HttpsPortBind = 9093
CertPath = "./cert.pem"
KeyPath = "./key.pem"
ChanRequest = make(chan interface{}, 5)
TimeoutSec = 60 // 1 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) {
// TODO
fmt.Fprintf(w, "Welcome to HTTP authentification over XMPP")
}
@ -62,15 +53,15 @@ func authHandler(w http.ResponseWriter, r *http.Request) {
jid := strings.Join(r.Form[PARAM_JID], "")
method := strings.Join(r.Form[METHOD_ACCESS], "")
domain := strings.Join(r.Form[DOMAIN_ACCESS], "")
transaction := strings.Join(r.Form[TRANSACTION_ID], "")
if jid == "" || method == "" || domain == "" || transaction == "" {
if jid == "" || method == "" || domain == "" {
// If mandatory params is missing
log.Printf("%sMandatory params is missing", LogInfo)
w.WriteHeader(http.StatusBadRequest)
return
}
transaction := strings.Join(r.Form[TRANSACTION_ID], "")
timeoutStr := strings.Join(r.Form[TIMEOUTE], "")
log.Printf("%sAuth %s", LogInfo, jid)
timeout, err := strconv.Atoi(timeoutStr)
@ -83,13 +74,13 @@ func authHandler(w http.ResponseWriter, r *http.Request) {
chanAnswer := make(chan string)
confirmation := new(xmpp.Confirmation)
confirmation.JID = jid
confirmation.Method = method
confirmation.Domain = domain
confirmation.Transaction = transaction
confirmation.ChanReply = chanAnswer
confirmation.SendConfirmation()
client := new(xmpp.Client)
client.JID = jid
client.Method = method
client.Domain = domain
client.Transaction = transaction
client.ChanReply = chanAnswer
client.QueryClient()
select {
case answer := <-chanAnswer:
@ -110,14 +101,14 @@ func authHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusUnauthorized)
}
switch confirmation.TypeSend {
switch client.TypeSend {
case xmpp.TYPE_SEND_IQ:
log.Printf("%sDelete IQ", LogDebug)
delete(xmpp.WaitIqMessages, confirmation.IdMap)
delete(xmpp.WaitIqMessages, client.IdMap)
case xmpp.TYPE_SEND_MESSAGE:
log.Printf("%sDelete Message", LogDebug)
delete(xmpp.WaitMessageAnswers, confirmation.IdMap)
delete(xmpp.WaitMessageAnswers, client.IdMap)
}
}
@ -128,44 +119,26 @@ func Run() {
http.HandleFunc(ROUTE_AUTH, authHandler)
if HttpPortBind > 0 {
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)
}
go runHttp()
}
if HttpsPortBind > 0 {
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)
}
go runHttps()
}
}
func runHttp(bindAddress string) {
func runHttp() {
port := strconv.Itoa(HttpPortBind)
log.Printf("%sHTTP listenning on %s:%s", LogInfo, bindAddress, port)
err := http.ListenAndServe(bindAddress+":"+port, nil)
log.Printf("%sHTTP listenning on port %s", LogInfo, port)
err := http.ListenAndServe(":"+port, nil)
if err != nil {
log.Fatal("%sListenAndServe: ", LogError, err)
}
}
func runHttps(bindAddress string) {
func runHttps() {
port := strconv.Itoa(HttpsPortBind)
log.Printf("%sHTTPS listenning on %s:%s", LogInfo, bindAddress, port)
err := http.ListenAndServeTLS(bindAddress+":"+port, CertPath, KeyPath, nil)
log.Printf("%sHTTPS listenning on port %s", LogInfo, port)
err := http.ListenAndServeTLS(":"+port, CertPath, KeyPath, nil)
if err != nil {
log.Fatal("%sListenAndServe: ", LogError, err)
}

View File

@ -1,16 +1,13 @@
# XMPP informations (component)
# XMPP informations
xmpp_server_address=192.168.1.2
xmpp_server_port=5347
xmpp_jid=xmppsteam.kingpenguin.tk
xmpp_hostname=xmppsteam.kingpenguin.tk
xmpp_secret=xmpp4steam_password
xmpp_debug=true
xmpp_verify_cert_validity=true
# HTTP informations
http_bind_address_ipv4=127.0.0.1
http_bind_address_ipv6=[::1]
http_port=9090
https_port=9093
https_cert_path=./cert.pem
https_key_path=./key.pem
http_timeout_sec=60
http_timeoute_sec=60

27
key.pem Normal file
View File

@ -0,0 +1,27 @@
-----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
View File

@ -10,17 +10,16 @@ import (
"os"
"os/signal"
"strconv"
"strings"
"syscall"
"time"
)
const (
Version = "v0.5-dev"
configurationFilePath = "http-auth/httpAuth.conf"
langFilePath = "http-auth/messages.lang"
PathConfEnvVariable = "XDG_CONFIG_DIRS"
DefaultXdgConfigDirs = "/etc/xdg"
Version = "v0.3.1"
configurationFilePath = "httpAuth.cfg"
default_xmpp_server_address = "127.0.0.1"
default_xmpp_server_port = "5347"
)
var (
@ -30,13 +29,13 @@ var (
func init() {
log.Printf("Running HTTP-Auth %v", Version)
if !loadConfigFile() {
log.Fatal("Failed to load configuration file.")
err := cfg.Load(configurationFilePath, mapConfig)
if err != nil {
log.Fatal("Failed to load configuration file.", err)
}
loadLangFile()
// HTTP config
httpTimeout, err := strconv.Atoi(mapConfig["http_timeout_sec"])
httpTimeout, err := strconv.Atoi(mapConfig["http_timeoute_sec"])
if err == nil && httpTimeout > 0 && httpTimeout < http.MaxTimeout {
log.Println("Define HTTP timeout to " + strconv.Itoa(httpTimeout) + " second")
http.TimeoutSec = httpTimeout
@ -53,71 +52,28 @@ func init() {
http.CertPath = mapConfig["https_cert_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_server_address := mapConfig["xmpp_server_address"]
if xmpp_server_address != "" {
xmpp.Addr = xmpp_server_address
if xmpp_server_address == "" {
xmpp_server_address = default_xmpp_server_address
}
xmpp_server_port := mapConfig["xmpp_server_port"]
if xmpp_server_port != "" {
xmpp.Port = xmpp_server_port
if xmpp_server_port == "" {
xmpp_server_port = default_xmpp_server_port
}
xmpp.JidStr = mapConfig["xmpp_jid"]
xmpp.Addr = xmpp_server_address + ":" + xmpp_server_port
xmpp.JidStr = mapConfig["xmpp_hostname"]
xmpp.Secret = mapConfig["xmpp_secret"]
xmpp.Debug = mapConfig["xmpp_debug"] == "true"
xmpp.VerifyCertValidity = mapConfig["xmpp_verify_cert_validity"] != "false" // Default TRUE
}
func loadConfigFile() bool {
ret := false
envVariable := os.Getenv(PathConfEnvVariable)
if envVariable == "" {
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
}
}
func getChanString(c chan interface{}) string {
ret := ""
i := <-c
if v, ok := i.(string); ok {
ret = v
}
return ret
}

View File

@ -1,2 +0,0 @@
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 functionnality, 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 ?\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.

View File

@ -1,40 +0,0 @@
# 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``.

View File

@ -1,9 +0,0 @@
[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

View File

@ -1,53 +0,0 @@
#!/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

93
xmpp/client.go Normal file
View File

@ -0,0 +1,93 @@
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)
}

View File

@ -1,97 +0,0 @@
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.Confir = &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})
}
}
}

View File

@ -8,17 +8,13 @@ import (
)
const (
LogInfo = "\t[XMPP INFO]\t"
LogError = "\t[XMPP ERROR]\t"
LogDebug = "\t[XMPP DEBUG]\t"
DEFAULT_SERVER_ADDRESS = "127.0.0.1"
DEFAULT_SERVER_PORT = "5347"
LogInfo = "\t[XMPP COMPONENT INFO]\t"
LogError = "\t[XMPP COMPONENT ERROR]\t"
LogDebug = "\t[XMPP COMPONENT DEBUG]\t"
)
var (
Addr = ""
Port = ""
Addr = "127.0.0.1:5347"
JidStr = ""
Secret = ""
@ -32,55 +28,18 @@ var (
ChanAction = make(chan string)
WaitMessageAnswers = make(map[string]*Confirmation)
WaitIqMessages = make(map[string]*Confirmation)
WaitMessageAnswers = make(map[string]*Client)
WaitIqMessages = make(map[string]*Client)
Debug = true
VerifyCertValidity = true
identity = &xmpp.DiscoIdentity{Category: "client", Type: "bot", Name: "HTTP authentication over XMPP"}
)
func Run() {
var addr string
var isComponent bool
log.Printf("%sRunning", LogInfo)
// Create stream and configure it as a component connection.
jid = must(xmpp.ParseJID(JidStr)).(xmpp.JID)
isComponent = jid.Node == ""
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 {
stream = must(xmpp.NewStream(Addr, &xmpp.StreamConfig{LogStanzas: Debug})).(*xmpp.Stream)
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()
log.Printf("%sReach main method's end", LogInfo)
@ -95,27 +54,22 @@ func mainXMPP() {
case *xmpp.Message:
confirm := v.Confir
if confirm != nil {
confirmation := WaitMessageAnswers[confirm.Id]
processConfirm(v, confirmation)
client := WaitMessageAnswers[confirm.Id]
processConfirm(v, client)
} else {
// If body is the confirmation id, it will be considerated as accepted.
// In order to be compatible with all confirmations.
if len(v.Body) > 0 {
confirmation := WaitMessageAnswers[v.Body[0].Value]
// In order to be compatible with all clients.
client := WaitMessageAnswers[v.Body]
jidFrom, _ := xmpp.ParseJID(v.From)
if confirmation != nil && confirmation.JID == jidFrom.Bare() {
processConfirm(v, confirmation)
}
if client != nil && client.JID == jidFrom.Bare() {
processConfirm(v, client)
}
}
case *xmpp.Iq:
switch v.PayloadName().Space {
case xmpp.NSDiscoInfo:
execDisco(v)
case xmpp.NSDiscoItems:
execDisco(v)
execDiscoCommand(v)
case xmpp.NSVCardTemp:
reply := v.Response(xmpp.IQTypeResult)
@ -131,17 +85,17 @@ func mainXMPP() {
case xmpp.NSHTTPAuth:
confirm := &xmpp.Confirm{}
v.PayloadDecode(confirm)
confirmation := WaitIqMessages[v.Id]
processConfirm(v, confirmation)
client := WaitIqMessages[v.Id]
processConfirm(v, client)
default:
// Handle reply iq that doesn't contain HTTP-Auth namespace
confirmation := WaitIqMessages[v.Id]
processConfirm(v, confirmation)
client := WaitIqMessages[v.Id]
processConfirm(v, client)
if confirmation == nil {
if client == nil {
reply := v.Response(xmpp.IQTypeError)
reply.PayloadEncode(xmpp.NewError("cancel", xmpp.ErrorFeatureNotImplemented, ""))
reply.PayloadEncode(xmpp.NewError("cancel", xmpp.FeatureNotImplemented, ""))
comp.Out <- reply
}
}
@ -152,38 +106,38 @@ func mainXMPP() {
}
}
func processConfirm(x interface{}, confirmation *Confirmation) {
func processConfirm(x interface{}, client *Client) {
mes, mesOK := x.(*xmpp.Message)
iq, iqOK := x.(*xmpp.Iq)
if confirmation != nil {
if client != nil {
if mesOK && mes.Error != nil {
// Message error
errCondition := mes.Error.Condition()
if errCondition == xmpp.ErrorServiceUnavailable {
if errCondition == xmpp.ServiceUnavailable {
// unreachable
confirmation.ChanReply <- REPLY_UNREACHABLE
client.ChanReply <- REPLY_UNREACHABLE
} else {
confirmation.ChanReply <- REPLY_DENY
client.ChanReply <- REPLY_DENY
}
} else if iqOK && iq.Error != nil {
// IQ error
errCondition := iq.Error.Condition()
if errCondition == xmpp.ErrorServiceUnavailable || errCondition == xmpp.ErrorFeatureNotImplemented {
if errCondition == xmpp.ServiceUnavailable || errCondition == xmpp.FeatureNotImplemented {
// send by message if client doesn't implemente it
confirmation.JID = strings.SplitN(confirmation.JID, "/", 2)[0]
go confirmation.SendConfirmation()
} else if errCondition == xmpp.ErrorRemoteServerNotFound {
client.JID = strings.SplitN(client.JID, "/", 2)[0]
go client.QueryClient()
} else if errCondition == xmpp.RemoteServerNotFound {
// unreachable
confirmation.ChanReply <- REPLY_UNREACHABLE
client.ChanReply <- REPLY_UNREACHABLE
} else {
confirmation.ChanReply <- REPLY_DENY
client.ChanReply <- REPLY_DENY
}
} else {
// No error
confirmation.ChanReply <- REPLY_OK
client.ChanReply <- REPLY_OK
}
}
}
@ -195,39 +149,8 @@ func must(v interface{}, err error) interface{} {
return v
}
func execDisco(iq *xmpp.Iq) {
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)
log.Printf("%sDiscovery item iq received", LogInfo)
reply := iq.Response(xmpp.IQTypeResult)
discoItem := &xmpp.DiscoItems{Node: xmpp.NodeAdHocCommand}
reply.PayloadEncode(discoItem)