Add Discourse set password integration

This commit is contained in:
Tanner Collin 2021-09-04 06:19:57 +00:00
parent fd0e4b290a
commit ccd44a063b
4 changed files with 168 additions and 2 deletions

View File

@ -2,15 +2,23 @@ from log import logger
import time import time
import secrets import secrets
import subprocess import subprocess
import requests
from uuid import uuid4
from flask import abort from flask import abort
HTTP_NOTFOUND = 404 HTTP_NOTFOUND = 404
random_email = lambda: 'spaceport-' + str(uuid4()).split('-')[0] + '@protospace.ca'
def set_wiki_password(username, password): def set_wiki_password(username, password):
# sets a user's wiki password # sets a user's wiki password
# creates the account if it doesn't exist # creates the account if it doesn't exist
if not secrets.WIKI_MAINTENANCE:
logger.error('Wiki setting not configured, aborting')
abort(400)
if not username: if not username:
logger.error('Empty username, aborting') logger.error('Empty username, aborting')
abort(400) abort(400)
@ -34,6 +42,133 @@ def set_wiki_password(username, password):
if result.stderr: if result.stderr:
abort(400) abort(400)
def discourse_api_get(url, params={}):
headers = {
'Api-Key': secrets.DISCOURSE_API_KEY,
'Api-Username': secrets.DISCOURSE_API_USER,
}
response = requests.get(url, headers=headers, params=params, timeout=10)
response.raise_for_status()
logger.info('Response: %s %s', response.status_code, response.text)
return response
def discourse_api_put(url, data={}):
headers = {
'Api-Key': secrets.DISCOURSE_API_KEY,
'Api-Username': secrets.DISCOURSE_API_USER,
}
response = requests.put(url, headers=headers, data=data, timeout=10)
response.raise_for_status()
logger.info('Response: %s %s', response.status_code, response.text)
return response
def discourse_api_post(url, data={}):
headers = {
'Api-Key': secrets.DISCOURSE_API_KEY,
'Api-Username': secrets.DISCOURSE_API_USER,
}
response = requests.post(url, headers=headers, data=data, timeout=10)
response.raise_for_status()
logger.info('Response: %s %s', response.status_code, response.text)
return response
def discourse_rails_script(script):
result = subprocess.run(['docker', 'exec', '-i', secrets.DISCOURSE_CONTAINER, 'rails', 'runner', script],
shell=False, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output = result.stdout or result.stderr
output = output.strip() or 'No complaints'
return result, output
def set_discourse_password(username, password, first_name, email):
# sets a user's discourse password
# creates the account if it doesn't exist
# things to test:
# - user changes Spaceport password
# - user changes Spaceport password to same
# - new Spaceport signup
# - existing Discourse user Spaceport signup
# - existing Discourse user Spaceport signup with same email
# note: Spaceport emails are unconfirmed!!
if not secrets.DISCOURSE_CONTAINER or not secrets.DISCOURSE_API_KEY or not secrets.DISCOURSE_API_USER:
logger.error('Discourse setting not configured, aborting')
abort(400)
if not username:
logger.error('Empty username, aborting')
abort(400)
if not password:
logger.error('Empty password, aborting')
abort(400)
if not first_name:
logger.error('Empty first_name, aborting')
abort(400)
if not email:
logger.error('Empty email, aborting')
abort(400)
logger.info('Checking Discourse for existing email: ' + email)
params = {
'filter': email,
'show_emails': 'true',
}
response = discourse_api_get('https://forum.protospace.ca/admin/users/list/active.json', params)
response = response.json()
for user in response:
if user['email'] == email:
if user['username'] == username:
logger.info('Username match, skipping')
continue
new_email = random_email()
logger.info('Email found on different user %s, changing to: %s', user['username'], new_email)
script = 'UserEmail.find_by(email: "{}").update!(email: "{}")'.format(email, new_email)
result, output = discourse_rails_script(script)
logger.info('Confirming email change...')
response = discourse_api_get('https://forum.protospace.ca/admin/users/list/active.json', params)
if len(response.json()):
logger.error('Email change failed, aborting')
abort(400)
logger.info('Creating Discourse user for: ' + username)
data = {
'name': first_name,
'username': username,
'password': password,
'email': email,
'active': True,
'approved': True,
}
response = discourse_api_post('https://forum.protospace.ca/users.json', data)
response = response.json()
if response['success']:
logger.info('Skipping set password')
return True
logger.info('User exists, setting Discourse password for: ' + username)
script = 'User.find_by(username: "{}").update!(password: "{}")'.format(username, password)
result, output = discourse_rails_script(script)
if 'Password is the same' in result.stderr:
logger.info('Output: Password is the same as your current password. (ActiveRecord::RecordInvalid)')
return True
else:
logger.info('Output: ' + output)
if result.stderr:
abort(400)
if __name__ == '__main__': if __name__ == '__main__':
set_wiki_password('tanner.collin', 'protospace1') #set_wiki_password('tanner.collin', 'protospace1')
set_discourse_password('test8a', 'protospace1', 'testie', 'test8@example.com')
pass pass

View File

@ -1,6 +1,12 @@
certifi==2021.5.30
charset-normalizer==2.0.4
click==7.1.2 click==7.1.2
Flask==1.1.2 Flask==1.1.2
idna==3.2
itsdangerous==1.1.0 itsdangerous==1.1.0
Jinja2==2.11.3 Jinja2==2.11.3
logging-tree==1.9
MarkupSafe==1.1.1 MarkupSafe==1.1.1
requests==2.26.0
urllib3==1.26.6
Werkzeug==1.0.1 Werkzeug==1.0.1

View File

@ -10,3 +10,16 @@ AUTH_TOKEN = ''
# Probably: # Probably:
# /var/www/wiki/maintenance # /var/www/wiki/maintenance
WIKI_MAINTENANCE = '' WIKI_MAINTENANCE = ''
# The ID of the Docker container.
# Find it with docker ps
# Probably something like:
# 9c81ac530cdd
DISCOURSE_CONTAINER = ''
# API key created here:
# https://forum.protospace.ca/admin/api/keys
DISCOURSE_API_KEY = ''
# Username who created the API key
DISCOURSE_API_USER = ''

View File

@ -23,7 +23,7 @@ def index():
def ping(): def ping():
return 'pong' return 'pong'
@app.route('/set-password', methods=['POST']) @app.route('/set-wiki-password', methods=['POST'])
def set_password(): def set_password():
check_auth() check_auth()
@ -33,5 +33,17 @@ def set_password():
auth_functions.set_wiki_password(username, password) auth_functions.set_wiki_password(username, password)
return '' return ''
@app.route('/set-discourse-password', methods=['POST'])
def set_password():
check_auth()
username = request.form['username']
password = request.form['password']
first_name = request.form['first_name']
email = request.form['email']
auth_functions.set_discourse_password(username, password, first_name, email)
return ''
if __name__ == '__main__': if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0') app.run(debug=True, host='0.0.0.0')