Add Discourse set password integration
This commit is contained in:
parent
fd0e4b290a
commit
ccd44a063b
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 = ''
|
||||||
|
|
|
@ -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')
|
||||||
|
|
Loading…
Reference in New Issue
Block a user