Bryan Yep
Bryan Yep

Reputation: 1

Ejabberd: How can I register a new user in my XMPP server with a Python script?

I recently set up a XMPP server with ejabberd. I followed this tutorial to do so:

DenshiVideo: How to setup an XMPP server (ejabberd)

I've got access to the admin interface in the browser where I can register new users and delete them. The url is like this (replace "myserver"):

http://myserver.com:5280/admin/server/myserver.com/users/

What I want to do now is be able to register new users from outside the admin interface in the browser, maybe with a Python script. I'm trying to do so using the XMPPpy library.

This is the script I tried:

`#!/usr/bin/python3
 import xmpp

 server = "myserver.com"
 admin_username = "[email protected]"
 admin_password = "password1234"
 new_username = "[email protected]"
 new_password = "newuserpassword1234"

 def register_user(server, admin_username, admin_password, new_username, new_password):
     admin_jid = xmpp.protocol.JID(admin_username)
     client = xmpp.Client(server=server, debug=[])
     client.connect()

     # Authenticate as administrator
     client.auth(user=admin_jid.getNode(), password=admin_password)

     # Register a new account
     iq = xmpp.Iq(to=server, typ='set')
     iq.addChild("query", namespace="jabber:iq:register")
     iq.getTag("query").addChild("username", payload=[new_username])
     iq.getTag("query").addChild("password", payload=[new_password])

     result = client.SendAndWaitForResponse(iq)

     if result and result.getType() == 'result':
         print("User registration successful.")
     else:
         print(result)
         print("User registration failed.")

     client.disconnect()

 if __name__ == '__main__':
     register_user(server, admin_username, admin_password, new_username, new_password)`

However, the ejabberd server gives me back the following error:

`<iq xmlns="jabber:client" xml:lang="en" to="[email protected]/18317692873934713179914" from="bryanyep.com" type="error" id="3">
<query xmlns="jabber:iq:register">
<username>dinaboluarte</username>
<password>ROLEX</password>
</query>
<error type="auth">
<forbidden xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" />
<text xml:lang="en" xmlns="urn:ietf:params:xml:ns:xmpp-stanzas">
Access denied by service policy
</text>
</error>
</iq>
User registration failed.`

I asked ChatGPT about it and it told to do some changes in the /opt/ejabberd/conf/ejabberd.yml file, but I double-checked the file and the changes where already there. This is the 'access_rules' section on this file:

`access_rules:
  local:
    allow: local
  c2s:
    deny: blocked
    allow: all
  announce:
    allow: admin
  configure:
    allow: admin
  muc_create:
    allow: local
  pubsub_createnode:
    allow: local
  trusted_network:
    allow: loopback
  register:
    allow: all`

I'm not sure if I need to set up an API first and do a POST request, or if the API is already there and I don't know how to access it. However, there is an 'api_permissions' section on that same file:

`api_permissions:
  "console commands":
    from:
      - ejabberd_ctl
    who: all
    what: "*"
  "admin access":
    who:
      access:
        allow:
          - acl: loopback
          - acl: admin
      oauth:
        scope: "ejabberd:admin"
        access:
          allow:
            - acl: loopback
            - acl: admin
    what:
      - "*"
      - "!stop"
      - "!start"
  "public commands":
    who:
      ip: 127.0.0.1/8
    what:
      - status
      - connected_users_number`

And just in case, this is the 'listen' section on that same file:

`listen:
  -
    port: 5222
    ip: "::"
    module: ejabberd_c2s
    max_stanza_size: 262144
    shaper: c2s_shaper
    access: c2s
    starttls_required: true
  -
    port: 5223
    ip: "::"
    module: ejabberd_c2s
    max_stanza_size: 262144
    shaper: c2s_shaper
    access: c2s
    tls: true
  -
    port: 5269
    ip: "::"
    module: ejabberd_s2s_in
    max_stanza_size: 524288
    shaper: s2s_shaper
  -
    port: 5443
    ip: "::"
    module: ejabberd_http
    tls: true
    request_handlers:
      /admin: ejabberd_web_admin
      /api: mod_http_api
      /bosh: mod_bosh
      /captcha: ejabberd_captcha
      /upload: mod_http_upload
      /ws: ejabberd_http_ws
  -
    port: 5280
    ip: "::"
    module: ejabberd_http
    request_handlers:
      /admin: ejabberd_web_admin
      /.well-known/acme-challenge: ejabberd_acme
  -
    port: 3478
    ip: "::"
    transport: udp
    module: ejabberd_stun
    use_turn: true
    ## The server's public IPv4 address:
    # turn_ipv4_address: "203.0.113.3"
    ## The server's public IPv6 address:
    # turn_ipv6_address: "2001:db8::3"
  -
    port: 1883
    ip: "::"
    module: mod_mqtt
    backlog: 1000`

Please, if anyone knows how to do this I'll be very thankful. And sorry for the n00b question, this is my first time working with the XMPP protocol.

Thank you all in advance.

Upvotes: 0

Views: 211

Answers (1)

Badlop
Badlop

Reputation: 4120

There are two ways to register programmatically an account in ejabberd:

A) Using the XMPP method: First configure mod_register to allow any XMPP clients to register accounts. Or you could allow registration only from IP address 127.0.0.1. Then write a small Python script that behaves like a regular XMPP client: it connects to port 5222 and registers an account like any other client.

B) Using ejabberd's register API command: First configure a listener with ejabberd_http and mod_http_api, also grant access with option api_permissions. Then write a small Python script that sends an HTTP query to port 5443 and will be handled by mod_http_api.

Looking at your question, you are mixing stuff: your python script and access_rules are useful for method A. On the other hand, api_permissions is useful only for method B. Finally, the listen configuration is valid for method A and for method B.

I'll provide an example to use method B. I'll assume you start with the default configuration, that way when other person finds this thread, they can follow the example without needing to understand your previous configuration.

  1. listen section must include a port with module ejabberd_http and mod_http_api, for example:
listen:
  -
    port: 5443
    module: ejabberd_http
    tls: true
    request_handlers:
      /api: mod_http_api
  1. api_permissions must allow your desired connection, but disallow the rest. For example, this works in my case:
api_permissions:
  "console commands":
    from:
      - ejabberd_ctl
    who: all
    what: "*"
  "admin access":
    from:
      - mod_http_api
    who:
      access:
        allow:
          - acl: loopback
          - acl: admin
    what:
      - register
  1. modules section must include the module. Probably you already have it enabled, but just to make sure, check:
modules:
  ...
  mod_http_api: {}
  1. In my example configuration, the API query must include authentication details of some account with admin privileges in ejabberd, so let's grant admin privileges to an account named tim@localhost:
acl:
  admin: [user: tim]
  loopback:
    ip:
      - 127.0.0.0/8
      - ::1/128
  1. It's time to start ejabberd, and let's register the admin account:
$ ejabberdctl register tim localhost somepass
User tim@localhost successfully registered
  1. Now prepare a Python script to register an account, for example:
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
import requests
from requests.auth import HTTPBasicAuth
url = "https://localhost:5443/api/register"
data = {
    "user": "username2",
    "host": "localhost",
    "password": "somepassword"
}
res = requests.post(url, json=data, auth=HTTPBasicAuth("tim@localhost", "somepass"), verify=False)
print(res)
  1. Finally, execute the script, check the account was registered, and the password was correctly set:
$ python3 register.py
<Response [200]>

$ ejabberdctl registered_users localhost
tim
username2

$ ejabberdctl check_password username2 localhost somepassword
$ echo $?
0

Upvotes: 0

Related Questions