diff --git a/.gitignore b/.gitignore index c20f20a..174401e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ data/db.sqlite __pycache__ oshipka.egg-info provision/tmp -provision/auto_dns/sensitive.py \ No newline at end of file +provision/auto_dns/sensitive.py +ssl/ \ No newline at end of file diff --git a/bootstrap/.gitignore b/bootstrap/.gitignore index 71e8dbc..1b02a12 100644 --- a/bootstrap/.gitignore +++ b/bootstrap/.gitignore @@ -3,4 +3,5 @@ venv *.pyc data/ __pycache__ -sensitive.py \ No newline at end of file +sensitive.py +webapp/ssl \ No newline at end of file diff --git a/bootstrap/config.py b/bootstrap/config.py index 1283a33..63a2b7f 100644 --- a/bootstrap/config.py +++ b/bootstrap/config.py @@ -24,7 +24,7 @@ MAKEDIRS = [ DATA_DIR, STATIC_DATA_DIR, MEDIA_DIR, TASKS_DIR, TASKS_IN_DIR, TASKS_PROC_DIR, TASKS_BUF_DIR, ] -APP_BASE_URL = "http://localhost:5000" +APP_BASE_URL = "https://PROJECT_NAME.localhost:5000" SECURITY_ENABLED = True SSO_BASE_URL = 'https://sso.localhost:5008' SSO_CLIENT_ID = APP_BASE_URL diff --git a/bootstrap/data_static/User.csv b/bootstrap/data_static/User.csv index 8e2670a..3370f98 100644 --- a/bootstrap/data_static/User.csv +++ b/bootstrap/data_static/User.csv @@ -1,2 +1,2 @@ -username -daniel \ No newline at end of file +username,_m_n_roles +admin,1 \ No newline at end of file diff --git a/bootstrap/run.py b/bootstrap/run.py index 1d516f8..15d7b81 100644 --- a/bootstrap/run.py +++ b/bootstrap/run.py @@ -12,4 +12,4 @@ app.template_folder = TEMPLATES_FOLDER app.static_folder = STATIC_FOLDER if __name__ == "__main__": - app.run(debug=True) + app.run(port=5000, debug=True, ssl_context=('webapp/ssl/cert.crt', 'webapp/ssl/cert.key')) diff --git a/oshipka.sh b/oshipka.sh index 4558eac..79b13e5 100755 --- a/oshipka.sh +++ b/oshipka.sh @@ -23,6 +23,9 @@ Usage $0 [ bootstrap | model | db_migrate | db_upgrade | db_populate | db_recrea translate Translations subcommand + ca Create oshipka certificate authority (Oshipka CA) + cert_dev [DOMAIN] Generate dev certificate signed by Oshipka CA + worker Start worker web Start webapp @@ -64,6 +67,56 @@ command_translate() { return $? } +ca () { + if [ -f ${OSHIPKA_PATH}/ssl/oshipka_ca.pem ]; then + echo "Oshipka CA already exists" + exit 1; + fi + echo "Creating Oshipka CA..." + + mkdir -p ${OSHIPKA_PATH}/ssl + cd ${OSHIPKA_PATH}/ssl || exit + openssl genrsa -out oshipka_ca.key 2048 + openssl req -x509 -new -nodes -key oshipka_ca.key -sha256 -days 1825 -out oshipka_ca.pem -subj "/C=/ST=/L=/O=Oshipka Web Development CA/OU=/CN=oshipka_web_development_ca" +} + +cert_dev () { + shift + DOMAIN=$1 + + if [ -f webapp/ssl/cert.crt ]; then + echo "Certificate already exists" + exit 1; + elif [ ! -f ${OSHIPKA_PATH}/ssl/oshipka_ca.key ]; then + echo "Oshipka CA not found, generating..." + ca + fi + + if [ -z "${DOMAIN}" ]; then + DOMAIN=$(basename `pwd`) + fi + + mkdir -p webapp/ssl + echo "Create CSR for ${DOMAIN}" + openssl genrsa -out "webapp/ssl/cert.key" 2048 + openssl req -new -key "webapp/ssl/cert.key" -out "webapp/ssl/cert.csr" -subj "/C=/ST=/L=/O=Oshipka Web Development/OU=/CN=${DOMAIN}.localhost" + + cat > "webapp/ssl/cert.ext" << EOF +authorityKeyIdentifier=keyid,issuer +basicConstraints=CA:FALSE +keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment +subjectAltName = @alt_names + +[alt_names] +DNS.1 = ${DOMAIN}.localhost +EOF + + echo "Create self-signed certificate" + openssl x509 -req -in "webapp/ssl/cert.csr" -CA ${OSHIPKA_PATH}/ssl/oshipka_ca.pem -CAkey ${OSHIPKA_PATH}/ssl/oshipka_ca.key -CAcreateserial -out "webapp/ssl/cert.crt" -days 825 -sha256 -extfile "webapp/ssl/cert.ext" + + rm "webapp/ssl/cert.ext" "webapp/ssl/cert.csr" +} + worker () { source venv/bin/activate @@ -150,8 +203,9 @@ bootstrap() { cp -r ${OSHIPKA_PATH}/bootstrap/* ${PROJECT_PATH}/ cp ${OSHIPKA_PATH}/bootstrap/.gitignore ${PROJECT_PATH}/.gitignore mkdir ${PROJECT_PATH}/data - mkdir ${PROJECT_PATH}/webapp/view_models cd ${PROJECT_PATH} + sed -i "s/PROJECT_NAME.localhost:5000/${PROJECT_NAME}.localhost:5000/" config.py + cert_dev init_venv link_dev_oshipka source venv/bin/activate @@ -286,6 +340,7 @@ db_purge_recreate() { source venv/bin/activate rm -rf data/db.sqlite data/search_index migrations/ data/media python manager.py db init + model db_migrate db_upgrade db_populate @@ -341,6 +396,10 @@ command_main() { ;; venv) init_venv "$@" ;; + ca) ca "$@" + ;; + cert_dev) cert_dev "$@" + ;; install) install_reqs "$@" ;; link) link_dev_oshipka "$@" diff --git a/oshipka/persistance/__init__.py b/oshipka/persistance/__init__.py index a7b0c07..332a1af 100644 --- a/oshipka/persistance/__init__.py +++ b/oshipka/persistance/__init__.py @@ -338,7 +338,7 @@ SENSITIVE_PREFIX = "__SENSITIVE__." DEFAULT_PERMISSION_PERMISSIONS = ['get', 'add_user', 'add_role', 'remove_user', 'remove_role'] DEFAULT_MODEL_PERMISSIONS = ['get', 'list', 'search', 'create', 'update', 'delete'] DEFAULT_COLUMN_PERMISSIONS = ['read', 'write'] -DEFAULT_SUBJECTS = ['public', 'logged'] +DEFAULT_SUBJECTS = [('0', 'public'), ('1', 'logged')] def generate_permissions(): @@ -351,24 +351,27 @@ def generate_permissions(): if model in ['permission']: continue is_ownable = 'Ownable' in model_view.definitions.get('inherits', []) - subjects = DEFAULT_SUBJECTS + ['owner']if is_ownable else DEFAULT_SUBJECTS + subjects = DEFAULT_SUBJECTS + [('1', 'owner')] if is_ownable else DEFAULT_SUBJECTS f.write("role,1,permission.update,models.{},,1\n".format(model)) f.write("role,1,permission.remove_user_self,models.{},,1\n".format(model)) - for subject in subjects: + for perm, subject in subjects: for permission in DEFAULT_PERMISSION_PERMISSIONS: f.write("{},,permission.{},models.{},,0\n".format(subject, permission, model)) f.write("role,1,permission.{},models.{},,1\n".format(permission, model)) f.write("{},,permission.update,models.{},,0\n".format(subject, model)) f.write("{},,permission.remove_user_self,models.{},,0\n".format(subject, model)) if is_ownable: - f.write("{},,permission.change_owner,models.{},,1\n".format(subject, model)) + if subject in ['owner']: + f.write("{},,permission.change_owner,models.{},,1\n".format(subject, model)) + else: + f.write("{},,permission.change_owner,models.{},,0\n".format(subject, model)) for permission in DEFAULT_MODEL_PERMISSIONS: - f.write("{},,model.{},models.{},,1\n".format(subject, permission, model)) + f.write("{},,model.{},models.{},,{}\n".format(subject, permission, model, perm)) for column in model_view.definitions.get('columns'): column_name = column.get('name') for permission in DEFAULT_COLUMN_PERMISSIONS: - f.write("{},,column.{}.{},columns.{},,1\n".format(subject, column_name, permission, model)) - f.write("role,1,column.{}.{},columns.{},,1\n".format(subject, column_name, permission, model)) + f.write("{},,column.{}.{},columns.{},,{}\n".format(subject, column_name, permission, model, perm)) + f.write("role,1,column.{}.{},columns.{},,{}\n".format(subject, column_name, permission, model, perm)) def populate_static(app): diff --git a/oshipka/webapp/views.py b/oshipka/webapp/views.py index edd2e15..b16e3de 100644 --- a/oshipka/webapp/views.py +++ b/oshipka/webapp/views.py @@ -15,11 +15,10 @@ from oshipka.persistance import db, filter_m_n, update_m_ns, SHARING_TYPE_TYPES_ SHARING_TYPE_TYPES_TYPE_AUTHZ, SHARING_TYPE_TYPES_TYPE_AUTHN from oshipka.util.strings import camel_case_to_snake_case from config import MEDIA_DIR, SECURITY_ENABLED +from webapp.models import Permission webapp_models = importlib.import_module("webapp.models") -if SECURITY_ENABLED: - from webapp.models import Permission MODEL_VIEWS = dict() @@ -55,14 +54,15 @@ def has_permission(obj, action, instance=None, object_prefix="models", action_pr return True # ROLE PERMISSIONS - roles_ids = [r.id for r in current_user.roles] - role_permissions = Permission.query.filter(Permission.object == "{}.{}".format(object_prefix, obj), - Permission.action == "{}.{}".format(action_prefix, action), - Permission.subject == "user", - Permission.subject_id.in_(roles_ids)).all() - for role_permission in role_permissions: - if role_permission.is_allowed: - return True + if hasattr(current_user, "roles"): + roles_ids = [r.id for r in current_user.roles] + role_permissions = Permission.query.filter(Permission.object == "{}.{}".format(object_prefix, obj), + Permission.action == "{}.{}".format(action_prefix, action), + Permission.subject == "user", + Permission.subject_id.in_(roles_ids)).all() + for role_permission in role_permissions: + if role_permission.is_allowed: + return True # USER PERMISSIONS user_id = current_user.id