Skip to content

Commit

Permalink
Merge pull request #5 from elokapina/jaywink/users-command
Browse files Browse the repository at this point in the history
Add command `users`
  • Loading branch information
jaywink authored Apr 5, 2021
2 parents d2a6911 + 25a9534 commit 5bf4b63
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@

* Add command `power` to set power levels in a room where the bot has the
required power.

* Add command `users` to interact with an identity provider. Currently only Keycloak
is supported and the only functionality is to list usernames found in the configured
realm.

### Fixed

Expand Down
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,18 @@ ENCRYPTED and PUBLIC are either 'yes' or 'no'.

Same as without a subcommand, Bubo will tell you all about the rooms it maintains.

#### `users`

Manage users of an identity provider.

Currently [Keycloak](https://www.keycloak.org/) is the only identity provider supported.
The `users` command requires `admin` level bot privileges and currently just lists the
usernames in the configured realm. See `sample.config.yaml` for how to configure
a Keycloak client.

Future functionality will include registering users and sending them password reset
emails, as some examples.

### Room power levels

#### User power
Expand Down
24 changes: 24 additions & 0 deletions bot_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
# noinspection PyPackageRequirements
from nio.schemas import check_user_id

import help_strings
from chat_functions import send_text_to_room, invite_to_room
from communities import ensure_community_exists
from rooms import ensure_room_exists, create_breakout_room, set_user_power
from users import list_users

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -86,6 +88,8 @@ async def process(self):
await self._power()
elif self.command.startswith("rooms"):
await self._rooms()
elif self.command.startswith("users"):
await self._users()
else:
await self._unknown_command()

Expand Down Expand Up @@ -378,3 +382,23 @@ async def _unknown_command(self):
self.room.room_id,
f"Unknown command '{self.command}'. Try the 'help' command for more information.",
)

async def _users(self):
"""
Command to manage users.
"""
if not await self._ensure_admin():
return
text = None
if self.args:
if self.args[0] == "list":
users = await list_users(self.config)
text = f"The following usernames were found: {', '.join([user['username'] for user in users])}"
elif self.args[0] == "help":
text = help_strings.HELP_USERS
else:
users = await list_users(self.config)
text = f"The following usernames were found: {', '.join([user['username'] for user in users])}"
if not text:
text = help_strings.HELP_USERS
await send_text_to_room(self.client, self.room.room_id, text)
3 changes: 3 additions & 0 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ def __init__(self, filepath):
# Callbacks
self.callbacks = self._get_cfg(["callbacks"], default={}, required=False)

# Users
self.users = self._get_cfg(["users"], default={}, required=False)

def _get_cfg(
self,
path: List[str],
Expand Down
4 changes: 4 additions & 0 deletions help_strings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
HELP_USERS = """List or manage users.
Without any subcommands, lists users. Actually that's the only thing it does for now :P
"""
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
matrix-nio[e2e]>=0.8.0
Markdown>=3.1.1
python-keycloak>=0.24.0
PyYAML>=5.1.2
requests
18 changes: 18 additions & 0 deletions sample.config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,24 @@ permissions:
# Promote users who should have more power in a room
promote_users: true

# Configuration related to user management.
# Currently Keycloak is the only provider.
users:
provider:
# Future expansion, this is not checked yet for anything
# Please note you need to create a client in your keycloak realm, with the following:
# - client protocol: openid-connect
# - access type: confidential
# - service accounts enabled: true
# - service account roles -> client roles -> realm management: manage-users
# - service account roles -> client roles -> realm management: query-groups
# Then copy the credentials client secret here in 'client_secret_key'
type: keycloak
url: "http://keycloak.domain.tld/auth/"
# Define your realm
realm_name: "master"
client_secret_key: "client-secret"

# Configuration for rooms maintained by Bubo.
rooms:
# Default power levels. Bubo will ensure these are set for the rooms
Expand Down
24 changes: 24 additions & 0 deletions users.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from typing import List, Dict

# noinspection PyPackageRequirements
from keycloak import KeycloakAdmin

from config import Config


def get_admin_client(config: Config) -> KeycloakAdmin:
params = {
"server_url": config.users["provider"]["url"],
"realm_name": config.users["provider"]["realm_name"],
"client_secret_key": config.users["provider"]["client_secret_key"],
"verify": True,
}
return KeycloakAdmin(**params)


async def list_users(config: Config) -> List[Dict]:
if not config.users.get('provider'):
return []
keycloak_admin = get_admin_client(config)
users = keycloak_admin.get_users({})
return users

0 comments on commit 5bf4b63

Please sign in to comment.