add config option to disable registration
add command to create user
This commit is contained in:
parent
17eabddd70
commit
b8531a0347
43
manage.py
43
manage.py
|
@ -1,18 +1,43 @@
|
||||||
from gevent import wsgi
|
from gevent import wsgi
|
||||||
from realms import config, app, manager
|
from realms import config, app, cli, db
|
||||||
from flask.ext.script import Server
|
from werkzeug.serving import run_with_reloader
|
||||||
|
import click
|
||||||
manager.add_command("runserver", Server(host="0.0.0.0", port=5000))
|
import os
|
||||||
|
|
||||||
|
|
||||||
@manager.command
|
@cli.command()
|
||||||
|
@click.option('--port', default=5000)
|
||||||
|
def runserver(port):
|
||||||
|
""" Run development server
|
||||||
|
"""
|
||||||
|
click.secho("Starting development server", fg='green')
|
||||||
|
app.run(host="0.0.0.0",
|
||||||
|
port=port,
|
||||||
|
debug=True)
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command()
|
||||||
def run():
|
def run():
|
||||||
|
""" Run production server
|
||||||
"""
|
"""
|
||||||
Run production ready server
|
click.echo("Server started. Env: %s Port: %s" % (config.ENV, config.PORT))
|
||||||
"""
|
|
||||||
print "Server started. Env: %s Port: %s" % (config.ENV, config.PORT)
|
|
||||||
wsgi.WSGIServer(('', int(config.PORT)), app).serve_forever()
|
wsgi.WSGIServer(('', int(config.PORT)), app).serve_forever()
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command()
|
||||||
|
def create_db():
|
||||||
|
""" Creates DB tables
|
||||||
|
"""
|
||||||
|
click.echo("Creating all tables")
|
||||||
|
db.create_all()
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command()
|
||||||
|
def drop_db():
|
||||||
|
""" Drops DB tables
|
||||||
|
"""
|
||||||
|
click.echo("Dropping all tables")
|
||||||
|
db.drop_all()
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
manager.run()
|
cli()
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Monkey patch stdlib.
|
# Monkey patch stdlib.
|
||||||
import gevent.monkey
|
import gevent.monkey
|
||||||
gevent.monkey.patch_all(aggressive=False)
|
gevent.monkey.patch_all(aggressive=False, subprocess=True)
|
||||||
|
|
||||||
# Set default encoding to UTF-8
|
# Set default encoding to UTF-8
|
||||||
import sys
|
import sys
|
||||||
|
@ -14,9 +14,9 @@ import sys
|
||||||
import json
|
import json
|
||||||
import httplib
|
import httplib
|
||||||
import traceback
|
import traceback
|
||||||
|
import click
|
||||||
from flask import Flask, request, render_template, url_for, redirect, g
|
from flask import Flask, request, render_template, url_for, redirect, g
|
||||||
from flask.ext.cache import Cache
|
from flask.ext.cache import Cache
|
||||||
from flask.ext.script import Manager
|
|
||||||
from flask.ext.login import LoginManager, current_user
|
from flask.ext.login import LoginManager, current_user
|
||||||
from flask.ext.sqlalchemy import SQLAlchemy
|
from flask.ext.sqlalchemy import SQLAlchemy
|
||||||
from flask.ext.assets import Environment, Bundle
|
from flask.ext.assets import Environment, Bundle
|
||||||
|
@ -67,9 +67,9 @@ class Application(Flask):
|
||||||
if hasattr(sources, 'views'):
|
if hasattr(sources, 'views'):
|
||||||
self.register_blueprint(sources.views.blueprint)
|
self.register_blueprint(sources.views.blueprint)
|
||||||
|
|
||||||
# Flask-Script
|
# Click
|
||||||
if hasattr(sources, 'commands'):
|
if hasattr(sources, 'commands'):
|
||||||
manager.add_command(module_name, sources.commands.manager)
|
cli.add_command(sources.commands.cli, name=module_name)
|
||||||
|
|
||||||
print >> sys.stderr, ' * Ready in %.2fms' % (1000.0 * (time.time() - start_time))
|
print >> sys.stderr, ' * Ready in %.2fms' % (1000.0 * (time.time() - start_time))
|
||||||
|
|
||||||
|
@ -142,40 +142,39 @@ def error_handler(e):
|
||||||
return response, status_code
|
return response, status_code
|
||||||
|
|
||||||
|
|
||||||
def create_app():
|
|
||||||
app = Application(__name__)
|
|
||||||
app.config.from_object('realms.config')
|
|
||||||
app.url_map.converters['regex'] = RegexConverter
|
|
||||||
app.url_map.strict_slashes = False
|
|
||||||
|
|
||||||
for status_code in httplib.responses:
|
app = Application(__name__)
|
||||||
if status_code >= 400:
|
app.config.from_object('realms.config')
|
||||||
app.register_error_handler(status_code, error_handler)
|
app.url_map.converters['regex'] = RegexConverter
|
||||||
|
app.url_map.strict_slashes = False
|
||||||
|
|
||||||
@app.before_request
|
for status_code in httplib.responses:
|
||||||
def init_g():
|
if status_code >= 400:
|
||||||
g.assets = dict(css=['main.css'], js=['main.js'])
|
app.register_error_handler(status_code, error_handler)
|
||||||
|
|
||||||
@app.template_filter('datetime')
|
@app.before_request
|
||||||
def _jinja2_filter_datetime(ts):
|
def init_g():
|
||||||
return time.strftime('%b %d, %Y %I:%M %p', time.localtime(ts))
|
g.assets = dict(css=['main.css'], js=['main.js'])
|
||||||
|
|
||||||
@app.errorhandler(404)
|
@app.template_filter('datetime')
|
||||||
def page_not_found(e):
|
def _jinja2_filter_datetime(ts):
|
||||||
return render_template('errors/404.html'), 404
|
return time.strftime('%b %d, %Y %I:%M %p', time.localtime(ts))
|
||||||
|
|
||||||
if config.RELATIVE_PATH:
|
@app.errorhandler(404)
|
||||||
@app.route("/")
|
def page_not_found(e):
|
||||||
def root():
|
return render_template('errors/404.html'), 404
|
||||||
return redirect(url_for(config.ROOT_ENDPOINT))
|
|
||||||
|
|
||||||
return app
|
if config.RELATIVE_PATH:
|
||||||
|
@app.route("/")
|
||||||
|
def root():
|
||||||
|
return redirect(url_for(config.ROOT_ENDPOINT))
|
||||||
|
|
||||||
app = create_app()
|
|
||||||
|
@click.group()
|
||||||
|
def cli():
|
||||||
|
pass
|
||||||
|
|
||||||
# Init plugins here if possible
|
# Init plugins here if possible
|
||||||
manager = Manager(app)
|
|
||||||
|
|
||||||
login_manager = LoginManager(app)
|
login_manager = LoginManager(app)
|
||||||
login_manager.login_view = 'auth.login'
|
login_manager.login_view = 'auth.login'
|
||||||
|
|
||||||
|
@ -204,7 +203,7 @@ assets.register('main.css',
|
||||||
|
|
||||||
app.discover()
|
app.discover()
|
||||||
|
|
||||||
# Should be called explicitly during install?
|
# This will be removed at some point
|
||||||
db.create_all()
|
db.create_all()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -37,19 +37,31 @@ RECAPTCHA_OPTIONS = {}
|
||||||
|
|
||||||
SECRET_KEY = 'K3dRq1q9eN72GJDkgvyshFVwlqHHCyPI'
|
SECRET_KEY = 'K3dRq1q9eN72GJDkgvyshFVwlqHHCyPI'
|
||||||
|
|
||||||
|
# Path on file system where wiki data will reside
|
||||||
WIKI_PATH = os.path.join(APP_PATH, 'wiki')
|
WIKI_PATH = os.path.join(APP_PATH, 'wiki')
|
||||||
|
|
||||||
|
# Name of page that will act as home
|
||||||
WIKI_HOME = 'home'
|
WIKI_HOME = 'home'
|
||||||
|
|
||||||
ALLOW_ANON = True
|
ALLOW_ANON = True
|
||||||
|
REGISTRATION_ENABLED = True
|
||||||
|
|
||||||
|
# Used by Flask-Login
|
||||||
LOGIN_DISABLED = ALLOW_ANON
|
LOGIN_DISABLED = ALLOW_ANON
|
||||||
|
|
||||||
# Page names that can't be modified
|
# Page names that can't be modified
|
||||||
LOCKED = []
|
WIKI_LOCKED_PAGES = []
|
||||||
|
# Depreciated variable name
|
||||||
|
LOCKED = WIKI_LOCKED_PAGES
|
||||||
|
|
||||||
ROOT_ENDPOINT = 'wiki.page'
|
ROOT_ENDPOINT = 'wiki.page'
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(os.path.join(APP_PATH, 'config.json')) as f:
|
with open(os.path.join(APP_PATH, 'config.json')) as f:
|
||||||
__settings = json.load(f)
|
__settings = json.load(f)
|
||||||
|
for k in ['APP_PATH', 'USER_HOME']:
|
||||||
|
if k in __settings:
|
||||||
|
del __settings[k]
|
||||||
globals().update(__settings)
|
globals().update(__settings)
|
||||||
except IOError:
|
except IOError:
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -2,6 +2,8 @@ import re
|
||||||
import os
|
import os
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
|
import string
|
||||||
|
import random
|
||||||
|
|
||||||
|
|
||||||
class AttrDict(dict):
|
class AttrDict(dict):
|
||||||
|
@ -10,6 +12,10 @@ class AttrDict(dict):
|
||||||
self.__dict__ = self
|
self.__dict__ = self
|
||||||
|
|
||||||
|
|
||||||
|
def random_string(size=6, chars=string.ascii_lowercase + string.ascii_uppercase + string.digits):
|
||||||
|
return ''.join(random.choice(chars) for _ in range(size))
|
||||||
|
|
||||||
|
|
||||||
def to_json(data):
|
def to_json(data):
|
||||||
return json.dumps(to_dict(data), separators=(',', ':'))
|
return json.dumps(to_dict(data), separators=(',', ':'))
|
||||||
|
|
||||||
|
|
35
realms/modules/auth/commands.py
Normal file
35
realms/modules/auth/commands.py
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import click
|
||||||
|
from realms.lib.util import random_string
|
||||||
|
from realms.modules.auth.models import User
|
||||||
|
|
||||||
|
|
||||||
|
@click.group()
|
||||||
|
def cli():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command()
|
||||||
|
@click.argument('username')
|
||||||
|
@click.argument('email')
|
||||||
|
@click.option('--password', help='Leave blank for random password')
|
||||||
|
def create_user(username, email, password):
|
||||||
|
""" Create a new user
|
||||||
|
"""
|
||||||
|
show_pass = not password
|
||||||
|
|
||||||
|
if not password:
|
||||||
|
password = random_string(12)
|
||||||
|
|
||||||
|
if User.get_by_username(username):
|
||||||
|
click.secho("Username %s already exists" % username, fg='red')
|
||||||
|
return
|
||||||
|
|
||||||
|
if User.get_by_email(email):
|
||||||
|
click.secho("Email %s already exists" % email, fg='red')
|
||||||
|
return
|
||||||
|
|
||||||
|
User.create(username, email, password)
|
||||||
|
click.secho("User %s created" % username, fg='green')
|
||||||
|
|
||||||
|
if show_pass:
|
||||||
|
click.secho("Password: %s" % password, fg='yellow')
|
|
@ -2,6 +2,7 @@ from flask_wtf import Form, RecaptchaField
|
||||||
from wtforms import StringField, PasswordField, validators
|
from wtforms import StringField, PasswordField, validators
|
||||||
from realms import config
|
from realms import config
|
||||||
|
|
||||||
|
|
||||||
class RegistrationForm(Form):
|
class RegistrationForm(Form):
|
||||||
username = StringField('Username', [validators.Length(min=4, max=25)])
|
username = StringField('Username', [validators.Length(min=4, max=25)])
|
||||||
email = StringField('Email Address', [validators.Length(min=6, max=35)])
|
email = StringField('Email Address', [validators.Length(min=6, max=35)])
|
||||||
|
|
|
@ -33,6 +33,11 @@ def login():
|
||||||
|
|
||||||
@blueprint.route("/register", methods=['GET', 'POST'])
|
@blueprint.route("/register", methods=['GET', 'POST'])
|
||||||
def register():
|
def register():
|
||||||
|
|
||||||
|
if not config.REGISTRATION_ENABLED:
|
||||||
|
flash("Registration is disabled")
|
||||||
|
return redirect(url_for(config.ROOT_ENDPOINT))
|
||||||
|
|
||||||
form = RegistrationForm()
|
form = RegistrationForm()
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
|
@ -61,6 +66,7 @@ def register():
|
||||||
def settings():
|
def settings():
|
||||||
return render_template("auth/settings.html")
|
return render_template("auth/settings.html")
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route("/logout")
|
@blueprint.route("/logout")
|
||||||
def logout():
|
def logout():
|
||||||
User.logout()
|
User.logout()
|
||||||
|
|
|
@ -33,7 +33,7 @@ def revert():
|
||||||
commit = request.form.get('commit')
|
commit = request.form.get('commit')
|
||||||
cname = to_canonical(name)
|
cname = to_canonical(name)
|
||||||
|
|
||||||
if cname in config.LOCKED:
|
if cname in config.WIKI_LOCKED_PAGES:
|
||||||
flash("Page is locked")
|
flash("Page is locked")
|
||||||
return redirect(url_for(config.ROOT_ENDPOINT))
|
return redirect(url_for(config.ROOT_ENDPOINT))
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ def edit(name):
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
edit_cname = to_canonical(request.form['name'])
|
edit_cname = to_canonical(request.form['name'])
|
||||||
|
|
||||||
if edit_cname in config.LOCKED:
|
if edit_cname in config.WIKI_LOCKED_PAGES:
|
||||||
return redirect(url_for(config.ROOT_ENDPOINT))
|
return redirect(url_for(config.ROOT_ENDPOINT))
|
||||||
|
|
||||||
if edit_cname.lower() != cname.lower():
|
if edit_cname.lower() != cname.lower():
|
||||||
|
@ -91,7 +91,7 @@ def create(name):
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
cname = to_canonical(request.form['name'])
|
cname = to_canonical(request.form['name'])
|
||||||
|
|
||||||
if cname in config.LOCKED:
|
if cname in config.WIKI_LOCKED_PAGES:
|
||||||
return redirect(url_for("wiki.create"))
|
return redirect(url_for("wiki.create"))
|
||||||
|
|
||||||
if not cname:
|
if not cname:
|
||||||
|
|
|
@ -59,8 +59,10 @@
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
{% else %}
|
{% else %}
|
||||||
<li><a href="{{ url_for('auth.login') }}"><i class="icon-user"></i> Login</a></li>
|
<li><a href="{{ url_for('auth.login') }}"><i class="icon-user"></i> Login</a></li>
|
||||||
|
{% if config.REGISTRATION_ENABLED %}
|
||||||
<li><a href="{{ url_for('auth.register') }}"><i class="icon-pencil"></i> Register</a></li>
|
<li><a href="{{ url_for('auth.register') }}"><i class="icon-pencil"></i> Register</a></li>
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</div><!--/.nav-collapse -->
|
</div><!--/.nav-collapse -->
|
||||||
|
|
|
@ -3,7 +3,6 @@ Flask
|
||||||
Flask-Assets
|
Flask-Assets
|
||||||
Flask-Cache
|
Flask-Cache
|
||||||
Flask-Login
|
Flask-Login
|
||||||
Flask-Script
|
|
||||||
Flask-SQLAlchemy
|
Flask-SQLAlchemy
|
||||||
Flask-WTF
|
Flask-WTF
|
||||||
beautifulsoup4
|
beautifulsoup4
|
||||||
|
@ -15,3 +14,4 @@ lxml
|
||||||
markdown2
|
markdown2
|
||||||
simplejson
|
simplejson
|
||||||
PyYAML
|
PyYAML
|
||||||
|
click
|
||||||
|
|
Loading…
Reference in a new issue