From e6e4f655f2abe8d1d5ff63ecd70255094af6de73 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Fri, 12 Feb 2016 01:09:01 -0600 Subject: let's go ahead and bite off more than we can chew by adding object-security we'll allow anyone to create pages, not just editors finally we'll allow page creators of pages to edit their pages even if they are not editors --- docs/tutorials/wiki2/definingmodels.rst | 65 +++++++++++------ docs/tutorials/wiki2/design.rst | 82 +++++++++++++--------- .../src/models/tutorial/scripts/initializedb.py | 6 +- 3 files changed, 94 insertions(+), 59 deletions(-) diff --git a/docs/tutorials/wiki2/definingmodels.rst b/docs/tutorials/wiki2/definingmodels.rst index b90bf77e6..5af8110da 100644 --- a/docs/tutorials/wiki2/definingmodels.rst +++ b/docs/tutorials/wiki2/definingmodels.rst @@ -106,28 +106,49 @@ made to both the models.py file and to the initializedb.py file. See Success will look something like this:: - 2015-05-24 15:34:14,542 INFO [sqlalchemy.engine.base.Engine:1192][MainThread] SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1 - 2015-05-24 15:34:14,542 INFO [sqlalchemy.engine.base.Engine:1193][MainThread] () - 2015-05-24 15:34:14,543 INFO [sqlalchemy.engine.base.Engine:1192][MainThread] SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1 - 2015-05-24 15:34:14,543 INFO [sqlalchemy.engine.base.Engine:1193][MainThread] () - 2015-05-24 15:34:14,543 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] PRAGMA table_info("pages") - 2015-05-24 15:34:14,544 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] () - 2015-05-24 15:34:14,544 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] - CREATE TABLE pages ( - id INTEGER NOT NULL, - name TEXT, - data TEXT, - PRIMARY KEY (id), - UNIQUE (name) - ) - - - 2015-05-24 15:34:14,545 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] () - 2015-05-24 15:34:14,546 INFO [sqlalchemy.engine.base.Engine:686][MainThread] COMMIT - 2015-05-24 15:34:14,548 INFO [sqlalchemy.engine.base.Engine:646][MainThread] BEGIN (implicit) - 2015-05-24 15:34:14,549 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] INSERT INTO pages (name, data) VALUES (?, ?) - 2015-05-24 15:34:14,549 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] ('FrontPage', 'This is the front page') - 2015-05-24 15:34:14,550 INFO [sqlalchemy.engine.base.Engine:686][MainThread] COMMIT + 2016-02-12 01:06:35,855 INFO [sqlalchemy.engine.base.Engine:1192][MainThread] SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1 + 2016-02-12 01:06:35,855 INFO [sqlalchemy.engine.base.Engine:1193][MainThread] () + 2016-02-12 01:06:35,855 INFO [sqlalchemy.engine.base.Engine:1192][MainThread] SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1 + 2016-02-12 01:06:35,855 INFO [sqlalchemy.engine.base.Engine:1193][MainThread] () + 2016-02-12 01:06:35,856 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] PRAGMA table_info("pages") + 2016-02-12 01:06:35,856 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] () + 2016-02-12 01:06:35,856 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] PRAGMA table_info("users") + 2016-02-12 01:06:35,856 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] () + 2016-02-12 01:06:35,857 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] + CREATE TABLE users ( + id INTEGER NOT NULL, + name TEXT NOT NULL, + role TEXT NOT NULL, + password_hash TEXT, + CONSTRAINT pk_users PRIMARY KEY (id), + CONSTRAINT uq_users_name UNIQUE (name) + ) + + + 2016-02-12 01:06:35,857 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] () + 2016-02-12 01:06:35,858 INFO [sqlalchemy.engine.base.Engine:686][MainThread] COMMIT + 2016-02-12 01:06:35,858 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] + CREATE TABLE pages ( + id INTEGER NOT NULL, + name TEXT NOT NULL, + data INTEGER NOT NULL, + creator_id INTEGER NOT NULL, + CONSTRAINT pk_pages PRIMARY KEY (id), + CONSTRAINT uq_pages_name UNIQUE (name), + CONSTRAINT fk_pages_creator_id_users FOREIGN KEY(creator_id) REFERENCES users (id) + ) + + + 2016-02-12 01:06:35,859 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] () + 2016-02-12 01:06:35,859 INFO [sqlalchemy.engine.base.Engine:686][MainThread] COMMIT + 2016-02-12 01:06:36,383 INFO [sqlalchemy.engine.base.Engine:646][MainThread] BEGIN (implicit) + 2016-02-12 01:06:36,384 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] INSERT INTO users (name, role, password_hash) VALUES (?, ?, ?) + 2016-02-12 01:06:36,384 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] ('editor', 'editor', '$2b$12$bSr5QR3wFs1LAnld7R94e.TXPj7DVoTxu2hA1kY6rm.Q3cAhD.AQO') + 2016-02-12 01:06:36,384 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] INSERT INTO users (name, role, password_hash) VALUES (?, ?, ?) + 2016-02-12 01:06:36,384 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] ('basic', 'basic', '$2b$12$.v0BQK2xWEQOnywbX2BFs.qzXo5Qf9oZohGWux/MOSj6Z.pVaY2Z6') + 2016-02-12 01:06:36,385 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] INSERT INTO pages (name, data, creator_id) VALUES (?, ?, ?) + 2016-02-12 01:06:36,385 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] ('FrontPage', 'This is the front page', 1) + 2016-02-12 01:06:36,385 INFO [sqlalchemy.engine.base.Engine:686][MainThread] COMMIT View the application in a browser --------------------------------- diff --git a/docs/tutorials/wiki2/design.rst b/docs/tutorials/wiki2/design.rst index 8e3bb4c13..42f06f7bf 100644 --- a/docs/tutorials/wiki2/design.rst +++ b/docs/tutorials/wiki2/design.rst @@ -19,11 +19,17 @@ Models We'll be using an SQLite database to hold our wiki data, and we'll be using :term:`SQLAlchemy` to access the data in this database. -Within the database, we define a single table named `pages`, whose elements -will store the wiki pages. There are two columns: `name` and `data`. +Within the database, we will define two tables: -URLs like ``/PageName`` will try to find an element in the table that has a -corresponding name. +- The `users` table which will store the `name`, `password_hash` and `role`. +- The `pages` table, whose elements will store the wiki pages. + There are three columns: `name`, `data` and `creator_id`. + +There is a one-to-many relationship between `users` and `pages` tracking +the user who created each wiki page. + +URLs like ``/PageName`` will try to find an element in the `pages` table that +has a corresponding name. To add a page to the wiki, a new row is created and the text is stored in `data`. @@ -32,8 +38,8 @@ A page named ``FrontPage`` containing the text *This is the front page*, will be created when the storage is initialized, and will be used as the wiki home page. -Views ------ +Wiki Views +---------- There will be three views to handle the normal operations of adding, editing, and viewing wiki pages, plus one view for the wiki front page. Two templates @@ -47,33 +53,41 @@ templates. Security -------- -We'll eventually be adding security to our application. The components we'll -use to do this are below. - -- USERS, a dictionary mapping :term:`userids ` to their corresponding - passwords. - -- GROUPS, a dictionary mapping :term:`userids ` to a list of groups to - which they belong. - -- ``groupfinder``, an *authorization callback* that looks up USERS and GROUPS. - It will be provided in a new ``security/default.py`` subpackage and file. - -- An :term:`ACL` is attached to the root :term:`resource`. Each row below - details an :term:`ACE`: - - +----------+----------------+----------------+ - | Action | Principal | Permission | - +==========+================+================+ - | Allow | Everyone | View | - +----------+----------------+----------------+ - | Allow | group:editors | Edit | - +----------+----------------+----------------+ - -- Permission declarations are added to the views to assert the security - policies as each request is handled. - -Two additional views and one template will handle the login and logout tasks. +We'll eventually be adding security to our application. To do this, we'll +be using a very simple role-based security model. We'll assign a single +role category to each user in our system. + +`basic` + An authenticated user who can view content and create new pages. A `basic` + user may also edit the pages they have created but not pages created by + other users. + +`editor` + An authenticated user who can create and edit any content in the system. + +In order to accomplish this we'll need to define an authentication policy +which can identify users by their :term:`userid` and role. Then we'll +need to define a page :term:`resource` which contains the appropriate +:term:`ACL`: + ++----------+--------------------+----------------+ +| Action | Principal | Permission | ++==========+====================+================+ +| Allow | Everyone | view | ++----------+--------------------+----------------+ +| Allow | group:basic | create | ++----------+--------------------+----------------+ +| Allow | group:editors | edit | ++----------+--------------------+----------------+ +| Allow | | edit | ++----------+--------------------+----------------+ + +Permission declarations will be added to the views to assert the security +policies as each request is handled. + +On the security side of the application there are two additional views for +handling login and logout as well as two exception views for handling +invalid access attempts and unhandled URLs. Summary ------- @@ -102,7 +116,7 @@ in the following table: | | submitted, redirect | | | | | | to /PageName | | | | +----------------------+-----------------------+-------------+----------------+------------+ -| /add_page/PageName | Create the page | add_page | edit.jinja2 | edit | +| /add_page/PageName | Create the page | add_page | edit.jinja2 | create | | | *PageName* in | | | | | | storage, display | | | | | | the edit form | | | | diff --git a/docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py index 175b7190f..f3c0a6fef 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py +++ b/docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py @@ -45,9 +45,9 @@ def main(argv=sys.argv): editor.set_password('editor') dbsession.add(editor) - viewer = User(name='viewer', role='viewer') - viewer.set_password('viewer') - dbsession.add(viewer) + basic = User(name='basic', role='basic') + basic.set_password('basic') + dbsession.add(basic) page = Page( name='FrontPage', -- cgit v1.2.3