diff --git a/config.py b/config.py index d0d2d8d..28b6574 100644 --- a/config.py +++ b/config.py @@ -25,3 +25,7 @@ MAKEDIRS = [ ] TRANSLATION_LANGUAGES = ['en', 'bg'] +APP_BASE_URL = "https://blog.pi2.dev" +SECURITY_ENABLED = True +SSO_BASE_URL = 'https://sso.localhost:5008' +SSO_CLIENT_ID = APP_BASE_URL diff --git a/data_static/User.csv b/data_static/User.csv index 4206147..5b31ede 100644 --- a/data_static/User.csv +++ b/data_static/User.csv @@ -1,2 +1,2 @@ -email,password,role_names -admin@blog.pi2.dev,__SENSITIVE__.ADMIN_PASSWORD,admin \ No newline at end of file +username,_m_n_roles +daniel,1 \ No newline at end of file diff --git a/migrations/versions/40f7b6561b4a_002.py b/migrations/versions/40f7b6561b4a_002.py new file mode 100644 index 0000000..1491216 --- /dev/null +++ b/migrations/versions/40f7b6561b4a_002.py @@ -0,0 +1,40 @@ +"""002 + +Revision ID: 40f7b6561b4a +Revises: d091fbf48f6f +Create Date: 2021-05-15 00:35:30.800690 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '40f7b6561b4a' +down_revision = 'd091fbf48f6f' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('permission', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('uuid', sa.Unicode(), nullable=True), + sa.Column('subject', sa.UnicodeText(), nullable=True), + sa.Column('subject_id', sa.Integer(), nullable=True), + sa.Column('action', sa.UnicodeText(), nullable=True), + sa.Column('object', sa.UnicodeText(), nullable=True), + sa.Column('object_id', sa.Integer(), nullable=True), + sa.Column('is_allowed', sa.Boolean(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_index(op.f('ix_permission_uuid'), 'permission', ['uuid'], unique=False) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index(op.f('ix_permission_uuid'), table_name='permission') + op.drop_table('permission') + # ### end Alembic commands ### diff --git a/migrations/versions/d091fbf48f6f_001.py b/migrations/versions/d091fbf48f6f_001.py index 985ea69..b919e88 100644 --- a/migrations/versions/d091fbf48f6f_001.py +++ b/migrations/versions/d091fbf48f6f_001.py @@ -40,8 +40,7 @@ def upgrade(): op.create_table('user', sa.Column('id', sa.Integer(), nullable=False), sa.Column('uuid', sa.Unicode(), nullable=True), - sa.Column('email', sa.Unicode(), nullable=True), - sa.Column('password', sa.Unicode(), nullable=True), + sa.Column('username', sa.Unicode(), nullable=True), sa.Column('active', sa.Boolean(), nullable=True), sa.Column('confirmed_at', sa.DateTime(), nullable=True), sa.Column('timezone', sa.String(), nullable=True), diff --git a/run.py b/run.py index 47fc2fe..77896b0 100644 --- a/run.py +++ b/run.py @@ -12,6 +12,5 @@ app.template_folder = TEMPLATES_FOLDER app.static_folder = STATIC_FOLDER - if __name__ == "__main__": app.run(debug=True) diff --git a/webapp/models/__init__.py b/webapp/models/__init__.py index 569feb8..92ee32e 100644 --- a/webapp/models/__init__.py +++ b/webapp/models/__init__.py @@ -1,3 +1,4 @@ from oshipka.persistance import db from webapp.models.tag import Tag from webapp.models.blog_post import BlogPost +from webapp.models.permission import Permission diff --git a/webapp/models/_blog_post.py b/webapp/models/_blog_post.py index 9e15c7e..bc40000 100644 --- a/webapp/models/_blog_post.py +++ b/webapp/models/_blog_post.py @@ -9,6 +9,11 @@ blog_post__tag = db.Table('blog_post__tag', class BlogPost(db.Model, ModelController, Ownable): __searchable__ = ['body', ] + _file_columns = [] + + model_acls = {'get': {'authn': False, 'authz': []}, 'list': {'authn': False, 'authz': []}, 'table': {'authn': False, 'authz': []}, 'search': { + 'authn': False, 'authz': []}, 'create': {'authn': True, 'authz': ['admin']}, 'update': {'authn': True, 'authz': ['admin']}, 'delete': {'authn': True, 'authz': ['admin']}} + filename = db.Column(db.UnicodeText,) title = db.Column(db.UnicodeText,) body = db.Column(db.UnicodeText,) diff --git a/webapp/models/_permission.py b/webapp/models/_permission.py new file mode 100644 index 0000000..d601d5a --- /dev/null +++ b/webapp/models/_permission.py @@ -0,0 +1,19 @@ +from oshipka.persistance import db, ModelController, index_service, LiberalBoolean, Ownable + + +class Permission(db.Model, ModelController): + + _file_columns = [] + + model_acls = {'get': {'authn': True, 'authz': []}, 'list': {'authn': True, 'authz': []}, 'table': {'authn': True, 'authz': []}, 'search': { + 'authn': True, 'authz': []}, 'create': {'authn': True, 'authz': []}, 'update': {'authn': True, 'authz': []}, 'delete': {'authn': True, 'authz': []}} + + subject = db.Column(db.UnicodeText,) + subject_id = db.Column(db.Integer,) + action = db.Column(db.UnicodeText,) + object = db.Column(db.UnicodeText,) + object_id = db.Column(db.Integer,) + is_allowed = db.Column(LiberalBoolean,) + + def __repr__(self): + return "{} ({} - {})".format(self.subject, self.action, self.object) diff --git a/webapp/models/_tag.py b/webapp/models/_tag.py index 11ba1c5..b783d41 100644 --- a/webapp/models/_tag.py +++ b/webapp/models/_tag.py @@ -2,6 +2,12 @@ from oshipka.persistance import db, ModelController, index_service, LiberalBoole class Tag(db.Model, ModelController, Ownable): + + _file_columns = [] + + model_acls = {'get': {'authn': False, 'authz': []}, 'list': {'authn': False, 'authz': []}, 'table': {'authn': False, 'authz': []}, 'search': { + 'authn': False, 'authz': []}, 'create': {'authn': True, 'authz': ['admin']}, 'update': {'authn': True, 'authz': ['admin']}, 'delete': {'authn': True, 'authz': ['admin']}} + name = db.Column(db.UnicodeText,) def __repr__(self): diff --git a/webapp/models/permission.py b/webapp/models/permission.py new file mode 100644 index 0000000..14da211 --- /dev/null +++ b/webapp/models/permission.py @@ -0,0 +1 @@ +from webapp.models._permission import Permission diff --git a/webapp/routes/__init__.py b/webapp/routes/__init__.py index 191e322..ec163c3 100644 --- a/webapp/routes/__init__.py +++ b/webapp/routes/__init__.py @@ -1,2 +1,3 @@ from webapp.routes.tag import * from webapp.routes.blog_post import * +from webapp.routes.permission import * diff --git a/webapp/routes/blog_post.py b/webapp/routes/blog_post.py index f5c2872..4c46390 100644 --- a/webapp/routes/blog_post.py +++ b/webapp/routes/blog_post.py @@ -4,12 +4,18 @@ Edit the hooks in webapp/routes/blog_post_hooks.py instead """ +from flask import render_template +from flask_security import login_required + from oshipka.webapp import app from oshipka.webapp.views import ModelView -from webapp.models import BlogPost from webapp.routes.blog_post_hooks import * +from webapp.models import BlogPost + + +blog_post = ModelView(app, BlogPost, {'name': 'BlogPost', 'searchable': ['body'], 'inherits': ['Ownable'], 'access': [{'verb': 'all', 'login_required': True, 'roles_required': ['admin']}, {'verb': 'get', 'login_required': False}, {'verb': 'list', 'login_required': False}, {'verb': 'table', 'login_required': False}, {'verb': 'search', 'login_required': False}], 'columns': [{'name': 'filename', '_type': 'db.UnicodeText'}, {'name': 'title', '_type': 'db.UnicodeText'}, {'name': 'body', 'type': 'long_text', '_type': 'db.UnicodeText'}, {'name': 'tag', 'type': 'relationship', 'multiple': True, 'secondary': {'name': 'blog_post__tag', 'columns': [{'name': 'blog_post'}, {'name': 'tag'}]}, '_type': 'relationship'}, {'name': 'created_dt', '_type': 'db.UnicodeText'}, {'name': 'updated_dt', '_type': 'db.UnicodeText'}], 'display': {'primary': 'title', 'secondary': 'created_dt'}, '_secondaries': [{'name': 'blog_post__tag', 'columns': [{'name': 'blog_post'}, {'name': 'tag'}]}], '_verbs': {'get': {'per_item': 'True', 'methods': [ + 'GET'], 'is_login_required': False, 'the_roles_required': []}, 'list': {'per_item': 'False', 'methods': ['GET'], 'is_login_required': False, 'the_roles_required': []}, 'table': {'per_item': 'False', 'methods': ['GET'], 'is_login_required': False, 'the_roles_required': []}, 'search': {'per_item': 'False', 'methods': ['GET'], 'is_login_required': False, 'the_roles_required': []}, 'create': {'per_item': 'False', 'methods': ['GET', 'POST'], 'is_login_required': True, 'the_roles_required': ['admin']}, 'update': {'per_item': 'True', 'methods': ['GET', 'POST'], 'is_login_required': True, 'the_roles_required': ['admin']}, 'delete': {'per_item': 'True', 'methods': ['GET', 'POST'], 'is_login_required': True, 'the_roles_required': ['admin']}}, 'acls': {'get': {'authn': False, 'authz': []}, 'list': {'authn': False, 'authz': []}, 'table': {'authn': False, 'authz': []}, 'search': {'authn': False, 'authz': []}, 'create': {'authn': True, 'authz': ['admin']}, 'update': {'authn': True, 'authz': ['admin']}, 'delete': {'authn': True, 'authz': ['admin']}}}) -blog_post = ModelView(app, BlogPost) blog_post.register_verb(view_context=get_view_context, verb="get", diff --git a/webapp/routes/permission.py b/webapp/routes/permission.py new file mode 100644 index 0000000..41290e4 --- /dev/null +++ b/webapp/routes/permission.py @@ -0,0 +1,74 @@ +""" +!!!AUTOGENERATED: DO NOT EDIT!!! + +Edit the hooks in webapp/routes/permission_hooks.py instead +""" + +from flask import render_template +from flask_security import login_required + +from oshipka.webapp import app +from oshipka.webapp.views import ModelView +from webapp.routes.permission_hooks import * +from webapp.models import Permission + + +permission = ModelView(app, Permission, {'name': 'Permission', 'access': [{'verb': 'all', 'login_required': True}], 'columns': [{'name': 'subject', '_type': 'db.UnicodeText'}, {'name': 'subject_id', 'type': 'int', '_type': 'db.Integer'}, {'name': 'action', '_type': 'db.UnicodeText'}, {'name': 'object', '_type': 'db.UnicodeText'}, {'name': 'object_id', 'type': 'int', '_type': 'db.Integer'}, {'name': 'is_allowed', 'type': 'boolean', '_type': 'LiberalBoolean'}], 'display': {'primary': 'subject', 'secondary': 'action', 'tertiary': 'object'}, '_verbs': {'get': {'per_item': 'True', 'methods': ['GET'], 'is_login_required': True, 'the_roles_required': []}, 'list': {'per_item': 'False', 'methods': ['GET'], 'is_login_required': True, 'the_roles_required': []}, 'table': {'per_item': 'False', 'methods': [ + 'GET'], 'is_login_required': True, 'the_roles_required': []}, 'search': {'per_item': 'False', 'methods': ['GET'], 'is_login_required': True, 'the_roles_required': []}, 'create': {'per_item': 'False', 'methods': ['GET', 'POST'], 'is_login_required': True, 'the_roles_required': []}, 'update': {'per_item': 'True', 'methods': ['GET', 'POST'], 'is_login_required': True, 'the_roles_required': []}, 'delete': {'per_item': 'True', 'methods': ['GET', 'POST'], 'is_login_required': True, 'the_roles_required': []}}, 'acls': {'get': {'authn': True, 'authz': []}, 'list': {'authn': True, 'authz': []}, 'table': {'authn': True, 'authz': []}, 'search': {'authn': True, 'authz': []}, 'create': {'authn': True, 'authz': []}, 'update': {'authn': True, 'authz': []}, 'delete': {'authn': True, 'authz': []}}}) + + +permission.register_verb(view_context=get_view_context, + verb="get", + methods=['GET'], + per_item=True, + is_login_required=True, + the_roles_required=[], + ) + +permission.register_verb(view_context=list_view_context, + verb="list", + methods=['GET'], + per_item=False, + is_login_required=True, + the_roles_required=[], + ) + +permission.register_verb(view_context=table_view_context, + verb="table", + methods=['GET'], + per_item=False, + is_login_required=True, + the_roles_required=[], + ) + +permission.register_verb(view_context=search_view_context, + verb="search", + methods=['GET'], + per_item=False, + is_login_required=True, + the_roles_required=[], + ) + +permission.register_verb(view_context=create_view_context, + verb="create", + methods=['GET', 'POST'], + per_item=False, + is_login_required=True, + the_roles_required=[], + ) + +permission.register_verb(view_context=update_view_context, + verb="update", + methods=['GET', 'POST'], + per_item=True, + is_login_required=True, + the_roles_required=[], + ) + +permission.register_verb(view_context=delete_view_context, + verb="delete", + methods=['GET', 'POST'], + per_item=True, + is_login_required=True, + the_roles_required=[], + ) diff --git a/webapp/routes/permission_hooks.py b/webapp/routes/permission_hooks.py new file mode 100644 index 0000000..164439a --- /dev/null +++ b/webapp/routes/permission_hooks.py @@ -0,0 +1,71 @@ +from oshipka.webapp.views import ViewContext, default_get_args_func, default_get_func, default_list_func, \ + default_get_form_func, default_create_func, default_update_func, default_delete_func, default_search_func + + +def get_template(vc): + vc.template = "{}/get.html".format(vc.model_view.model_name) + + +def list_template(vc): + vc.template = "{}/list.html".format(vc.model_view.model_name) + + +def table_template(vc): + vc.template = "{}/table.html".format(vc.model_view.model_name) + + +def search_template(vc): + vc.template = "{}/search.html".format(vc.model_view.model_name) + + +def create_template(vc): + vc.template = "{}/create.html".format(vc.model_view.model_name) + + +def update_template(vc): + vc.template = "{}/update.html".format(vc.model_view.model_name) + + +def delete_template(vc): + vc.template = "delete_instance.html".format(vc.model_view.model_name) + + +get_view_context = ViewContext( + filter_func=default_get_func, + template_func=get_template, +) + +list_view_context = ViewContext( + filter_func=default_list_func, + template_func=list_template, +) + +table_view_context = ViewContext( + filter_func=default_list_func, + template_func=table_template, +) + +search_view_context = ViewContext( + filter_func=default_search_func, + template_func=list_template, +) + +create_view_context = ViewContext( + args_get_func=default_get_form_func, + template_func=create_template, + execute_func=default_create_func, +) + +update_view_context = ViewContext( + args_get_func=default_get_form_func, + filter_func=default_get_func, + template_func=update_template, + execute_func=default_update_func, +) + +delete_view_context = ViewContext( + args_get_func=default_get_form_func, + filter_func=default_get_func, + template_func=delete_template, + execute_func=default_delete_func, +) diff --git a/webapp/routes/tag.py b/webapp/routes/tag.py index 320ba4b..e267ca6 100644 --- a/webapp/routes/tag.py +++ b/webapp/routes/tag.py @@ -4,12 +4,18 @@ Edit the hooks in webapp/routes/tag_hooks.py instead """ +from flask import render_template +from flask_security import login_required + from oshipka.webapp import app from oshipka.webapp.views import ModelView -from webapp.models import Tag from webapp.routes.tag_hooks import * +from webapp.models import Tag + + +tag = ModelView(app, Tag, {'name': 'Tag', 'inherits': ['Ownable'], 'access': [{'verb': 'all', 'login_required': True, 'roles_required': ['admin']}, {'verb': 'get', 'login_required': False}, {'verb': 'list', 'login_required': False}, {'verb': 'table', 'login_required': False}, {'verb': 'search', 'login_required': False}], 'columns': [{'name': 'name', '_type': 'db.UnicodeText'}], 'display': {'primary': 'name'}, '_verbs': {'get': {'per_item': 'True', 'methods': ['GET'], 'is_login_required': False, 'the_roles_required': []}, 'list': {'per_item': 'False', 'methods': ['GET'], 'is_login_required': False, 'the_roles_required': []}, 'table': {'per_item': 'False', 'methods': ['GET'], 'is_login_required': False, 'the_roles_required': []}, 'search': { + 'per_item': 'False', 'methods': ['GET'], 'is_login_required': False, 'the_roles_required': []}, 'create': {'per_item': 'False', 'methods': ['GET', 'POST'], 'is_login_required': True, 'the_roles_required': ['admin']}, 'update': {'per_item': 'True', 'methods': ['GET', 'POST'], 'is_login_required': True, 'the_roles_required': ['admin']}, 'delete': {'per_item': 'True', 'methods': ['GET', 'POST'], 'is_login_required': True, 'the_roles_required': ['admin']}}, 'acls': {'get': {'authn': False, 'authz': []}, 'list': {'authn': False, 'authz': []}, 'table': {'authn': False, 'authz': []}, 'search': {'authn': False, 'authz': []}, 'create': {'authn': True, 'authz': ['admin']}, 'update': {'authn': True, 'authz': ['admin']}, 'delete': {'authn': True, 'authz': ['admin']}}}) -tag = ModelView(app, Tag) tag.register_verb(view_context=get_view_context, verb="get", diff --git a/webapp/templates/blog_post/_action_create.html b/webapp/templates/blog_post/_action_create.html new file mode 100644 index 0000000..0f61302 --- /dev/null +++ b/webapp/templates/blog_post/_action_create.html @@ -0,0 +1,3 @@ +{% if has_permission('blog_post', 'create') %} +{{ _("Create") }} +{% endif %} \ No newline at end of file diff --git a/webapp/templates/blog_post/_action_delete.html b/webapp/templates/blog_post/_action_delete.html index 79689f9..421c322 100644 --- a/webapp/templates/blog_post/_action_delete.html +++ b/webapp/templates/blog_post/_action_delete.html @@ -1 +1,3 @@ -x \ No newline at end of file +{% if has_permission('blog_post', 'delete', instance) %} +x +{% endif %} \ No newline at end of file diff --git a/webapp/templates/blog_post/_action_edit.html b/webapp/templates/blog_post/_action_edit.html index ed7010b..af5f67c 100644 --- a/webapp/templates/blog_post/_action_edit.html +++ b/webapp/templates/blog_post/_action_edit.html @@ -1 +1,3 @@ -e \ No newline at end of file +{% if has_permission('blog_post', 'update', instance) %} +e +{% endif %} \ No newline at end of file diff --git a/webapp/templates/blog_post/_action_list.html b/webapp/templates/blog_post/_action_list.html new file mode 100644 index 0000000..df0cde3 --- /dev/null +++ b/webapp/templates/blog_post/_action_list.html @@ -0,0 +1,3 @@ +{% if has_permission('blog_post', 'list') %} +{{ _("list") }} +{% endif %} \ No newline at end of file diff --git a/webapp/templates/blog_post/_action_search.html b/webapp/templates/blog_post/_action_search.html new file mode 100644 index 0000000..214b114 --- /dev/null +++ b/webapp/templates/blog_post/_action_search.html @@ -0,0 +1,6 @@ +{% if has_permission('blog_post', 'search') %} +
+{% endif %} \ No newline at end of file diff --git a/webapp/templates/blog_post/_action_table.html b/webapp/templates/blog_post/_action_table.html new file mode 100644 index 0000000..87ace96 --- /dev/null +++ b/webapp/templates/blog_post/_action_table.html @@ -0,0 +1,3 @@ +{% if has_permission('blog_post', 'table') %} +{{ _("table") }} +{% endif %} \ No newline at end of file diff --git a/webapp/templates/blog_post/_actions.html b/webapp/templates/blog_post/_actions.html index 9612f38..b15bc26 100644 --- a/webapp/templates/blog_post/_actions.html +++ b/webapp/templates/blog_post/_actions.html @@ -1,4 +1,7 @@ -[ -{% include "blog_post/_action_edit.html" %} | -{% include "blog_post/_action_delete.html" %} -] \ No newline at end of file +{% if not has_permission('blog_post', 'update', instance) and not has_permission('blog_post', 'delete', instance) %} +{% else %} + [ + {% include "blog_post/_action_edit.html" %} | + {% include "blog_post/_action_delete.html" %} + ] +{% endif %} \ No newline at end of file diff --git a/webapp/templates/blog_post/_actions_multiple.html b/webapp/templates/blog_post/_actions_multiple.html new file mode 100644 index 0000000..8de1da6 --- /dev/null +++ b/webapp/templates/blog_post/_actions_multiple.html @@ -0,0 +1,2 @@ +{% include "blog_post/_action_list.html" %} | +{% include "blog_post/_action_table.html" %} \ No newline at end of file diff --git a/webapp/templates/blog_post/_create.html b/webapp/templates/blog_post/_create.html index f9df3fc..de27990 100644 --- a/webapp/templates/blog_post/_create.html +++ b/webapp/templates/blog_post/_create.html @@ -1,4 +1,5 @@ -