Schema changes + introducing CSRF. You have to run python3 -m migrations.0_6to0_7 to continue using
This commit is contained in:
parent
e449e06b5d
commit
0b5286fb8a
3 changed files with 40 additions and 59 deletions
|
|
@ -2,6 +2,11 @@
|
||||||
|
|
||||||
## 0.7
|
## 0.7
|
||||||
|
|
||||||
|
+ Schema changes:
|
||||||
|
+ Removed `PagePolicy` and `PagePolicyKey` tables altogether. They were never useful.
|
||||||
|
+ Added `calendar` field to `Page`.
|
||||||
|
+ Added `User` table.
|
||||||
|
+ Added `Flask-Login` and `Flask-WTF` dependencies in order to implement user logins.
|
||||||
+ Added `python-i18n` as a dependency. Therefore, i18n changed format, using JSON files now.
|
+ Added `python-i18n` as a dependency. Therefore, i18n changed format, using JSON files now.
|
||||||
+ Like it or not, now gzip library is required.
|
+ Like it or not, now gzip library is required.
|
||||||
|
|
||||||
|
|
|
||||||
85
app.py
85
app.py
|
|
@ -15,6 +15,8 @@ Application is kept compact, with all its core in a single file.
|
||||||
from flask import (
|
from flask import (
|
||||||
Flask, Markup, abort, flash, g, jsonify, make_response, redirect, request,
|
Flask, Markup, abort, flash, g, jsonify, make_response, redirect, request,
|
||||||
render_template, send_from_directory)
|
render_template, send_from_directory)
|
||||||
|
from flask_login import LoginManager
|
||||||
|
from flask_wtf import CSRFProtect
|
||||||
from werkzeug.routing import BaseConverter
|
from werkzeug.routing import BaseConverter
|
||||||
from peewee import *
|
from peewee import *
|
||||||
from playhouse.db_url import connect as dbconnect
|
from playhouse.db_url import connect as dbconnect
|
||||||
|
|
@ -130,14 +132,27 @@ def _passphrase_hash(pp):
|
||||||
h = str(len(pp_bin)) + ':' + hashlib.sha256(pp_bin).hexdigest()
|
h = str(len(pp_bin)) + ':' + hashlib.sha256(pp_bin).hexdigest()
|
||||||
return h
|
return h
|
||||||
|
|
||||||
|
class User(BaseModel):
|
||||||
|
username = CharField(32, unique=True)
|
||||||
|
email = CharField(256, null=True)
|
||||||
|
password = CharField()
|
||||||
|
join_date = DateTimeField(default=datetime.datetime.now)
|
||||||
|
karma = IntegerField(default=1)
|
||||||
|
privileges = BitField()
|
||||||
|
is_admin = privileges.flag(1)
|
||||||
|
|
||||||
|
|
||||||
class Page(BaseModel):
|
class Page(BaseModel):
|
||||||
url = CharField(64, unique=True, null=True)
|
url = CharField(64, unique=True, null=True)
|
||||||
title = CharField(256, index=True)
|
title = CharField(256, index=True)
|
||||||
touched = DateTimeField(index=True)
|
touched = DateTimeField(index=True)
|
||||||
|
calendar = DateTimeField(index=True, null=True)
|
||||||
|
owner = ForeignKeyField(User, null=True)
|
||||||
flags = BitField()
|
flags = BitField()
|
||||||
is_redirect = flags.flag(1)
|
is_redirect = flags.flag(1)
|
||||||
is_sync = flags.flag(2)
|
is_sync = flags.flag(2)
|
||||||
is_math_enabled = flags.flag(4)
|
is_math_enabled = flags.flag(4)
|
||||||
|
is_locked = flags.flag(8)
|
||||||
@property
|
@property
|
||||||
def latest(self):
|
def latest(self):
|
||||||
if self.revisions:
|
if self.revisions:
|
||||||
|
|
@ -174,22 +189,6 @@ class Page(BaseModel):
|
||||||
@property
|
@property
|
||||||
def prop(self):
|
def prop(self):
|
||||||
return PagePropertyDict(self)
|
return PagePropertyDict(self)
|
||||||
def unlock(self, perm, pp, sec):
|
|
||||||
## XX complete later!
|
|
||||||
policies = self.policies.where(PagePolicy.type << _makelist(perm))
|
|
||||||
if not policies.exists():
|
|
||||||
return True
|
|
||||||
for policy in policies:
|
|
||||||
if policy.verify(pp, sec):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
def is_locked(self, perm):
|
|
||||||
policies = self.policies.where(PagePolicy.type << _makelist(perm))
|
|
||||||
return policies.exists()
|
|
||||||
def is_classified(self):
|
|
||||||
return self.is_locked(POLICY_CLASSIFY)
|
|
||||||
def is_editable(self):
|
|
||||||
return not self.is_locked(POLICY_EDIT)
|
|
||||||
|
|
||||||
|
|
||||||
class PageText(BaseModel):
|
class PageText(BaseModel):
|
||||||
|
|
@ -223,7 +222,7 @@ class PageText(BaseModel):
|
||||||
|
|
||||||
class PageRevision(BaseModel):
|
class PageRevision(BaseModel):
|
||||||
page = FK(Page, backref='revisions', index=True)
|
page = FK(Page, backref='revisions', index=True)
|
||||||
user_id = IntegerField(default=0)
|
user = ForeignKeyField(User, null=True)
|
||||||
comment = CharField(1024, default='')
|
comment = CharField(1024, default='')
|
||||||
textref = FK(PageText)
|
textref = FK(PageText)
|
||||||
pub_date = DateTimeField(index=True)
|
pub_date = DateTimeField(index=True)
|
||||||
|
|
@ -310,42 +309,6 @@ class PagePropertyDict(object):
|
||||||
return PageProperty.select().where((PageProperty.page == self._page) &
|
return PageProperty.select().where((PageProperty.page == self._page) &
|
||||||
(PageProperty.key == key)).exists()
|
(PageProperty.key == key)).exists()
|
||||||
|
|
||||||
# Store keys for PagePolicy.
|
|
||||||
# Experimental.
|
|
||||||
class PagePolicyKey(BaseModel):
|
|
||||||
passphrase = CharField()
|
|
||||||
sec_code = IntegerField()
|
|
||||||
class Meta:
|
|
||||||
indexes = (
|
|
||||||
(('passphrase','sec_code'), True),
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def create_from_plain(cls, pp, sec):
|
|
||||||
PagePolicyKey.create(passphrase=_passphrase_hash(pp), sec_code=sec)
|
|
||||||
def verify(self, pp, sec):
|
|
||||||
h = _passphrase_hash(pp)
|
|
||||||
return self.passphrase == h and self.sec_code == sec
|
|
||||||
|
|
||||||
POLICY_ADMIN = 1
|
|
||||||
POLICY_READ = 2
|
|
||||||
POLICY_EDIT = 3
|
|
||||||
POLICY_META = 4
|
|
||||||
POLICY_CLASSIFY = 5
|
|
||||||
|
|
||||||
# Manage policies for pages (e.g., reading or editing).
|
|
||||||
# Experimental.
|
|
||||||
class PagePolicy(BaseModel):
|
|
||||||
page = FK(Page, backref='policies', index=True, null=True)
|
|
||||||
type = IntegerField()
|
|
||||||
key = FK(PagePolicyKey, backref='applied_to')
|
|
||||||
sitewide = IntegerField(default=0)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
indexes = (
|
|
||||||
(('page', 'key'), True),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# Link table for caching purposes.
|
# Link table for caching purposes.
|
||||||
class PageLink(BaseModel):
|
class PageLink(BaseModel):
|
||||||
|
|
@ -389,7 +352,9 @@ class PageLink(BaseModel):
|
||||||
|
|
||||||
|
|
||||||
def init_db():
|
def init_db():
|
||||||
database.create_tables([Page, PageText, PageRevision, PageTag, PageProperty, PagePolicyKey, PagePolicy, PageLink])
|
database.create_tables([
|
||||||
|
Page, PageText, PageRevision, PageTag, PageProperty, PageLink
|
||||||
|
])
|
||||||
|
|
||||||
#### WIKI SYNTAX ####
|
#### WIKI SYNTAX ####
|
||||||
|
|
||||||
|
|
@ -447,9 +412,12 @@ forbidden_urls = [
|
||||||
]
|
]
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.secret_key = 'qrdldCcvamtdcnidmtasegasdsedrdqvtautar'
|
app.secret_key = b'\xf3\xa9?\xbee$L\xabA\xd3\r\xa2\x08\xf6\x00%0b\xa9\xfe\x11\x04\xa6\xd8=\xd3\xa2\x00\xb3\xd5;9'
|
||||||
app.url_map.converters['slug'] = SlugConverter
|
app.url_map.converters['slug'] = SlugConverter
|
||||||
|
|
||||||
|
csrf = CSRFProtect(app)
|
||||||
|
login_manager = LoginManager(app)
|
||||||
|
|
||||||
|
|
||||||
#### ROUTES ####
|
#### ROUTES ####
|
||||||
|
|
||||||
|
|
@ -475,6 +443,11 @@ def _inject_variables():
|
||||||
'material_icons_url': _getconf('site', 'material_icons_url')
|
'material_icons_url': _getconf('site', 'material_icons_url')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@login_manager.user_loader
|
||||||
|
def _inject_user(userid):
|
||||||
|
return User[userid]
|
||||||
|
|
||||||
|
|
||||||
@app.template_filter()
|
@app.template_filter()
|
||||||
def linebreaks(text):
|
def linebreaks(text):
|
||||||
text = html.escape(text)
|
text = html.escape(text)
|
||||||
|
|
@ -713,7 +686,7 @@ def page_leaderboard():
|
||||||
pages.append((p, score, p.back_links.count(), p.forward_links.count(), p.latest.length))
|
pages.append((p, score, p.back_links.count(), p.forward_links.count(), p.latest.length))
|
||||||
pages.sort(key = lambda x: (x[1], x[2], x[4], x[3]), reverse = True)
|
pages.sort(key = lambda x: (x[1], x[2], x[4], x[3]), reverse = True)
|
||||||
|
|
||||||
return render_template('leaderboard.html', pages=pages)
|
return render_template('leaderboard.html', pages=pages), headers
|
||||||
|
|
||||||
@app.route('/<slug:name>/')
|
@app.route('/<slug:name>/')
|
||||||
def view_named(name):
|
def view_named(name):
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
from playhouse.migrate import migrate, SqliteMigrator, MySQLMigrator
|
from playhouse.migrate import migrate, SqliteMigrator, MySQLMigrator
|
||||||
from peewee import MySQLDatabase, SqliteDatabase
|
from peewee import MySQLDatabase, SqliteDatabase, \
|
||||||
from app import database
|
IntegerField, DateTimeField, ForeignKeyField
|
||||||
|
from app import database, User
|
||||||
|
|
||||||
if type(database) == MySQLDatabase:
|
if type(database) == MySQLDatabase:
|
||||||
migrator = MySQLMigrator(database)
|
migrator = MySQLMigrator(database)
|
||||||
|
|
@ -11,7 +12,9 @@ else:
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
with database.atomic():
|
with database.atomic():
|
||||||
|
database.create_tables([User])
|
||||||
migrate(
|
migrate(
|
||||||
|
migrator.add_column('page', 'calendar', DateTimeField(index=True, null=True)),
|
||||||
|
migrator.add_column('page', 'owner_id', IntegerField(null=True))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue