[TASK] Initial commit.

This commit is contained in:
Jan Philipp Timme 2014-09-30 18:11:38 +02:00
commit 0da26c151a
38 changed files with 681 additions and 0 deletions

9
.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
settings.py
*.pyc
*~
*.sqlite
.project
.settings
.buildpath
.pydevproject

97
app.py Normal file
View File

@ -0,0 +1,97 @@
# -*- coding: utf-8 -*-
from flask import Flask, render_template_string, request
from flask.ext.babel import Babel
from flask.ext.mail import Mail
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.user import current_user, login_required, SQLAlchemyAdapter, UserManager, UserMixin
from flask.ext.user import roles_required
# Import config file containing secret information and more things.
from settings import ConfigClass, username_validator
from flask.templating import render_template
def create_app(test_config=None): # For automated tests
# Setup Flask and read config from ConfigClass defined above
app = Flask(__name__)
app.config.from_object(__name__ + '.ConfigClass')
# Load local_settings.py if file exists # For automated tests
try: app.config.from_object('local_settings')
except: pass
# Load optional test_config # For automated tests
if test_config:
app.config.update(test_config)
# Initialize Flask extensions
babel = Babel(app) # Initialize Flask-Babel
mail = Mail(app) # Initialize Flask-Mail
db = SQLAlchemy(app) # Initialize Flask-SQLAlchemy
@babel.localeselector
def get_locale():
translations = [str(translation) for translation in babel.list_translations()]
return request.accept_languages.best_match(translations)
# Define the User-Roles pivot table
user_roles = db.Table('user_roles',
db.Column('id', db.Integer(), primary_key=True),
db.Column('user_id', db.Integer(), db.ForeignKey('user.id', ondelete='CASCADE')),
db.Column('role_id', db.Integer(), db.ForeignKey('role.id', ondelete='CASCADE')))
# Define Role model
class Role(db.Model):
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(50), unique=True)
# Define User model. Make sure to add flask.ext.user UserMixin!!
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
active = db.Column(db.Boolean(), nullable=False, default=False)
username = db.Column(db.String(50), nullable=False, unique=True)
email = db.Column(db.String(255), nullable=False, unique=True)
confirmed_at = db.Column(db.DateTime())
password = db.Column(db.String(255), nullable=False, default='')
reset_password_token = db.Column(db.String(100), nullable=False, default='')
# Relationships
roles = db.relationship('Role', secondary=user_roles, backref=db.backref('users', lazy='dynamic'))
# Reset all the database tables
db.create_all()
# Setup Flask-User
db_adapter = SQLAlchemyAdapter(db, User)
user_manager = UserManager(db_adapter, username_validator=username_validator)
user_manager.init_app(app)
# Create the default admin user if not exists.
if not User.query.filter(User.username == ConfigClass.DDNS_ADMIN_USERNAME).first():
adminuser = User(username=ConfigClass.DDNS_ADMIN_USERNAME, email=ConfigClass.DDNS_ADMIN_EMAIL, active=True,
password=user_manager.hash_password(ConfigClass.DDNS_ADMIN_PASSWORD))
adminuser.roles.append(Role(name='admin'))
db.session.add(adminuser)
db.session.commit()
@app.route('/')
def home_page():
if current_user.is_authenticated():
return profile_page()
return render_template('index.html')
@app.route('/profile')
@login_required
def profile_page():
return render_template('profile.html')
@app.route('/special')
@roles_required('admin')
def special_page():
return render_template('admin.html')
return app
""" Start development web server """
if __name__ == '__main__':
app = create_app()
app.run(host='0.0.0.0', port=5000, debug=True)

1
requirements.txt Normal file
View File

@ -0,0 +1 @@
flask-user

38
settings.example.py Normal file
View File

@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
from wtforms.validators import ValidationError
class ConfigClass(object):
""" Configuration class """
SECRET_KEY = 'THIS IS AN INSECURE SECRET' # Change this for production!
SQLALCHEMY_DATABASE_URI = 'sqlite:///minimal_app.sqlite' # Use Sqlite file db
CSRF_ENABLED = True
USER_ENABLE_EMAIL = True
USER_ENABLE_CHANGE_USERNAME = True
USER_ENABLE_CHANGE_PASSWORD = True
USER_ENABLE_FORGOT_PASSWORD = True
USER_ENABLE_REGISTRATION = True # Turn to False to disable registration.
""" Configure Flask-Mail """
MAIL_SERVER = 'your.mailserver.here'
MAIL_PORT = 587
MAIL_USE_SSL = True
MAIL_USE_TLS = True # Use this for STARTTLS!
MAIL_USERNAME = ''
MAIL_PASSWORD = ''
MAIL_DEFAULT_SENDER = '"Example Sender" <cool@service.example>'
""" Default Admin User """
DDNS_ADMIN_USERNAME = 'admin'
DDNS_ADMIN_PASSWORD = 'SuperSecretAdminPassword'
DDNS_ADMIN_EMAIL = 'admin@service.example'
def username_validator(form, field):
""" Since usernames will be used for subdomains, take your time here. """
username = field.data
if len(username) < 4:
raise ValidationError(_('Username must be at least 4 characters long'))
if not username.isalnum():
raise ValidationError(_('Username may only contain letters and numbers'))

4
templates/admin.html Normal file
View File

@ -0,0 +1,4 @@
{% extends "base.html" %}
{% block content %}
<h1>Admin area!</h1>
{% endblock %}

58
templates/base.html Normal file
View File

@ -0,0 +1,58 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Simple Dynamic DNS Service</title>
<!-- Bootstrap -->
<link href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">
<style>
.col-centered { float: none; margin: 0 auto; }
hr { border-color: #cccccc; }
</style>
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7/html5shiv.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.js"></script>
<![endif]-->
</head>
<body>
{% block body %}
<div class="container">
<div class="row">
<div class="col-sm-8 col-md-7 col-lg-6 col-centered">
<h1><a href="/">{% block title %}Simple Dynamic DNS Service{% endblock %}</a></h1>
<hr>
{% block flash_messages %}
{%- with messages = get_flashed_messages(with_categories=true) -%}
{% if messages %}
{% for category, message in messages %}
{% if category=='error' %}
{% set category='danger' %}
{% endif %}
<div class="alert alert-{{category}}">{{ message|safe }}</div>
{% endfor %}
{% endif %}
{%- endwith %}
{% endblock %}
{% block content %}
{% endblock %}
<hr>
Have fun!
</div>
</div>
</div>
{% endblock %}
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
</body>
</html>

View File

@ -0,0 +1,39 @@
{% macro render_field(field, label=None, label_visible=true, right_url=None, right_label=None) -%}
<div class="form-group {% if field.errors %}has-error{% endif %} {{ kwargs.pop('class_', '') }}">
{% if field.type != 'HiddenField' and label_visible %}
{% if not label %}{% set label=field.label.text %}{% endif %}
<label for="{{ field.id }}" class="control-label">{{ label|safe }}</label>
{% endif %}
{{ field(class_='form-control', **kwargs) }}
{% if field.errors %}
{% for e in field.errors %}
<p class="help-block">{{ e }}</p>
{% endfor %}
{% endif %}
</div>
{%- endmacro %}
{% macro render_checkbox_field(field, label=None) -%}
{% if not label %}{% set label=field.label.text %}{% endif %}
<div class="checkbox">
<label>
{{ field(type='checkbox', **kwargs) }} {{ field.label }}
</label>
</div>
{%- endmacro %}
{% macro render_radio_field(field) -%}
{% for value, label, _ in field.iter_choices() %}
<div class="radio">
<label>
<input type="radio" name="{{ field.id }}" id="{{ field.id }}" value="{{ value }}">{{ label }}
</label>
</div>
{% endfor %}
{%- endmacro %}
{% macro render_submit_field(field, label=None, tabindex=None) -%}
{% if not label %}{% set label=field.label.text %}{% endif %}
{#<button type="submit" class="form-control btn btn-default btn-primary">{{label}}</button>#}
<input type="submit" value="{{label}}" {% if tabindex %}tabindex="{{ tabindex }}"{% endif %}>
{%- endmacro %}

View File

@ -0,0 +1,17 @@
{% extends 'flask_user/member_base.html' %}
{% block content %}
{% from "flask_user/_macros.html" import render_field, render_submit_field %}
<h1>{%trans%}Change Password{%endtrans%}</h1>
<form action="" method="POST" class="form" role="form">
{{ form.hidden_tag() }}
{{ render_field(form.old_password, tabindex=10) }}
{{ render_field(form.new_password, tabindex=20) }}
{% if user_manager.enable_retype_password %}
{{ render_field(form.retype_password, tabindex=30) }}
{% endif %}
{{ render_submit_field(form.submit, tabindex=90) }}
</form>
{% endblock %}

View File

@ -0,0 +1,14 @@
{% extends 'flask_user/member_base.html' %}
{% block content %}
{% from "flask_user/_macros.html" import render_field, render_submit_field %}
<h1>{%trans%}Change Username{%endtrans%}</h1>
<form action="" method="POST" class="form" role="form">
{{ form.hidden_tag() }}
{{ render_field(form.new_username, tabindex=10) }}
{{ render_field(form.old_password, tabindex=20) }}
{{ render_submit_field(form.submit, tabindex=90) }}
</form>
{% endblock %}

View File

@ -0,0 +1,7 @@
<p>Dear User,</p>
{% block message %}
{% endblock %}
<p>Sincerely,<br/>
The Flask-User Team</p>

View File

@ -0,0 +1,7 @@
Dear User,
{% block message %}
{% endblock %}
Sincerely,
The Flask-User Team

View File

@ -0,0 +1 @@
Flask-User: {% block subject %}{% endblock %}

View File

@ -0,0 +1,15 @@
{% extends 'flask_user/emails/base_message.html' %}
{% block message %}
<p>Thank you for registering with Flask-User.</p>
{% if user_manager.enable_confirm_email %}
<p>Visit the link below to complete your registration:</p>
<p><a href="{{ confirm_email_link }}?next=/">Confirm your email address</a>.</p>
<p>If you did not initiate this registration, you may safely ignore this email.</p>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,13 @@
{% extends 'flask_user/emails/base_message.txt' %}
{% block message %}
Thank you for registering with Flask-User.
{% if user_manager.enable_confirm_email -%}
Visit the link below to complete your registration:
{{ confirm_email_link }}
If you did not initiate this registration, you may safely ignore this email.
{%- endif %}
{% endblock %}

View File

@ -0,0 +1,3 @@
{% extends 'flask_user/emails/base_subject.txt' %}
{% block subject %}Registration Confirmation{% endblock %}

View File

@ -0,0 +1,13 @@
{% extends 'flask_user/emails/base_message.html' %}
{% block message %}
<p>We have received your password reset request.</p>
<p>Visit the link below to provide a new password:</p>
<p><a href="{{ reset_password_link }}">Reset your password</a>.</p>
<p>If you did not initiate this password reset request, you may safely ignore this email.</p>
{% endblock %}

View File

@ -0,0 +1,11 @@
{% extends 'flask_user/emails/base_message.txt' %}
{% block message %}
We have received your password reset request.
Visit the link below to provide a new password:
{{ reset_password_link }}
If you did not initiate this password reset request, you may safely ignore this email.
{% endblock %}

View File

@ -0,0 +1,3 @@
{% extends 'flask_user/emails/base_subject.txt' %}
{% block subject %}Reset password{% endblock %}

View File

@ -0,0 +1,8 @@
{% extends 'flask_user/emails/base_message.html' %}
{% block message %}
<p>Your password has been changed.</p>
{% if user_manager.enable_forgot_password %}
<p>If you did not change your password, <a href="{{ url_for('user.forgot_password', _external=True) }}">click here to reset it</a>.</p>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,12 @@
{% extends 'flask_user/emails/base_message.txt' %}
{% block message %}
Your password has been changed.
{% if user_manager.enable_forgot_password -%}
If you did not change your password, click the link below to reset it.
{{ url_for('user.forgot_password', _external=True) }}
{% endif -%}
{% endblock %}

View File

@ -0,0 +1,3 @@
{% extends 'flask_user/emails/base_subject.txt' %}
{% block subject %}Your password has been changed{% endblock %}

View File

@ -0,0 +1,7 @@
{% extends 'flask_user/emails/base_message.html' %}
{% block message %}
<p>Thank you for registering with Flask-User.</p>
{% endblock %}

View File

@ -0,0 +1,6 @@
{% extends 'flask_user/emails/base_message.txt' %}
{% block message %}
Thank you for registering with Flask-User.
{% endblock %}

View File

@ -0,0 +1,3 @@
{% extends 'flask_user/emails/base_subject.txt' %}
{% block subject %}Registration Confirmation{% endblock %}

View File

@ -0,0 +1,6 @@
{% extends 'flask_user/emails/base_message.html' %}
{% block message %}
<p>Your username has been changed.</p>
<p>If you did not change your username, please <a href="{{ url_for('user.login', _external=True) }}">sign in</a> (using your email address) and change your password.</p>
{% endblock %}

View File

@ -0,0 +1,10 @@
{% extends 'flask_user/emails/base_message.txt' %}
{% block message %}
Your username has been changed.
If you did not change your username, please sign in (using your email address) and change your password.
{{ url_for('user.login', _external=True) }}
{% endblock %}

View File

@ -0,0 +1,3 @@
{% extends 'flask_user/emails/base_subject.txt' %}
{% block subject %}Your username has been changed{% endblock %}

View File

@ -0,0 +1,13 @@
{% extends 'flask_user/public_base.html' %}
{% block content %}
{% from "flask_user/_macros.html" import render_field, render_submit_field %}
<h1>{%trans%}Forgot Password{%endtrans%}</h1>
<form action="" method="POST" class="form" role="form">
{{ form.hidden_tag() }}
{{ render_field(form.email, tabindex=10) }}
{{ render_submit_field(form.submit, tabindex=90) }}
</form>
{% endblock %}

View File

@ -0,0 +1,60 @@
{% extends 'flask_user/public_base.html' %}
{% block content %}
{% from "flask_user/_macros.html" import render_field, render_submit_field %}
<h1>{%trans%}Sign in{%endtrans%}</h1>
<form action="" method="POST" class="form" role="form">
{{ form.hidden_tag() }}
{# Username or Email #}
{% set field = form.username if user_manager.enable_username else form.email %}
<div class="form-group {% if field.errors %}has-error{% endif %}">
{# Label on left, "New here? Register." on right #}
<div class="row">
<div class="col-xs-6">
<label for="{{ field.id }}" class="control-label">{{ field.label.text }}</label>
</div>
<div class="col-xs-6 text-right">
{% if user_manager.enable_register and not user_manager.require_invitation %}
<a href="{{ url_for('user.register') }}" tabindex='190'>
{%trans%}New here? Register.{%endtrans%}</a>
{% endif %}
</div>
</div>
{{ field(class_='form-control', tabindex=110) }}
{% if field.errors %}
{% for e in field.errors %}
<p class="help-block">{{ e }}</p>
{% endfor %}
{% endif %}
</div>
{# Password #}
{% set field = form.password %}
<div class="form-group {% if field.errors %}has-error{% endif %}">
{# Label on left, "Forgot your Password?" on right #}
<div class="row">
<div class="col-xs-6">
<label for="{{ field.id }}" class="control-label">{{ field.label.text }}</label>
</div>
<div class="col-xs-6 text-right">
{% if user_manager.enable_forgot_password %}
<a href="{{ url_for('user.forgot_password') }}" tabindex='195'>
{%trans%}Forgot your Password?{%endtrans%}</a>
{% endif %}
</div>
</div>
{{ field(class_='form-control', tabindex=120) }}
{% if field.errors %}
{% for e in field.errors %}
<p class="help-block">{{ e }}</p>
{% endfor %}
{% endif %}
</div>
{# Submit button #}
{{ render_submit_field(form.submit, tabindex=180) }}
</form>
{% endblock %}

View File

@ -0,0 +1,61 @@
{% extends 'flask_user/public_base.html' %}
{% block content %}
{% from "flask_user/_macros.html" import render_field, render_submit_field %}
<div class="row">
<div class="col-sm-6">
<h1>{%trans%}Sign in{%endtrans%}</h1>
{# ** Login form ** #}
<form action="{{ url_for('user.login') }}" method="POST" class="form" role="form">
{{ login_form.hidden_tag() }}
{# Username or Email #}
{% set field = login_form.username if user_manager.enable_username else login_form.email %}
{{ render_field(field, tabindex=110) }}
{# Password #}
{{ render_field(login_form.password, tabindex=120) }}
{# Submit button #}
{{ render_submit_field(login_form.submit, tabindex=180) }}
</form>
{% if user_manager.enable_forgot_password %}
<p>
<br/>
<a href="{{ url_for('user.forgot_password') }}" tabindex='190'>
{%trans%}Forgot your Password?{%endtrans%}</a>
</p>
{% endif %}
</div>
<div class="col-sm-6">
<h1>{%trans%}Register{%endtrans%}</h1>
{# ** Register form ** #}
<form action="{{ url_for('user.register') }}" method="POST" novalidate formnovalidate class="form" role="form">
{{ register_form.hidden_tag() }}
{# Username or Email #}
{% set field = register_form.username if user_manager.enable_username else register_form.email %}
{{ render_field(field, tabindex=210) }}
{% if user_manager.enable_email and user_manager.enable_username %}
{{ render_field(register_form.email, tabindex=220) }}
{% endif %}
{{ render_field(register_form.password, tabindex=230) }}
{% if user_manager.enable_retype_password %}
{{ render_field(register_form.retype_password, tabindex=240) }}
{% endif %}
{{ render_submit_field(register_form.submit, tabindex=280) }}
</form>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,39 @@
{% extends 'flask_user/member_base.html' %}
{% block content %}
{% from "flask_user/_macros.html" import render_field, render_submit_field %}
<h1>{%trans%}Manage Emails{%endtrans%}</h1>
<table class="table">
<tr><th>Email</th><th>Status</th><th>Actions</th></tr>
{% for user_email in user_emails %}
<tr>
<td>{{ user_email.email }}</td>
<td>
{% if user_email.confirmed_at %}
Confirmed
{% else %}
<a href="{{ url_for('user.email_action', id=user_email.id, action='confirm') }}">Confirm Email</a>
{% endif %}
</td>
<td>
{% if user_email.is_primary %}
<b>Primary email</b>
{% else %}
{% if user_email.confirmed_at %}
<a href="{{ url_for('user.email_action', id=user_email.id, action='make-primary') }}">Make primary</a> |
{% endif %}
<a href="{{ url_for('user.email_action', id=user_email.id, action='delete') }}">Delete</a>
{% endif %}
</td>
</tr>
{% endfor %}
</table>
<form action="" method="POST" class="form" role="form">
{{ form.hidden_tag() }}
{{ render_field(form.email) }}
{{ render_submit_field(form.submit) }}
</form>
{% endblock %}

View File

@ -0,0 +1,2 @@
{% extends 'base.html' %}

View File

@ -0,0 +1,2 @@
{% extends 'base.html' %}

View File

@ -0,0 +1,46 @@
{% extends 'flask_user/public_base.html' %}
{% block content %}
{% from "flask_user/_macros.html" import render_field, render_submit_field %}
<h1>{%trans%}Register{%endtrans%}</h1>
<form action="" method="POST" novalidate formnovalidate class="form" role="form">
{{ form.hidden_tag() }}
{# Username or Email #}
{% set field = form.username if user_manager.enable_username else form.email %}
<div class="form-group {% if field.errors %}has-error{% endif %}">
{# Label on left, "Already registered? Sign in." on right #}
<div class="row">
<div class="col-xs-6">
<label for="{{ field.id }}" class="control-label">{{ field.label.text }}</label>
</div>
<div class="col-xs-6 text-right">
{% if user_manager.enable_register %}
<a href="{{ url_for('user.login') }}" tabindex='290'>
{%trans%}Already registered? Sign in.{%endtrans%}</a>
{% endif %}
</div>
</div>
{{ field(class_='form-control', tabindex=210) }}
{% if field.errors %}
{% for e in field.errors %}
<p class="help-block">{{ e }}</p>
{% endfor %}
{% endif %}
</div>
{% if user_manager.enable_email and user_manager.enable_username %}
{{ render_field(form.email, tabindex=220) }}
{% endif %}
{{ render_field(form.password, tabindex=230) }}
{% if user_manager.enable_retype_password %}
{{ render_field(form.retype_password, tabindex=240) }}
{% endif %}
{{ render_submit_field(form.submit, tabindex=280) }}
</form>
{% endblock %}

View File

@ -0,0 +1,13 @@
{% extends 'flask_user/public_base.html' %}
{% block content %}
{% from "flask_user/_macros.html" import render_field, render_submit_field %}
<h1>{%trans%}Resend Confirmation Email{%endtrans%}</h1>
<form action="" method="POST" class="form" role="form">
{{ form.hidden_tag() }}
{{ render_field(form.email, tabindex=10) }}
{{ render_submit_field(form.submit, tabindex=90) }}
</form>
{% endblock %}

View File

@ -0,0 +1,16 @@
{% extends 'flask_user/public_base.html' %}
{% block content %}
{% from "flask_user/_macros.html" import render_field, render_submit_field %}
<h1>{%trans%}Reset Password{%endtrans%}</h1>
<form action="" method="POST" class="form" role="form">
{{ form.hidden_tag() }}
{{ render_field(form.new_password, tabindex=10) }}
{% if user_manager.enable_retype_password %}
{{ render_field(form.retype_password, tabindex=20) }}
{% endif %}
{{ render_submit_field(form.submit, tabindex=90) }}
</form>
{% endblock %}

9
templates/index.html Normal file
View File

@ -0,0 +1,9 @@
{% extends "base.html" %}
{% block content %}
<h1>{%trans%}Welcome!{%endtrans%}</h1>
<p>
<a href="{{ url_for('user.login') }}">{%trans%}Sign in{%endtrans%}</a> or
<a href="{{ url_for('user.register') }}">{%trans%}Register{%endtrans%}</a>
</p>
{% endblock %}

12
templates/profile.html Normal file
View File

@ -0,0 +1,12 @@
{% extends "base.html" %}
{% block content %}
<h2>{%trans%}Profile Page{%endtrans%}</h2>
<p>
{%trans%}Hello{%endtrans%} {{ current_user.username or current_user.email }}!
</p>
<ul>
<li><a href="{{ url_for('user.change_username') }}">{%trans%}Change username{%endtrans%}</a></li>
<li><a href="{{ url_for('user.change_password') }}">{%trans%}Change password{%endtrans%}</a></li>
<li><a href="{{ url_for('user.logout') }}?next={{ url_for('user.login') }}">{%trans%}Sign out{%endtrans%}</a></li>
</ul>
{% endblock %}