summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGeorg2021-08-31 22:59:00 +0200
committerGeorg2021-09-01 00:07:01 +0200
commitb747836374ef426079bfe3722bbc5dbba30d86a8 (patch)
tree4d277ed966ffd2aaf68e526f24845e22ef6eb2e7
downloadkeycloak-b747836374ef426079bfe3722bbc5dbba30d86a8.tar.gz
keycloak-b747836374ef426079bfe3722bbc5dbba30d86a8.tar.bz2
keycloak-b747836374ef426079bfe3722bbc5dbba30d86a8.zip
Init + config values + basic registration function
Signed-off-by: Georg <georg@lysergic.dev>
-rw-r--r--.gitignore1
-rw-r--r--README.md1
-rw-r--r--__init__.py71
-rw-r--r--config.py93
-rw-r--r--local/__init__.py1
-rw-r--r--plugin.py123
-rw-r--r--test.py38
7 files changed, 328 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c18dd8d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+__pycache__/
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d9775f1
--- /dev/null
+++ b/README.md
@@ -0,0 +1 @@
+Allows Limnoria users to interface with Keycloak SSO through IRC.
diff --git a/__init__.py b/__init__.py
new file mode 100644
index 0000000..063acc3
--- /dev/null
+++ b/__init__.py
@@ -0,0 +1,71 @@
+###
+# Copyright (c) 2021, Georg Pfuetzenreuter
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions, and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions, and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the author of this software nor the name of
+# contributors to this software may be used to endorse or promote products
+# derived from this software without specific prior written consent.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+###
+
+"""
+Keycloak: Interfaces with Keycloak SSO.
+"""
+
+import sys
+import supybot
+from supybot import world
+
+# Use this for the version of this plugin.
+__version__ = ""
+
+# XXX Replace this with an appropriate author or supybot.Author instance.
+__author__ = supybot.authors.unknown
+
+# This is a dictionary mapping supybot.Author instances to lists of
+# contributions.
+__contributors__ = {}
+
+# This is a url where the most recent plugin package can be downloaded.
+__url__ = ''
+
+from . import config
+from . import plugin
+if sys.version_info >= (3, 4):
+ from importlib import reload
+else:
+ from imp import reload
+# In case we're being reloaded.
+reload(config)
+reload(plugin)
+# Add more reloads here if you add third-party modules and want them to be
+# reloaded when this plugin is reloaded. Don't forget to import them as well!
+
+if world.testing:
+ from . import test
+
+Class = plugin.Class
+configure = config.configure
+
+
+# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
diff --git a/config.py b/config.py
new file mode 100644
index 0000000..d1b4f08
--- /dev/null
+++ b/config.py
@@ -0,0 +1,93 @@
+###
+# Copyright (c) 2021, Georg Pfuetzenreuter
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions, and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions, and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the author of this software nor the name of
+# contributors to this software may be used to endorse or promote products
+# derived from this software without specific prior written consent.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+###
+
+from supybot import conf, registry
+try:
+ from supybot.i18n import PluginInternationalization
+ _ = PluginInternationalization('Keycloak')
+except:
+ # Placeholder that allows to run the plugin on a bot
+ # without the i18n module
+ _ = lambda x: x
+
+
+def configure(advanced):
+ # This will be called by supybot to configure this module. advanced is
+ # a bool that specifies whether the user identified themself as an advanced
+ # user or not. You should effect your configuration by manipulating the
+ # registry as appropriate.
+ from supybot.questions import expect, anything, something, yn
+ conf.registerPlugin('Keycloak', True)
+
+
+Keycloak = conf.registerPlugin('Keycloak')
+# This is where your configuration variables (if any) should go. For example:
+# conf.registerGlobalValue(Keycloak, 'someConfigVariableName',
+# registry.Boolean(False, _("""Help for someConfigVariableName.""")))
+
+###
+# API related settings below:
+###
+conf.registerGroup(Keycloak, 'backend')
+conf.registerGlobalValue(Keycloak.backend, 'server',
+ registry.String('https://example.com',
+ """
+ Keycloak: Instance hostname in the format https://example.com - the rest of the URL will be constructed by the system.
+ """
+ , private=True
+))
+conf.registerGlobalValue(Keycloak.backend, 'realm',
+ registry.String('MyRealm',
+ """
+ Keycloak: Realm name with exact capitalization.
+ """
+ , private=True
+))
+conf.registerGlobalValue(Keycloak.backend, 'token',
+ registry.String('InsanelyLongTokenConsistingOfRandomChars',
+ """
+ Keycloak: Access/Bearer/OIDC token. NOT a client secret. NOT a token with MASTER realm access. NOT a token with excess access roles.
+ """
+ , private=True
+))
+
+###
+# User replies settings below:
+###
+conf.registerGroup(Keycloak, 'replies')
+conf.registerGlobalValue(Keycloak.replies, 'error',
+ registry.String('Something went wrong. Please contact an administrator.',
+ """
+ Keycloak: Response to show the user if any kind of error occured. Note that exact error details will alwasy be printed exclusively to the console.
+ """
+ , private=False
+))
+
+# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
diff --git a/local/__init__.py b/local/__init__.py
new file mode 100644
index 0000000..e86e97b
--- /dev/null
+++ b/local/__init__.py
@@ -0,0 +1 @@
+# Stub so local is a module, used for third-party modules
diff --git a/plugin.py b/plugin.py
new file mode 100644
index 0000000..0ca7894
--- /dev/null
+++ b/plugin.py
@@ -0,0 +1,123 @@
+###
+# Copyright (c) 2021, Georg Pfuetzenreuter
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions, and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions, and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the author of this software nor the name of
+# contributors to this software may be used to endorse or promote products
+# derived from this software without specific prior written consent.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+###
+
+import re
+import requests
+import secrets
+import string
+from supybot import utils, plugins, ircutils, callbacks
+from supybot.commands import *
+from supybot.ircmsgs import nick
+try:
+ from supybot.i18n import PluginInternationalization
+ _ = PluginInternationalization('Keycloak')
+except ImportError:
+ # Placeholder that allows to run the plugin on a bot
+ # without the i18n module
+ _ = lambda x: x
+
+
+class Keycloak(callbacks.Plugin):
+ """Interfaces with Keycloak SSO."""
+ threaded = True
+
+ def register(self, irc, msg, args, email):
+ """<email>
+ registers an account with your username and the specified email address"""
+
+ server = self.registryValue('backend.server')
+ realm = self.registryValue('backend.realm')
+ tokenurl = self.registryValue('backend.token')
+ usererr = self.registryValue('replies.error')
+
+ try:
+ tokendl = requests.get(tokenurl)
+ tokendata = tokendl.json()
+ token = tokendata['access_token']
+ url = server + '/auth/admin/realms/' + realm + '/users'
+ if re.match(r"[^@]+@[^@]+\.[^@]+", email):
+ payload = {
+ "firstName": "Foo",
+ "lastName": "Bar",
+ "email": email,
+ "enabled": "true",
+ "username": msg.nick,
+ "credentials": [{"type": "password", "value": "test123", "temporary": "true"}]
+ }
+ response = requests.post(
+ url,
+ headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token},
+ json = payload
+ )
+ print("Keycloak: HTTP Status ", response.status_code)
+ if response.text:
+ print("Keycloak: Response Text: ", response.text)
+ print("Keycloak: Response JSON: ", response.json())
+ status = response.status_code
+ #To-Do: figure out why this needs to bere instead of being fed from the usererr config variable defined above
+ #usererr = irc.error("Something went wrong. Please contact an administrator.")
+ if status == 201:
+ print(" SSO User " + msg.nick + " created.")
+ irc.reply("OK, please log in and change your password NOW.")
+ if status == 400:
+ print("ERROR: Keycloak indicated that the request is invalid.")
+ irc.error(usererr)
+ if status == 401:
+ print("ERROR: Fix your Keycloak API credentials and/or client roles, doh.")
+ irc.error(usererr)
+ if status == 403:
+ print("ERROR: Keycloak indicated that the authorization provided is not enough to access the resource.")
+ irc.error(usererr)
+ if status == 404:
+ print("ERROR: Keycloak indicated that the requested resource does not exist.")
+ irc.error(usererr)
+ if status == 409:
+ print("ERROR: Keycloak indicated that the resource already exists or \"some other coonflict when processing the request\" occured.")
+ irc.reply("Your username seems to already be registerd.")
+ if status == 415:
+ print("ERROR: Keycloak indicated that the requested media type is not supported.")
+ irc.error(usererr)
+ if status == 500:
+ print("ERROR: Keycloak indicated that the server could not fullfill the request due to \"some unexpected error \".")
+ irc.error(usererr)
+ else:
+ irc.error("Is that a valid email address?")
+ except:
+ print("ERROR: Keycloak token could not be installed.")
+ irc.error(usererr)
+
+ register = wrap(register, ['anything'])
+
+
+
+Class = Keycloak
+
+
+# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:
diff --git a/test.py b/test.py
new file mode 100644
index 0000000..51476cd
--- /dev/null
+++ b/test.py
@@ -0,0 +1,38 @@
+###
+# Copyright (c) 2021, Georg Pfuetzenreuter
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions, and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions, and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the author of this software nor the name of
+# contributors to this software may be used to endorse or promote products
+# derived from this software without specific prior written consent.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+###
+
+from supybot.test import *
+
+
+class KeycloakTestCase(PluginTestCase):
+ plugins = ('Keycloak',)
+
+
+# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: