Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,12 @@ If you encounter any errors, please open an issue or contact us on slack in #oc-
}'
```

## Admin Panel Access

This project has an admin panel which can be used to view and manually edit categories and languages at a higher level than the API allows.
In order to create admin user, set following environment variables before starting the application -
`ADMIN_EMAIL` and `ADMIN_PASSWORD`

## Development Notes

If you make changes to the models.py or other schemas, you need to run a migration and upgrade again:
Expand Down
33 changes: 33 additions & 0 deletions app/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from flask_admin import Admin, AdminIndexView
from flask_admin.contrib.sqla import ModelView
from flask import url_for, redirect, request
from app import db
from .models import Resource, Category, Language, User, Role
from flask_security import current_user


class AdminView(ModelView):
def is_accessible(self):
return current_user.has_role("admin")

def inaccessible_callback(self):
return redirect(url_for("security.login", next=request.url))


class HomeAdminView(AdminIndexView):
def is_accessible(self):
return current_user.has_role("admin")

def inaccessible_callback(self, name):
return redirect(url_for("security.login", next=request.url))


def run_flask_admin(app):
admin = Admin(app, name="Resources_api", url='/',
index_view=HomeAdminView(name="Home"))
admin.add_view(AdminView(Role, db.session))
admin.add_view(AdminView(User, db.session))
admin.add_view(AdminView(Resource, db.session))
admin.add_view(AdminView(Category, db.session))
admin.add_view(AdminView(Language, db.session))
return admin
43 changes: 43 additions & 0 deletions app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from sqlalchemy import DateTime
from sqlalchemy.sql import func
from sqlalchemy_utils import URLType
from flask_security import RoleMixin, UserMixin

language_identifier = db.Table('language_identifier',
db.Column(
Expand Down Expand Up @@ -203,3 +204,45 @@ class VoteInformation(db.Model):
current_direction = db.Column(db.String, nullable=False)
resource = db.relationship('Resource', back_populates='voters')
voter = db.relationship('Key', back_populates='voted_resources')


roles_users = db.Table(
'roles_users',
db.Column('user_id', db.Integer(), db.ForeignKey('user.id')),
db.Column('role_id', db.Integer(), db.ForeignKey('role.id'))
)


# Role class
class Role(db.Model, RoleMixin):
# Our Role has three fields, ID, name and description
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(80), unique=True)
description = db.Column(db.String(255))

def __str__(self):
return self.name

# __hash__ is required to avoid the exception
# TypeError: unhashable type: 'Role' when saving a User
def __hash__(self):
return hash(self.name)


# User class
class User(db.Model, UserMixin):

# Our User has six fields: ID, email, password, active, confirmed_at
# and roles. The roles field represents a many-to-many relationship
# using the roles_users table. Each user may have no role, one role,
# or multiple roles.
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(255), unique=True)
password = db.Column(db.String(255))
active = db.Column(db.Boolean())
confirmed_at = db.Column(db.DateTime())
roles = db.relationship(
'Role',
secondary=roles_users,
backref=db.backref('users', lazy='dynamic')
)
5 changes: 5 additions & 0 deletions app/templates/admin/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{% extends 'admin/master.html' %}

{% block body %}
<p>Admin Home Page</p>
{% endblock %}
12 changes: 12 additions & 0 deletions configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ def get_sys_exec_root_or_drive():
if not all([algolia_app_id, algolia_api_key]):
print("Application requires 'ALGOLIA_APP_ID' and 'ALGOLIA_API_KEY' for search")


secret_key = os.environ.get('SECRET_KEY', 'change_secret_key')

security_password_hash = 'pbkdf2_sha512'
# Replace this with your own salt.
security_password_salt = os.environ.get('SECURITY_PASSWORD_SALT', '!@#$!!@$%@')


index_name = os.environ.get("INDEX_NAME")


Expand All @@ -45,6 +53,10 @@ class Config:

SQLALCHEMY_DATABASE_URI = f"postgresql://{pg_user}:{pg_pw}@{pg_host}:5432/{pg_db}"

SECRET_KEY = secret_key
SECURITY_PASSWORD_HASH = security_password_hash
SECURITY_PASSWORD_SALT = security_password_salt

ALGOLIA_APP_ID = algolia_app_id
ALGOLIA_API_KEY = algolia_api_key
INDEX_NAME = index_name
Expand Down
52 changes: 52 additions & 0 deletions migrations/versions/824f1576e904_.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""empty message

Revision ID: 824f1576e904
Revises: 205742d3b3f5
Create Date: 2020-10-20 10:36:16.978231

"""
from alembic import op
import sqlalchemy as sa
import sqlalchemy_utils


# revision identifiers, used by Alembic.
revision = '824f1576e904'
down_revision = 'fc34137ad3ba'
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('role',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(length=80), nullable=True),
sa.Column('description', sa.String(length=255), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('name')
)
op.create_table('user',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('email', sa.String(length=255), nullable=True),
sa.Column('password', sa.String(length=255), nullable=True),
sa.Column('active', sa.Boolean(), nullable=True),
sa.Column('confirmed_at', sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('email')
)
op.create_table('roles_users',
sa.Column('user_id', sa.Integer(), nullable=True),
sa.Column('role_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['role_id'], ['role.id'], ),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], )
)
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('roles_users')
op.drop_table('user')
op.drop_table('role')
# ### end Alembic commands ###
Loading