I'm trying to create an new user account on my ejabberd server using http. I am using the following python script:
def create_xmpp_account(username, password):
ejabberd_api_url = "https://example.com:5443/api/register"
headers = {'Content-Type': 'application/jason'}
data = {
'user': username,
'host': 'example.com',
'password': password
}
auth = ('admin', 'myAdminPassword')
response = requests.post(ejabberd_api_url, headers=headers, json=data, auth=auth)
if response.status_code == 200:
print("Account created successfully")
else:
print("Failed to create account:", response.text)
I am getting a code 10 response from the server saying "you are not authorized to call this command. here is a copy of my ejabberd.yml file:
hosts:
- example.com
loglevel: info
certfiles:
- /etc/letsencrypt/live/example.com/fullchain.pem
- /etc/letsencrypt/live/example.com/privkey.pem
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
-
port: 1883
ip: "::"
module: mod_mqtt
backlog: 1000
s2s_use_starttls: optional
acl:
local:
user_regexp: ""
loopback:
ip:
- 127.0.0.0/8
- ::1/128
admin:
user:
- "[email protected]"
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
api_permissions:
"console commands":
from:
- ejabberd_ctl
who: all
what: "*"
"admin access":
who:
- admin
what:
- "*"
- "!stop"
from:
- ejabberd_ctl
- mod_http_api
"public commands":
who:
ip: 127.0.0.1/8
what:
- status
- register
- connected_users_number
shaper:
normal:
rate: 3000
burst_size: 20000
fast: 100000
shaper_rules:
max_user_sessions: 10
max_user_offline_messages:
5000: admin
100: all
c2s_shaper:
none: admin
normal: all
s2s_shaper: fast
modules:
mod_adhoc: {}
mod_admin_extra: {}
mod_announce:
access: announce
mod_avatar: {}
mod_blocking: {}
mod_bosh: {}
mod_caps: {}
mod_carboncopy: {}
mod_client_state: {}
mod_configure: {}
mod_disco: {}
mod_fail2ban: {}
mod_http_api: {}
mod_http_upload:
docroot: "/opt/uploads"
put_url: "https://example.com:5443/upload"
get_url: "https://example.com:5443/upload"
file_mode: "0644" # File permissions
max_size: 10485760 # Max upload size in bytes (10 MB)
thumbnail: false # set to true to generate thumbnails
custom_headers:
"Access-Control-Allow-Origin": "https://@HOST@"
"Access-Control-Allow-Methods": "GET,HEAD,PUT,OPTIONS"
"Access-Control-Allow-Headers": "Content-Type"
mod_last: {}
mod_mam:
assume_mam_usage: true
default: always
mod_mqtt: {}
mod_muc:
access:
- allow
access_admin:
- allow: admin
access_create: muc_create
access_persistent: muc_create
access_mam:
- allow
default_room_options:
mam: true
mod_muc_admin: {}
#mod_offline:
mod_ping: {}
mod_privacy: {}
mod_private: {}
mod_proxy65:
access: local
max_connections: 5
mod_pubsub:
access_createnode: pubsub_createnode
plugins:
- flat
- pep
force_node_config:
storage:bookmarks:
access_model: whitelist
mod_push: {}
mod_push_keepalive: {}
mod_register:
ip_access: all
mod_roster:
versioning: true
mod_s2s_dialback: {}
mod_shared_roster: {}
mod_stream_mgmt:
resend_on_timeout: if_offline
mod_stun_disco: {}
mod_vcard: {}
mod_vcard_xupdate: {}
mod_version:
show_os: false
I have been trying to add the new user using my admin credentials for now but ideally I would prefer to have a user say 'apiuser' who would be allowed to create accounts only from one ip address.Although I'll be happy if someone could show my what wrong with my .yml file
There is a simple problem in your python script, once solved the feature should work. I'll also comment other improvements you can make.
You are providing only the account username:
auth=HTTPBasicAuth("admin", "myAdminPassword"))
you should provide the account JID, including the hostname:
auth=HTTPBasicAuth("admin@localhost", "myAdminPassword"))
There is a line with a problematic additional blankspace in your configuration, I guess the problem is only in your copy&paste. There is a problematic blankspace in the pubsub_createnode
line:
muc_create:
allow: local
pubsub_createnode:
allow: local
If your registration script will run in a machine with a specific IP address, you can specify it in your configuration script. For example, I run the script in localhost, which may be the IPv4 127.0.0.1
address or the IPv6 ::1
address. Also, I only grant permission to run the register
and unregister
commands.
api_permissions:
"console commands":
from:
- ejabberd_ctl
who: all
what: "*"
"register script":
who:
access:
allow:
user: admin@localhost
ip: 127.0.0.1/8
access:
allow:
user: admin@localhost
ip: "::1"
what:
- register
- unregister
from:
- mod_http_api
If you want to grant this permission to another account, simply specify that one:
user: apiuser@localhost
You opened the port to spam registration:
mod_register:
ip_access: all
That is not needed at all. In fact, if the accounts on your server will be registered using only your script, you can disable registration in mod_register (which implements In-Band Registration):
mod_register:
access: none
And just for reference, I used this example python script for my testing:
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": "password"
}
res = requests.post(url, json=data, auth=HTTPBasicAuth("apiuser@localhost", "myApiuserPassword"))
print(res)