Compare commits
No commits in common. "daf343535eca54261efd627e272f80adda75632f" and "bdfcebf439452044f7e9dff6cdb3114c3b3fb79e" have entirely different histories.
daf343535e
...
bdfcebf439
|
@ -1,45 +0,0 @@
|
|||
from flask_admin import Admin, helpers as admin_helpers
|
||||
from flask_admin.contrib.sqla import ModelView
|
||||
from flask_security import current_user, utils
|
||||
from wtforms.fields import PasswordField
|
||||
|
||||
from libertywiki.db import session
|
||||
from libertywiki.models import User, Role
|
||||
|
||||
|
||||
class UserAdmin(ModelView):
|
||||
"""A custom view for user administration"""
|
||||
column_exclude_list = ('password',)
|
||||
form_excluded_columns = ('password',)
|
||||
column_auto_select_related = True
|
||||
|
||||
def is_accessible(self):
|
||||
"""Prevent administration of users from users without the "admin" role"""
|
||||
return current_user.has_role('admin')
|
||||
|
||||
def scaffold_form(self):
|
||||
"""Remove the password field so that we can bypass it and set the password manually"""
|
||||
form_class = super().scaffold_form()
|
||||
form_class.password2 = PasswordField('New Password')
|
||||
return form_class
|
||||
|
||||
def on_model_change(self, form, model, is_created):
|
||||
"""Update the password when the user is saved"""
|
||||
if len(model.password2):
|
||||
model.password = utils.encrypt_password(model.password2)
|
||||
|
||||
|
||||
class RoleAdmin(ModelView):
|
||||
"""A custom view for role administration"""
|
||||
|
||||
def is_accessible(self):
|
||||
"""Prevent administration of roles from users without the "admin" role"""
|
||||
return current_user.has_role('admin')
|
||||
|
||||
|
||||
def setup_admin(app):
|
||||
"""Set up the admin interface"""
|
||||
admin = Admin(app, 'LibertyWiki Admin', base_template='admin/my_master.html', template_mode='bootstrap4')
|
||||
admin.add_view(UserAdmin(User, session))
|
||||
admin.add_view(RoleAdmin(Role, session))
|
||||
return admin, admin_helpers
|
|
@ -1,92 +1,26 @@
|
|||
import json
|
||||
import os
|
||||
import secrets
|
||||
from pathlib import Path
|
||||
from flask import Flask
|
||||
|
||||
import yaml
|
||||
from flask import Flask, url_for
|
||||
from flask_security import Security, SQLAlchemyUserDatastore
|
||||
from flask_security.utils import hash_password
|
||||
from rst2html import rst2html
|
||||
|
||||
from libertywiki.admin import setup_admin
|
||||
from libertywiki.db import db, session
|
||||
from libertywiki.models import User, Role
|
||||
from libertywiki.db import db
|
||||
from libertywiki.views import wiki
|
||||
|
||||
DEFAULT_CONFIG = {
|
||||
'DEBUG': False,
|
||||
'SECRET_KEY': secrets.token_urlsafe(128),
|
||||
'SECURITY_PASSWORD_SALT': secrets.token_urlsafe(128),
|
||||
'SECURITY_USERNAME_ENABLE': False,
|
||||
'SECURITY_REGISTERABLE': True,
|
||||
'SECURITY_SEND_REGISTER_EMAIL': False,
|
||||
'SQLALCHEMY_DATABASE_URI': 'sqlite://',
|
||||
'SQLALCHEMY_ENGINE_OPTIONS': {'pool_pre_ping': True},
|
||||
'SQLALCHEMY_TRACK_MODIFICATIONS': False
|
||||
}
|
||||
CONFIG_VARS = [
|
||||
'LIBERTYWIKI_CONFIG',
|
||||
'LW_CONFIG',
|
||||
'WIKI_CONFIG'
|
||||
]
|
||||
|
||||
|
||||
def get_app():
|
||||
"""Create and configure the application object"""
|
||||
app = Flask(__name__)
|
||||
|
||||
# Set app defaults
|
||||
app.config.update(DEFAULT_CONFIG)
|
||||
|
||||
# Load config from file
|
||||
for config_var in CONFIG_VARS:
|
||||
if os.environ.get(config_var):
|
||||
config_fname = Path(os.environ[config_var])
|
||||
if config_fname.suffix == 'json':
|
||||
loader = json.load
|
||||
elif config_fname.suffix in ['yaml', 'yml']:
|
||||
loader = yaml.safe_load
|
||||
with Path(os.environ[config_var]).open() as conf_file:
|
||||
app.config.update(**loader(conf_file))
|
||||
break
|
||||
|
||||
# Load from environment variables
|
||||
app.config.update(**{key: os.environ[key] for key in DEFAULT_CONFIG.keys() if os.environ.get(key)})
|
||||
|
||||
db.init_app(app)
|
||||
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
|
||||
security = Security(app, user_datastore)
|
||||
admin, admin_helpers = setup_admin(app)
|
||||
|
||||
with app.app_context():
|
||||
# Create all the tables in the database
|
||||
db.create_all()
|
||||
# Create the basic roles
|
||||
admin_role = user_datastore.find_or_create_role(name='admin', description='Administrator')
|
||||
user_datastore.find_or_create_role(name='end-user', description='End user')
|
||||
session.commit()
|
||||
# Create an administrator
|
||||
if not User.query.filter(User.roles.contains(admin_role)).first():
|
||||
user = user_datastore.create_user(name='Administrator', email='admin@example.com',
|
||||
password=hash_password('password1234'))
|
||||
user.roles.append(admin_role)
|
||||
session.commit()
|
||||
|
||||
@app.template_filter('rst2html')
|
||||
def rst2html_filter(text):
|
||||
html, warning = rst2html(text)
|
||||
print(html)
|
||||
print(warning)
|
||||
return html
|
||||
|
||||
@security.context_processor
|
||||
def security_context_processor():
|
||||
return dict(
|
||||
admin_base_template=admin.base_template,
|
||||
admin_view=admin.index_view,
|
||||
h=admin_helpers,
|
||||
get_url=url_for
|
||||
)
|
||||
|
||||
app.register_blueprint(wiki)
|
||||
|
||||
return app
|
||||
|
|
|
@ -12,7 +12,6 @@ LargeBinary = db.LargeBinary
|
|||
Table = db.Table
|
||||
String = db.String
|
||||
Text = db.Text
|
||||
backref = db.backref
|
||||
relationship = db.relationship
|
||||
inspect = db.inspect
|
||||
session = db.session
|
||||
|
|
|
@ -1,16 +1,9 @@
|
|||
from datetime import datetime
|
||||
|
||||
from flask_security import UserMixin, RoleMixin
|
||||
from sqlalchemy.ext.hybrid import hybrid_property
|
||||
|
||||
from libertywiki.db import Model, Table, Column, ForeignKey, Boolean, DateTime, Integer, String, Text, backref, \
|
||||
relationship
|
||||
|
||||
|
||||
roles_users = Table(
|
||||
'roles_users',
|
||||
Column('role_id', Integer, ForeignKey('roles.id')),
|
||||
Column('user_id', Integer, ForeignKey('users.id'))
|
||||
)
|
||||
from libertywiki.db import Model, Column, ForeignKey, DateTime, Integer, String, Text
|
||||
from libertywiki.utils import bcrypt
|
||||
|
||||
|
||||
class Page(Model):
|
||||
|
@ -28,24 +21,10 @@ class Page(Model):
|
|||
modified = Column(DateTime, default=datetime.now())
|
||||
|
||||
def __str__(self):
|
||||
return '<Page {}>'.format(self.title)
|
||||
return self.title
|
||||
|
||||
|
||||
class Role(Model, RoleMixin):
|
||||
"""
|
||||
Role model
|
||||
"""
|
||||
__tablename__ = 'roles'
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
name = Column(String(255), unique=True, index=True, nullable=False)
|
||||
description = Column(Text)
|
||||
|
||||
def __str__(self):
|
||||
return '<Role {}>'.format(self.name)
|
||||
|
||||
|
||||
class User(Model, UserMixin):
|
||||
class User(Model):
|
||||
"""
|
||||
User model
|
||||
"""
|
||||
|
@ -54,13 +33,13 @@ class User(Model, UserMixin):
|
|||
id = Column(Integer, primary_key=True)
|
||||
name = Column(String(255))
|
||||
email = Column(String(255), nullable=False, index=True, unique=True)
|
||||
password = Column(String(255), nullable=False)
|
||||
activation_code = Column(String(255), index=True)
|
||||
active = Column('is_active', Boolean, index=True)
|
||||
confirmed_at = Column(DateTime)
|
||||
fs_uniquifier = Column(String(255), unique=True, nullable=False, index=True)
|
||||
_password = Column('password', String(255), nullable=False)
|
||||
activation_code = Column(String(255))
|
||||
|
||||
roles = relationship('Role', secondary=roles_users, backref=backref('users', lazy='dynamic'))
|
||||
@hybrid_property
|
||||
def password(self):
|
||||
return self.password
|
||||
|
||||
def __str__(self):
|
||||
return '<User {}>'.format(self.email)
|
||||
@password.setter
|
||||
def password(self, value):
|
||||
self._password = bcrypt.generate_password_hash(value)
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
{% extends 'admin/master.html' %}
|
||||
{% block body %}
|
||||
{{ super() }}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-sm-10 col-sm-offset-1">
|
||||
<h1>Flask-Admin example</h1>
|
||||
<p class="lead">
|
||||
Authentication
|
||||
</p>
|
||||
<p>
|
||||
This example shows how you can use <a href="https://pythonhosted.org/Flask-Security/index.html" target="_blank">Flask-Security</a> for authentication.
|
||||
</p>
|
||||
{% if not current_user.is_authenticated %}
|
||||
<p>You can register as a regular user, or log in as a superuser with the following credentials:
|
||||
<ul>
|
||||
<li>email: <b>admin</b></li>
|
||||
<li>password: <b>admin</b></li>
|
||||
</ul>
|
||||
<p>
|
||||
<a class="btn btn-primary" href="{{ url_for('security.login') }}">login</a> <a class="btn btn-default" href="{{ url_for('security.register') }}">register</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
<p>
|
||||
<a class="btn btn-primary" href="/"><i class="glyphicon glyphicon-chevron-left"></i> Back</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock body %}
|
|
@ -1,18 +0,0 @@
|
|||
{% extends 'admin/base.html' %}
|
||||
|
||||
{% block access_control %}
|
||||
{% if current_user.is_authenticated %}
|
||||
<div class="navbar-text btn-group pull-right">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">
|
||||
<i class="glyphicon glyphicon-user"></i>
|
||||
{% if current_user.first_name -%}
|
||||
{{ current_user.first_name }}
|
||||
{% else -%}
|
||||
{{ current_user.email }}
|
||||
{%- endif %}<span class="caret"></span></a>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li><a href="{{ url_for('security.logout') }}">Log out</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
|
@ -1,27 +0,0 @@
|
|||
{% macro render_field_with_errors(field) %}
|
||||
|
||||
<div class="form-group">
|
||||
{{ field.label }} {{ field(class_='form-control', **kwargs)|safe }}
|
||||
{% if field.errors %}
|
||||
<ul>
|
||||
{% for error in field.errors %}
|
||||
<li>{{ error }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro render_field(field) %}
|
||||
<p>{{ field(class_='form-control', **kwargs)|safe }}</p>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro render_checkbox_field(field) -%}
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
{{ field(type='checkbox', **kwargs) }} {{ field.label }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{%- endmacro %}
|
|
@ -1,15 +0,0 @@
|
|||
{% if security.registerable or security.recoverable or security.confirmable %}
|
||||
<h2>Menu</h2>
|
||||
<ul>
|
||||
<li><a href="{{ url_for_security('login') }}{% if 'next' in request.args %}?next={{ request.args.next|urlencode }}{% endif %}">Login</a></li>
|
||||
{% if security.registerable %}
|
||||
<li><a href="{{ url_for_security('register') }}{% if 'next' in request.args %}?next={{ request.args.next|urlencode }}{% endif %}">Register</a><br/></li>
|
||||
{% endif %}
|
||||
{% if security.recoverable %}
|
||||
<li><a href="{{ url_for_security('forgot_password') }}">Forgot password</a><br/></li>
|
||||
{% endif %}
|
||||
{% if security.confirmable %}
|
||||
<li><a href="{{ url_for_security('send_confirmation') }}">Confirm account</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% endif %}
|
|
@ -1,9 +0,0 @@
|
|||
{%- with messages = get_flashed_messages(with_categories=true) -%}
|
||||
{% if messages %}
|
||||
<ul class="flashes">
|
||||
{% for category, message in messages %}
|
||||
<li class="{{ category }}">{{ message }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{%- endwith %}
|
|
@ -1,22 +0,0 @@
|
|||
{% extends 'admin/master.html' %}
|
||||
{% from "security/_macros.html" import render_field, render_field_with_errors, render_checkbox_field %}
|
||||
{% include "security/_messages.html" %}
|
||||
{% block body %}
|
||||
{{ super() }}
|
||||
<div class="row-fluid">
|
||||
<div class="col-sm-8 col-sm-offset-2">
|
||||
<h1>Login</h1>
|
||||
<div class="well">
|
||||
<form action="{{ url_for_security('login') }}" method="POST" name="login_user_form">
|
||||
{{ login_user_form.hidden_tag() }}
|
||||
{{ render_field_with_errors(login_user_form.email) }}
|
||||
{{ render_field_with_errors(login_user_form.password) }}
|
||||
{{ render_checkbox_field(login_user_form.remember) }}
|
||||
{{ render_field(login_user_form.next) }}
|
||||
{{ render_field(login_user_form.submit, class="btn btn-primary") }}
|
||||
</form>
|
||||
<p>Not yet signed up? Please <a href="{{ url_for('security.register') }}">register for an account</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock body %}
|
|
@ -1,23 +0,0 @@
|
|||
{% extends 'admin/master.html' %}
|
||||
{% from "security/_macros.html" import render_field_with_errors, render_field %}
|
||||
{% include "security/_messages.html" %}
|
||||
{% block body %}
|
||||
{{ super() }}
|
||||
<div class="row-fluid">
|
||||
<div class="col-sm-8 col-sm-offset-2">
|
||||
<h1>Register</h1>
|
||||
<div class="well">
|
||||
<form action="{{ url_for_security('register') }}" method="POST" name="register_user_form">
|
||||
{{ register_user_form.hidden_tag() }}
|
||||
{{ render_field_with_errors(register_user_form.email) }}
|
||||
{{ render_field_with_errors(register_user_form.password) }}
|
||||
{% if register_user_form.password_confirm %}
|
||||
{{ render_field_with_errors(register_user_form.password_confirm) }}
|
||||
{% endif %}
|
||||
{{ render_field(register_user_form.submit, class="btn btn-primary") }}
|
||||
</form>
|
||||
<p>Already signed up? Please <a href="{{ url_for('security.login') }}">log in</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock body %}
|
22
setup.cfg
22
setup.cfg
|
@ -1,32 +1,14 @@
|
|||
[metadata]
|
||||
name = libertywiki
|
||||
description = LibertyWiki is a simple, Flask-based open source wiki written in Python
|
||||
keywords = wiki, python, flask, restructuredtext
|
||||
license = MIT
|
||||
license_files = LICENSE
|
||||
url = https://git.libertytechforce.com/libertytechforce/libertywiki
|
||||
classifiers =
|
||||
Development Status :: 3 - Alpha
|
||||
Environment :: Web Environment
|
||||
Framework :: Flask
|
||||
License :: OSI Approved :: MIT License
|
||||
Operating System :: OS Independent
|
||||
Programming Language :: Python
|
||||
Programming Language :: Python :: 3
|
||||
Topic :: Documentation
|
||||
Topic :: Internet :: WWW/HTTP :: Dynamic Content :: Wiki
|
||||
version = 0.0.1
|
||||
|
||||
[options]
|
||||
packages = libertywiki
|
||||
install_requires =
|
||||
Flask
|
||||
Flask-Admin
|
||||
Flask-SQLAlchemy
|
||||
Flask-Bcrypt
|
||||
Flask-Security-Too
|
||||
bcrypt
|
||||
email_validator
|
||||
pyyaml
|
||||
Flask-Security
|
||||
rst2html
|
||||
setup_requires =
|
||||
setuptools_scm
|
||||
|
|
Loading…
Reference in New Issue