Init + SSO registration

Signed-off-by: Georg <georg@lysergic.dev>
This commit is contained in:
Georg Pfuetzenreuter 2021-09-01 12:55:44 +02:00
commit d2e8fcd2b5
Signed by: Georg
GPG Key ID: 1DAF57F49F8E8F22
10 changed files with 332 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
__pycache__/

1
README.md Normal file
View File

@ -0,0 +1 @@
This is a prototype allowing users to register with all of our services in a single place.

25
README.old.md Normal file
View File

@ -0,0 +1,25 @@
# Liberta Casa
This has a series of little enhancements I have taken up as pet projects.
### Registration Page
##### Theoreticals
It includes the following technologies: oragono IRCd, flask, a python bot using irctokens
It consists of the following flow.
1. A user shall go on to the [registration](https://liberta.casa/register.html) (placeholder). They will enter the details and click on Register.
* The Website is generated using `flask` and the form is generated using `wtforms, flask_wtf`.
* It shall capture the username and password entered by the user and POST it to the same route.
* The username and password already have validators to ensure they fit within the parameters if the oragono ircd services. eg. NICKLEN 32
2. The bot will be triggered and it shall carry the information provided as arguments by connecting to the IRCd.
3. It will use the `USER` ,`NICK ` commands to register the connection on the IRCd then assign the nickname same as that passed on by the flask route.
* If no lines are recieved it shall throw a server error.
* If the nickname is already in use then the received the `433` code will be captured and translated back to the user as suggestion to retry with a different username
* If the `NICK` command is successful it shall proceed to the next step
4. Using the `PRIVMSG` command the bot shall register for the user and it shall read for `NOTICE` indicating successful account creation and carry that back to the flask app and be shown to the user.
5. TODO: If this fails add and unconditional which exits or it will be an infinite loop.
6. After this success the bot shall Die and the user will be redirected to the page which contains Rules and FAQs about login and features.

64
flaskapp.py Normal file
View File

@ -0,0 +1,64 @@
from flask import Flask, render_template, url_for, request, redirect, flash
from forms import RegistrationForm
from irc_register import ircregister
#from irc_verify import ircverify
app = Flask(__name__)
app.config['SECRET_KEY'] = '$secret' #remove later
@app.route('/')
def hello():
return render_template('home.html')
@app.route('/kiwi')
def kiwi():
return redirect("https://liberta.casa/kiwi/")
@app.route('/kiwi/')
def kiwinick(nick, show_password_box):
nick = request.args.get('nick', None)
show_password_box = requests.args.get('show_password_box', None)
return redirect("https://liberta.casa/kiwi/")
@app.route('/register', methods=['GET', 'POST'])
def register():
form = RegistrationForm()
if request.method == 'POST':
username = request.form.get('username')
email = request.form.get('email')
password = request.form.get('password')
# email = request.form.get('email') add password arg to ircregisterfunction
response = ircregister(username, password, email)
if response == "server failure":
flash("Server Unavailable")
elif response == "433":
flash("Username already taken. Please select a different username")
elif response == "success":
return redirect(url_for('kiwinick', nick=username, show_password_box='true'))
elif response == "failure":
flash("Failure! Please try after some time or use NickServ.")
return render_template('register.html', title='Register', form=form)
#@app.route('/verify', methods=['GET', 'POST'])
#def verify():
# form = VerificationForm()
# if request.method == 'POST':
#
# username = request.form.get('username')
# verif_code = request.form.get('verif_code')
# response = ircverify(username, verif_code)
# if response == "server failure":
# flash("Server Unavailable")
# elif response == "433":
# flash("Username under use. Please check your username or visit us for help")
# elif response == "success":
# return redirect(url_for('kiwi'))
# elif response == "failure":
# flash("Failure! Please try after some time or use NickServ.")
# return render_template('verify.html', title='Verify', form=form)
if __name__ == '__main__':
app.run(debug=True)

16
forms.py Normal file
View File

@ -0,0 +1,16 @@
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Length, EqualTo, Email
class RegistrationForm(FlaskForm):
username = StringField('Username', validators=[DataRequired(), Length(min=1, max=32)])
email = StringField('Email', validators=[DataRequired(), Email()])
password = PasswordField('Password', validators=[DataRequired()])
confirm_password = PasswordField('Confirm Password', validators=[DataRequired(), EqualTo('password')])
submit = SubmitField('Register')
#class VerificationForm(FlaskForm):
# username = StringField('Username', validators=[DataRequired(), Length(min=1, max=32)])
# verif_code = StringField('Verification Code', validators=[DataRequired()])
# submit = SubmitField('Verify')

106
irc_register.py Normal file
View File

@ -0,0 +1,106 @@
import socket, irctokens
import requests
import re
def ircregister(username, password, email):
# define the variables
d = irctokens.StatefulDecoder()
e = irctokens.StatefulEncoder()
s = socket.socket()
#connecting to the server
s.connect(("127.0.0.1", 6667))
#defining the send function with proper formatting
def _send(line):
print(f"> {line.format()}")
e.push(line)
while e.pending():
e.pop(s.send(e.pending()))
# registering the connection to the server
_send(irctokens.build("USER", [username, "0", "*", username]))
_send(irctokens.build("NICK", [username]))
server = 'http://192.168.0.115:8880'
realm = 'devel'
tokenurl = 'http://localhost/kctoken'
usererr = 'An error occured.'
emailverified = False
firstname = 'Foo'
lastname = 'Bar'
# go through the cases
while True:
lines = d.push(s.recv(1024))
if lines == None: #if nothing is received from server
return "server error"
break
for line in lines:
print(f"< {line.format()}")
if line.command == "433": # if nickname already in use
return "433"
elif line.command == "005": # when 005 is received pass the nickserv register command command
_send(irctokens.build("PRIVMSG", ["NickServ", f"REGISTER {password}"]))
if line.command == 'NOTICE' and line.params == [username, f"Account created"]:
_send(irctokens.build("QUIT"))
try:
tokendl = requests.get(tokenurl)
tokendata = tokendl.json()
token = tokendata['access_token']
url = server + '/auth/admin/realms/' + realm + '/users'
except:
print("ERROR: Keycloak token could not be installed.")
if re.match(r"[^@]+@[^@]+\.[^@]+", email):
payload = {
"firstName": firstname,
"lastName": lastname,
"email": email,
"enabled": "true",
"username": username,
"credentials": [{"type": "password", "value": password, "temporary": emailverified,}],
"emailVerified": emailverified
}
response = requests.post(
url,
headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token},
json = payload
)
print("Keycloak: HTTP Status ", response.status_code)
try:
print("Keycloak: Response Text: ", response.text)
except:
print("Keycloak: No or invalid response text. This is not an error.")
try:
print("Keycloak: Response JSON: ", response.json())
except:
print("Keycloak: No or invalid response JSON. This it not an error.")
status = response.status_code
if status == 201:
print(" SSO User " + username + " created.")
if status == 400:
print("ERROR: Keycloak indicated that the request is invalid.")
if status == 401:
print("ERROR: Fix your Keycloak API credentials and/or client roles, doh.")
if status == 403:
print("ERROR: Keycloak indicated that the authorization provided is not enough to access the resource.")
if status == 404:
print("ERROR: Keycloak indicated that the requested resource does not exist.")
if status == 409:
print("ERROR: Keycloak indicated that the resource already exists or \"some other coonflict when processing the request\" occured.")
if status == 415:
print("ERROR: Keycloak indicated that the requested media type is not supported.")
if status == 500:
print("ERROR: Keycloak indicated that the server could not fullfill the request due to \"some unexpected error \".")
else:
print('Invalid email address supplied.')
return "success"
# register("hello", "test")

45
irc_verify.py Normal file
View File

@ -0,0 +1,45 @@
import socket, irctokens
def ircverify(username, verif_code):
# define the variables
d = irctokens.StatefulDecoder()
e = irctokens.StatefulEncoder()
s = socket.socket()
#connecting to the server
s.connect(("127.0.0.1", 6667))
#defining the send function with proper formatting
def _send(line):
print(f"> {line.format()}")
e.push(line)
while e.pending():
e.pop(s.send(e.pending()))
# registering the connection to the server
_send(irctokens.build("USER", [username, "0", "*", username]))
_send(irctokens.build("NICK", [username]))
# go through the cases
while True:
lines = d.push(s.recv(1024))
if lines == None: #if nothing is received from server
return "server error"
break
for line in lines:
print(f"< {line.format()}")
if line.command == "433": # if nickname already in use
return "433"
elif line.command == "005": # when 005 is received pass the nickserv register command command
_send(irctokens.build("PRIVMSG", ["NickServ", f"VERIFY {username} {verif_code}"]))
if line.command == "NOTICE" and line.params == [username, "Account created"]: # if Services respond with appropriate notice NOTICE
_send(irctokens.build("QUIT"))
return "success"

13
requirements.old.txt Normal file
View File

@ -0,0 +1,13 @@
click==7.1.2
dnspython==1.16.0
email-validator==1.1.1
Flask==1.1.2
Flask-WTF==0.14.3
gunicorn==20.0.4
idna==2.9
irctokens>=1.0.1
itsdangerous==1.1.0
Jinja2==2.11.2
MarkupSafe==1.1.1
Werkzeug==1.0.1
WTForms==2.3.1

17
requirements.txt Normal file
View File

@ -0,0 +1,17 @@
certifi==2021.5.30
charset-normalizer==2.0.4
click==7.1.2
dnspython==1.16.0
email-validator==1.1.1
Flask==1.1.2
Flask-WTF==0.14.3
gunicorn==20.0.4
idna==2.9
irctokens==2.0.0
itsdangerous==1.1.0
Jinja2==2.11.2
MarkupSafe==1.1.1
requests==2.26.0
urllib3==1.26.6
Werkzeug==1.0.1
WTForms==2.3.1

44
templates/register.html Normal file
View File

@ -0,0 +1,44 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Register</title>
<style>
body {
background-color: black;
color: white;
}
</style>
</head>
<body>
{% for message in get_flashed_messages() %}
{{ message }}
{% endfor %}
<pre>
┬ ┌┬┐ ┬─┐ ┬─┐ ┬─┐ ┌┐┐ ┬─┐
│ │ │─│ │─ │┬┘ │ │─┤
┘─┘ └┴┘ │─┘ ┴─┘ │└┘ ┘ ┘ │
┌─┐ ┬─┐ ┐─┐ ┬─┐
│ │─┤ └─┐ │─┤
└─┘ ┘ │ ──┘ ┘ │
<form method="POST" action="/register">
{{ form.hidden_tag() }}
<fieldset>
<legend>Sign Up</legend>
{{ form.username.label }}
{{ form.username }}
{{ form.email.label }}
{{ form.email }}
{{ form.password.label }}
{{ form.password }}
{{ form.confirm_password.label }}
{{ form.confirm_password }}
</fieldset>
{{ form.submit }}
</form>
If you already have an account please login to the IRC using SASL.