Merge pull request #84 from sartography/feature/bpmn_user_permissions
Feature/bpmn user permissions
This commit is contained in:
commit
6aab66a554
|
@ -35,6 +35,7 @@ module.exports = {
|
|||
new CopyWebpackPlugin({
|
||||
patterns: [
|
||||
{ from: 'assets/**', to: 'vendor/bpmn-js', context: 'node_modules/bpmn-js/dist/' },
|
||||
{ from: '*.css', to: 'vendor/bpmn-js-color-picker', context: 'node_modules/bpmn-js-color-picker/colors' },
|
||||
{
|
||||
from: 'assets/**',
|
||||
to: 'vendor/bpmn-js-properties-panel',
|
||||
|
|
|
@ -7,7 +7,8 @@ def main() -> None:
|
|||
"""Main."""
|
||||
app = get_hacked_up_app_for_script()
|
||||
with app.app_context():
|
||||
AuthorizationService.delete_all_permissions_and_recreate()
|
||||
AuthorizationService.delete_all_permissions()
|
||||
AuthorizationService.import_permissions_from_yaml_file()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
@ -424,6 +424,7 @@
|
|||
"enabled" : true,
|
||||
"totp" : false,
|
||||
"emailVerified" : false,
|
||||
"email": "admin@status.im",
|
||||
"firstName" : "",
|
||||
"lastName" : "",
|
||||
"credentials" : [ {
|
||||
|
@ -446,6 +447,7 @@
|
|||
"enabled" : true,
|
||||
"totp" : false,
|
||||
"emailVerified" : false,
|
||||
"email": "alex@sartography.com",
|
||||
"credentials" : [ {
|
||||
"id" : "81a61a3b-228d-42b3-b39a-f62d8e7f57ca",
|
||||
"type" : "password",
|
||||
|
@ -465,6 +467,7 @@
|
|||
"enabled" : true,
|
||||
"totp" : false,
|
||||
"emailVerified" : false,
|
||||
"email": "amir@status.im",
|
||||
"credentials" : [ {
|
||||
"id" : "e589f3ad-bf7b-4756-89f7-7894c03c2831",
|
||||
"type" : "password",
|
||||
|
@ -484,6 +487,7 @@
|
|||
"enabled" : true,
|
||||
"totp" : false,
|
||||
"emailVerified" : false,
|
||||
"email": "ciadmin1@status.im",
|
||||
"credentials" : [ {
|
||||
"id" : "111b5ea1-c2ab-470a-a16b-2373bc94de7a",
|
||||
"type" : "password",
|
||||
|
@ -506,6 +510,7 @@
|
|||
"enabled" : true,
|
||||
"totp" : false,
|
||||
"emailVerified" : false,
|
||||
"email": "ciuser1@status.im",
|
||||
"credentials" : [ {
|
||||
"id" : "762f36e9-47af-44da-8520-cf09d752497a",
|
||||
"type" : "password",
|
||||
|
@ -528,6 +533,7 @@
|
|||
"enabled" : true,
|
||||
"totp" : false,
|
||||
"emailVerified" : false,
|
||||
"email": "core@status.im",
|
||||
"firstName" : "",
|
||||
"lastName" : "",
|
||||
"credentials" : [ {
|
||||
|
@ -550,6 +556,7 @@
|
|||
"enabled" : true,
|
||||
"totp" : false,
|
||||
"emailVerified" : false,
|
||||
"email": "dan@sartography.com",
|
||||
"credentials" : [ {
|
||||
"id" : "d517c520-f500-4542-80e5-7144daef1e32",
|
||||
"type" : "password",
|
||||
|
@ -569,6 +576,7 @@
|
|||
"enabled" : true,
|
||||
"totp" : false,
|
||||
"emailVerified" : false,
|
||||
"email": "daniel@sartography.com",
|
||||
"credentials" : [ {
|
||||
"id" : "f240495c-265b-42fc-99db-46928580d07d",
|
||||
"type" : "password",
|
||||
|
@ -588,6 +596,7 @@
|
|||
"enabled" : true,
|
||||
"totp" : false,
|
||||
"emailVerified" : false,
|
||||
"email": "elizabeth@sartography.com",
|
||||
"credentials" : [ {
|
||||
"id" : "ae951ec8-9fc9-4f1b-b340-bbbe463ae5c2",
|
||||
"type" : "password",
|
||||
|
@ -607,6 +616,7 @@
|
|||
"enabled" : true,
|
||||
"totp" : false,
|
||||
"emailVerified" : false,
|
||||
"email": "fin@status.im",
|
||||
"firstName" : "",
|
||||
"lastName" : "",
|
||||
"credentials" : [ {
|
||||
|
@ -629,6 +639,7 @@
|
|||
"enabled" : true,
|
||||
"totp" : false,
|
||||
"emailVerified" : false,
|
||||
"email": "fin1@status.im",
|
||||
"firstName" : "",
|
||||
"lastName" : "",
|
||||
"credentials" : [ {
|
||||
|
@ -651,6 +662,7 @@
|
|||
"enabled" : true,
|
||||
"totp" : false,
|
||||
"emailVerified" : false,
|
||||
"email": "finance_user1@status.im",
|
||||
"credentials" : [ {
|
||||
"id" : "f14722ec-13a7-4d35-a4ec-0475d405ae58",
|
||||
"type" : "password",
|
||||
|
@ -670,6 +682,7 @@
|
|||
"enabled" : true,
|
||||
"totp" : false,
|
||||
"emailVerified" : false,
|
||||
"email": "harmeet@status.im",
|
||||
"credentials" : [ {
|
||||
"id" : "89c26090-9bd3-46ac-b038-883d02e3f125",
|
||||
"type" : "password",
|
||||
|
@ -689,6 +702,7 @@
|
|||
"enabled" : true,
|
||||
"totp" : false,
|
||||
"emailVerified" : false,
|
||||
"email": "j@status.im",
|
||||
"firstName" : "",
|
||||
"lastName" : "",
|
||||
"credentials" : [ {
|
||||
|
@ -711,6 +725,7 @@
|
|||
"enabled" : true,
|
||||
"totp" : false,
|
||||
"emailVerified" : false,
|
||||
"email": "jakub@status.im",
|
||||
"credentials" : [ {
|
||||
"id" : "ce141fa5-b8d5-4bbe-93e7-22e7119f97c2",
|
||||
"type" : "password",
|
||||
|
@ -730,6 +745,7 @@
|
|||
"enabled" : true,
|
||||
"totp" : false,
|
||||
"emailVerified" : false,
|
||||
"email": "jarrad@status.im",
|
||||
"credentials" : [ {
|
||||
"id" : "113e0343-1069-476d-83f9-21d98edb9cfa",
|
||||
"type" : "password",
|
||||
|
@ -749,6 +765,7 @@
|
|||
"enabled" : true,
|
||||
"totp" : false,
|
||||
"emailVerified" : false,
|
||||
"email": "jason@sartography.com",
|
||||
"credentials" : [ {
|
||||
"id" : "40abf32e-f0cc-4a17-8231-1a69a02c1b0b",
|
||||
"type" : "password",
|
||||
|
@ -768,6 +785,7 @@
|
|||
"enabled" : true,
|
||||
"totp" : false,
|
||||
"emailVerified" : false,
|
||||
"email": "jon@sartography.com",
|
||||
"credentials" : [ {
|
||||
"id" : "8b520e01-5b9b-44ab-9ee8-505bd0831a45",
|
||||
"type" : "password",
|
||||
|
@ -787,6 +805,7 @@
|
|||
"enabled" : true,
|
||||
"totp" : false,
|
||||
"emailVerified" : false,
|
||||
"email": "kb@sartography.com",
|
||||
"credentials" : [ {
|
||||
"id" : "2c0be363-038f-48f1-86d6-91fdd28657cf",
|
||||
"type" : "password",
|
||||
|
@ -806,6 +825,7 @@
|
|||
"enabled" : true,
|
||||
"totp" : false,
|
||||
"emailVerified" : false,
|
||||
"email": "lead@status.im",
|
||||
"firstName" : "",
|
||||
"lastName" : "",
|
||||
"credentials" : [ {
|
||||
|
@ -828,6 +848,7 @@
|
|||
"enabled" : true,
|
||||
"totp" : false,
|
||||
"emailVerified" : false,
|
||||
"email": "lead1@status.im",
|
||||
"firstName" : "",
|
||||
"lastName" : "",
|
||||
"credentials" : [ {
|
||||
|
@ -850,6 +871,7 @@
|
|||
"enabled" : true,
|
||||
"totp" : false,
|
||||
"emailVerified" : false,
|
||||
"email": "manuchehr@status.im",
|
||||
"credentials" : [ {
|
||||
"id" : "07dabf55-b5d3-4f98-abba-3334086ecf5e",
|
||||
"type" : "password",
|
||||
|
@ -869,6 +891,7 @@
|
|||
"enabled" : true,
|
||||
"totp" : false,
|
||||
"emailVerified" : false,
|
||||
"email": "mike@sartography.com",
|
||||
"credentials" : [ {
|
||||
"id" : "1ed375fb-0f1a-4c2a-9243-2477242cf7bd",
|
||||
"type" : "password",
|
||||
|
@ -888,6 +911,7 @@
|
|||
"enabled" : true,
|
||||
"totp" : false,
|
||||
"emailVerified" : false,
|
||||
"email": "natalia@sartography.com",
|
||||
"credentials" : [ {
|
||||
"id" : "b6aa9936-39cc-4931-bfeb-60e6753de5ba",
|
||||
"type" : "password",
|
||||
|
@ -907,6 +931,7 @@
|
|||
"enabled" : true,
|
||||
"totp" : false,
|
||||
"emailVerified" : false,
|
||||
"email": "sasha@status.im",
|
||||
"credentials" : [ {
|
||||
"id" : "4a170af4-6f0c-4e7b-b70c-e674edf619df",
|
||||
"type" : "password",
|
||||
|
@ -926,6 +951,7 @@
|
|||
"enabled" : true,
|
||||
"totp" : false,
|
||||
"emailVerified" : false,
|
||||
"email": "service-account@status.im",
|
||||
"serviceAccountClientId" : "spiffworkflow-backend",
|
||||
"credentials" : [ ],
|
||||
"disableableCredentialTypes" : [ ],
|
||||
|
@ -943,6 +969,7 @@
|
|||
"enabled" : true,
|
||||
"totp" : false,
|
||||
"emailVerified" : false,
|
||||
"email": "service-account-withauth@status.im",
|
||||
"serviceAccountClientId" : "withAuth",
|
||||
"credentials" : [ ],
|
||||
"disableableCredentialTypes" : [ ],
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: b99a4cb94b5b
|
||||
Revision ID: 67197b02b0c1
|
||||
Revises:
|
||||
Create Date: 2022-12-20 10:45:08.295317
|
||||
Create Date: 2022-12-20 15:05:31.545567
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
|
@ -10,7 +10,7 @@ import sqlalchemy as sa
|
|||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'b99a4cb94b5b'
|
||||
revision = '67197b02b0c1'
|
||||
down_revision = None
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
@ -72,16 +72,15 @@ def upgrade():
|
|||
op.create_table('user',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('username', sa.String(length=255), nullable=False),
|
||||
sa.Column('uid', sa.String(length=50), nullable=True),
|
||||
sa.Column('service', sa.String(length=50), nullable=False),
|
||||
sa.Column('service_id', sa.String(length=255), nullable=False),
|
||||
sa.Column('name', sa.String(length=255), nullable=True),
|
||||
sa.Column('display_name', sa.String(length=255), nullable=True),
|
||||
sa.Column('email', sa.String(length=255), nullable=True),
|
||||
sa.Column('updated_at_in_seconds', sa.Integer(), nullable=True),
|
||||
sa.Column('created_at_in_seconds', sa.Integer(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('service', 'service_id', name='service_key'),
|
||||
sa.UniqueConstraint('uid')
|
||||
sa.UniqueConstraint('username')
|
||||
)
|
||||
op.create_table('message_correlation_property',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
|
@ -176,6 +175,14 @@ def upgrade():
|
|||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('user_id', 'group_id', name='user_group_assignment_unique')
|
||||
)
|
||||
op.create_table('user_group_assignment_waiting',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('username', sa.String(length=255), nullable=False),
|
||||
sa.Column('group_id', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['group_id'], ['group.id'], ),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('username', 'group_id', name='user_group_assignment_staged_unique')
|
||||
)
|
||||
op.create_table('human_task',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('process_instance_id', sa.Integer(), nullable=False),
|
||||
|
@ -309,6 +316,7 @@ def downgrade():
|
|||
op.drop_table('message_correlation')
|
||||
op.drop_index(op.f('ix_human_task_completed'), table_name='human_task')
|
||||
op.drop_table('human_task')
|
||||
op.drop_table('user_group_assignment_waiting')
|
||||
op.drop_table('user_group_assignment')
|
||||
op.drop_table('secret')
|
||||
op.drop_table('refresh_token')
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -18,6 +18,7 @@ from werkzeug.exceptions import NotFound
|
|||
|
||||
import spiffworkflow_backend.load_database_models # noqa: F401
|
||||
from spiffworkflow_backend.config import setup_config
|
||||
from spiffworkflow_backend.helpers.api_version import V1_API_PATH_PREFIX
|
||||
from spiffworkflow_backend.routes.admin_blueprint.admin_blueprint import admin_blueprint
|
||||
from spiffworkflow_backend.routes.openid_blueprint.openid_blueprint import (
|
||||
openid_blueprint,
|
||||
|
@ -117,7 +118,7 @@ def create_app() -> flask.app.Flask:
|
|||
]
|
||||
CORS(app, origins=origins_re, max_age=3600)
|
||||
|
||||
connexion_app.add_api("api.yml", base_path="/v1.0")
|
||||
connexion_app.add_api("api.yml", base_path=V1_API_PATH_PREFIX)
|
||||
|
||||
mail = Mail(app)
|
||||
app.config["MAIL_APP"] = mail
|
||||
|
|
|
@ -10,55 +10,52 @@ groups:
|
|||
admin:
|
||||
users:
|
||||
[
|
||||
admin,
|
||||
jakub,
|
||||
kb,
|
||||
alex,
|
||||
dan,
|
||||
mike,
|
||||
jason,
|
||||
jarrad,
|
||||
elizabeth,
|
||||
jon,
|
||||
admin@status.im,
|
||||
jakub@status.im,
|
||||
jarrad@status.im,
|
||||
kb@sartography.com,
|
||||
alex@sartography.com,
|
||||
dan@sartography.com,
|
||||
mike@sartography.com,
|
||||
jason@sartography.com,
|
||||
j@sartography.com,
|
||||
elizabeth@sartography.com,
|
||||
jon@sartography.com,
|
||||
]
|
||||
|
||||
Finance Team:
|
||||
users:
|
||||
[
|
||||
jakub,
|
||||
alex,
|
||||
dan,
|
||||
mike,
|
||||
jason,
|
||||
amir,
|
||||
jarrad,
|
||||
elizabeth,
|
||||
jon,
|
||||
sasha,
|
||||
fin,
|
||||
fin1,
|
||||
jakub@status.im,
|
||||
amir@status.im,
|
||||
jarrad@status.im,
|
||||
sasha@status.im,
|
||||
fin@sartography.com,
|
||||
fin1@sartography.com,
|
||||
alex@sartography.com,
|
||||
dan@sartography.com,
|
||||
mike@sartography.com,
|
||||
jason@sartography.com,
|
||||
j@sartography.com,
|
||||
elizabeth@sartography.com,
|
||||
jon@sartography.com,
|
||||
]
|
||||
|
||||
demo:
|
||||
users:
|
||||
[
|
||||
core,
|
||||
fin,
|
||||
fin1,
|
||||
harmeet,
|
||||
jason,
|
||||
sasha,
|
||||
manuchehr,
|
||||
lead,
|
||||
lead1
|
||||
harmeet@status.im,
|
||||
sasha@status.im,
|
||||
manuchehr@status.im,
|
||||
core@status.im,
|
||||
fin@status.im,
|
||||
fin1@status.im,
|
||||
lead@status.im,
|
||||
lead1@status.im
|
||||
]
|
||||
|
||||
core-contributor:
|
||||
users:
|
||||
[
|
||||
core,
|
||||
harmeet,
|
||||
]
|
||||
test:
|
||||
users: [natalia@sartography.com]
|
||||
|
||||
admin-ro:
|
||||
users:
|
||||
|
@ -66,16 +63,12 @@ groups:
|
|||
j,
|
||||
]
|
||||
|
||||
test:
|
||||
users: [natalia]
|
||||
|
||||
permissions:
|
||||
admin:
|
||||
groups: [admin]
|
||||
users: []
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /*
|
||||
|
||||
admin-readonly:
|
||||
groups: [admin-ro]
|
||||
users: []
|
||||
|
@ -85,121 +78,93 @@ permissions:
|
|||
groups: [admin-ro]
|
||||
users: []
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /v1.0/process-instances/*
|
||||
uri: /process-instances/*
|
||||
|
||||
tasks-crud:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /v1.0/tasks/*
|
||||
service-tasks:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /v1.0/service-tasks
|
||||
user-groups-for-current-user:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /v1.0/user-groups/for-current-user
|
||||
|
||||
|
||||
# read all for everybody
|
||||
# open system defaults for everybody
|
||||
read-all-process-groups:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /v1.0/process-groups/*
|
||||
uri: /process-groups/*
|
||||
read-all-process-models:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /v1.0/process-models/*
|
||||
uri: /process-models/*
|
||||
|
||||
# basic perms for everybody
|
||||
read-all-process-instances-for-me:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /v1.0/process-instances/for-me/*
|
||||
uri: /process-instances/for-me/*
|
||||
read-process-instance-reports:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /v1.0/process-instances/reports/*
|
||||
uri: /process-instances/reports/*
|
||||
processes-read:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /v1.0/processes
|
||||
uri: /processes
|
||||
service-tasks:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /service-tasks
|
||||
tasks-crud:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /tasks/*
|
||||
user-groups-for-current-user:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /user-groups/for-current-user
|
||||
|
||||
|
||||
manage-procurement-admin:
|
||||
groups: ["Project Lead"]
|
||||
users: []
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /v1.0/process-groups/manage-procurement:*
|
||||
manage-procurement-admin-slash:
|
||||
groups: ["Project Lead"]
|
||||
users: []
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /v1.0/process-groups/manage-procurement/*
|
||||
manage-procurement-admin-models:
|
||||
groups: ["Project Lead"]
|
||||
users: []
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /v1.0/process-models/manage-procurement:*
|
||||
manage-procurement-admin-models-slash:
|
||||
groups: ["Project Lead"]
|
||||
users: []
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /v1.0/process-models/manage-procurement/*
|
||||
manage-procurement-admin-instances:
|
||||
groups: ["Project Lead"]
|
||||
users: []
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /v1.0/process-instances/manage-procurement:*
|
||||
manage-procurement-admin-instances-slash:
|
||||
groups: ["Project Lead"]
|
||||
users: []
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /v1.0/process-instances/manage-procurement/*
|
||||
|
||||
finance-admin:
|
||||
groups: ["Finance Team"]
|
||||
users: []
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /v1.0/process-groups/manage-procurement:procurement:*
|
||||
uri: /process-groups/manage-procurement:procurement:*
|
||||
|
||||
manage-revenue-streams-instances:
|
||||
groups: ["core-contributor", "demo"]
|
||||
groups: ["demo"]
|
||||
users: []
|
||||
allowed_permissions: [create, read]
|
||||
uri: /v1.0/process-instances/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/*
|
||||
|
||||
allowed_permissions: [create]
|
||||
uri: /process-instances/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/*
|
||||
manage-procurement-invoice-instances:
|
||||
groups: ["core-contributor", "demo"]
|
||||
groups: ["demo"]
|
||||
users: []
|
||||
allowed_permissions: [create, read]
|
||||
uri: /v1.0/process-instances/manage-procurement:procurement:core-contributor-invoice-management:*
|
||||
|
||||
allowed_permissions: [create]
|
||||
uri: /process-instances/manage-procurement:procurement:core-contributor-invoice-management:*
|
||||
manage-procurement-instances:
|
||||
groups: ["core-contributor", "demo"]
|
||||
groups: ["demo"]
|
||||
users: []
|
||||
allowed_permissions: [create, read]
|
||||
uri: /v1.0/process-instances/manage-procurement:vendor-lifecycle-management:*
|
||||
allowed_permissions: [create]
|
||||
uri: /process-instances/manage-procurement:vendor-lifecycle-management:*
|
||||
|
||||
manage-revenue-streams-instances-for-me:
|
||||
groups: ["demo"]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /process-instances/for-me/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/*
|
||||
manage-procurement-invoice-instances-for-me:
|
||||
groups: ["demo"]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /process-instances/for-me/manage-procurement:procurement:core-contributor-invoice-management:*
|
||||
manage-procurement-instances-for-me:
|
||||
groups: ["demo"]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /process-instances/for-me/manage-procurement:vendor-lifecycle-management:*
|
||||
|
||||
create-test-instances:
|
||||
groups: ["test"]
|
||||
users: []
|
||||
allowed_permissions: [create, read]
|
||||
uri: /v1.0/process-instances/misc:test:*
|
||||
|
||||
core1-admin-instances:
|
||||
groups: ["core-contributor", "Finance Team"]
|
||||
users: []
|
||||
allowed_permissions: [create, read]
|
||||
uri: /v1.0/process-instances/misc:category_number_one:process-model-with-form:*
|
||||
core1-admin-instances-slash:
|
||||
groups: ["core-contributor", "Finance Team"]
|
||||
users: []
|
||||
allowed_permissions: [create, read]
|
||||
uri: /v1.0/process-instances/misc:category_number_one:process-model-with-form/*
|
||||
uri: /process-instances/misc:test:*
|
||||
|
|
|
@ -2,14 +2,17 @@ default_group: everybody
|
|||
|
||||
users:
|
||||
admin:
|
||||
service: local_open_id
|
||||
email: admin@spiffworkflow.org
|
||||
password: admin
|
||||
preferred_username: Admin
|
||||
nelson:
|
||||
service: local_open_id
|
||||
email: nelson@spiffworkflow.org
|
||||
password: nelson
|
||||
preferred_username: Nelson
|
||||
malala:
|
||||
service: local_open_id
|
||||
email: malala@spiffworkflow.org
|
||||
password: malala
|
||||
preferred_username: Malala
|
||||
|
@ -18,17 +21,17 @@ groups:
|
|||
admin:
|
||||
users:
|
||||
[
|
||||
admin,
|
||||
admin@spiffworkflow.org,
|
||||
]
|
||||
Education:
|
||||
users:
|
||||
[
|
||||
malala
|
||||
malala@spiffworkflow.org
|
||||
]
|
||||
President:
|
||||
users:
|
||||
[
|
||||
nelson
|
||||
nelson@spiffworkflow.org
|
||||
]
|
||||
|
||||
permissions:
|
||||
|
@ -44,45 +47,44 @@ permissions:
|
|||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /v1.0/tasks/*
|
||||
uri: /tasks/*
|
||||
|
||||
# Everyone can see everything (all groups, and processes are visible)
|
||||
read-all-process-groups:
|
||||
groups: [ everybody ]
|
||||
users: [ ]
|
||||
allowed_permissions: [ read ]
|
||||
uri: /v1.0/process-groups/*
|
||||
uri: /process-groups/*
|
||||
read-all-process-models:
|
||||
groups: [ everybody ]
|
||||
users: [ ]
|
||||
allowed_permissions: [ read ]
|
||||
uri: /v1.0/process-models/*
|
||||
uri: /process-models/*
|
||||
read-all-process-instance:
|
||||
groups: [ everybody ]
|
||||
users: [ ]
|
||||
allowed_permissions: [ read ]
|
||||
uri: /v1.0/process-instances/*
|
||||
uri: /process-instances/*
|
||||
read-process-instance-reports:
|
||||
groups: [ everybody ]
|
||||
users: [ ]
|
||||
allowed_permissions: [ read ]
|
||||
uri: /v1.0/process-instances/reports/*
|
||||
uri: /process-instances/reports/*
|
||||
processes-read:
|
||||
groups: [ everybody ]
|
||||
users: [ ]
|
||||
allowed_permissions: [ read ]
|
||||
uri: /v1.0/processes
|
||||
|
||||
# Members of the Education group can change they processes work.
|
||||
uri: /processes
|
||||
# Members of the Education group can change the processes under "education".
|
||||
education-admin:
|
||||
groups: ["Education", "President"]
|
||||
users: []
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /v1.0/process-groups/education:*
|
||||
uri: /process-groups/education:*
|
||||
|
||||
# Anyone can start an education process.
|
||||
education-everybody:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [create, read]
|
||||
uri: /v1.0/process-instances/misc:category_number_one:process-model-with-form/*
|
||||
uri: /process-instances/misc:category_number_one:process-model-with-form/*
|
||||
|
|
|
@ -4,57 +4,48 @@ groups:
|
|||
admin:
|
||||
users:
|
||||
[
|
||||
admin,
|
||||
jakub,
|
||||
kb,
|
||||
alex,
|
||||
dan,
|
||||
mike,
|
||||
jason,
|
||||
j,
|
||||
jarrad,
|
||||
elizabeth,
|
||||
jon,
|
||||
natalia,
|
||||
admin@status.im,
|
||||
jakub@status.im,
|
||||
jarrad@status.im,
|
||||
kb@sartography.com,
|
||||
alex@sartography.com,
|
||||
dan@sartography.com,
|
||||
mike@sartography.com,
|
||||
jason@sartography.com,
|
||||
j@sartography.com,
|
||||
elizabeth@sartography.com,
|
||||
jon@sartography.com,
|
||||
]
|
||||
|
||||
Finance Team:
|
||||
users:
|
||||
[
|
||||
jakub,
|
||||
alex,
|
||||
dan,
|
||||
mike,
|
||||
jason,
|
||||
j,
|
||||
amir,
|
||||
jarrad,
|
||||
elizabeth,
|
||||
jon,
|
||||
natalia,
|
||||
sasha,
|
||||
fin,
|
||||
fin1,
|
||||
jakub@status.im,
|
||||
amir@status.im,
|
||||
jarrad@status.im,
|
||||
sasha@status.im,
|
||||
fin@sartography.com,
|
||||
fin1@sartography.com,
|
||||
alex@sartography.com,
|
||||
dan@sartography.com,
|
||||
mike@sartography.com,
|
||||
jason@sartography.com,
|
||||
j@sartography.com,
|
||||
elizabeth@sartography.com,
|
||||
jon@sartography.com,
|
||||
]
|
||||
|
||||
demo:
|
||||
users:
|
||||
[
|
||||
core,
|
||||
fin,
|
||||
fin1,
|
||||
harmeet,
|
||||
sasha,
|
||||
manuchehr,
|
||||
lead,
|
||||
lead1
|
||||
]
|
||||
|
||||
core-contributor:
|
||||
users:
|
||||
[
|
||||
core,
|
||||
harmeet,
|
||||
harmeet@status.im,
|
||||
sasha@status.im,
|
||||
manuchehr@status.im,
|
||||
core@status.im,
|
||||
fin@status.im,
|
||||
fin1@status.im,
|
||||
lead@status.im,
|
||||
lead1@status.im
|
||||
]
|
||||
|
||||
permissions:
|
||||
|
@ -67,104 +58,86 @@ permissions:
|
|||
groups: [admin]
|
||||
users: []
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /v1.0/process-instances/*
|
||||
uri: /process-instances/*
|
||||
|
||||
tasks-crud:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /v1.0/tasks/*
|
||||
|
||||
service-tasks:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /v1.0/service-tasks
|
||||
user-groups-for-current-user:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /v1.0/user-groups/for-current-user
|
||||
|
||||
|
||||
# read all for everybody
|
||||
# open system defaults for everybody
|
||||
read-all-process-groups:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /v1.0/process-groups/*
|
||||
uri: /process-groups/*
|
||||
read-all-process-models:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /v1.0/process-models/*
|
||||
uri: /process-models/*
|
||||
|
||||
# basic perms for everybody
|
||||
read-all-process-instances-for-me:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /v1.0/process-instances/for-me/*
|
||||
manage-process-instance-reports:
|
||||
uri: /process-instances/for-me/*
|
||||
read-process-instance-reports:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /v1.0/process-instances/reports/*
|
||||
uri: /process-instances/reports/*
|
||||
processes-read:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /v1.0/processes
|
||||
|
||||
|
||||
manage-procurement-admin-instances:
|
||||
groups: ["Project Lead"]
|
||||
users: []
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /v1.0/process-instances/manage-procurement:*
|
||||
manage-procurement-admin-instances-slash:
|
||||
groups: ["Project Lead"]
|
||||
users: []
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /v1.0/process-instances/manage-procurement/*
|
||||
manage-procurement-admin-instance-logs:
|
||||
groups: ["Project Lead"]
|
||||
uri: /processes
|
||||
service-tasks:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /v1.0/logs/manage-procurement:*
|
||||
manage-procurement-admin-instance-logs-slash:
|
||||
groups: ["Project Lead"]
|
||||
uri: /service-tasks
|
||||
tasks-crud:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /tasks/*
|
||||
user-groups-for-current-user:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /v1.0/logs/manage-procurement/*
|
||||
uri: /user-groups/for-current-user
|
||||
|
||||
manage-revenue-streams-instances:
|
||||
groups: ["core-contributor", "demo"]
|
||||
groups: ["demo"]
|
||||
users: []
|
||||
allowed_permissions: [create, read]
|
||||
uri: /v1.0/process-instances/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/*
|
||||
manage-revenue-streams-instance-logs:
|
||||
groups: ["core-contributor", "demo"]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /v1.0/logs/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/*
|
||||
|
||||
allowed_permissions: [create]
|
||||
uri: /process-instances/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/*
|
||||
manage-procurement-invoice-instances:
|
||||
groups: ["core-contributor", "demo"]
|
||||
groups: ["demo"]
|
||||
users: []
|
||||
allowed_permissions: [create, read]
|
||||
uri: /v1.0/process-instances/manage-procurement:procurement:core-contributor-invoice-management:*
|
||||
manage-procurement-invoice-instance-logs:
|
||||
groups: ["core-contributor", "demo"]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /v1.0/logs/manage-procurement:procurement:core-contributor-invoice-management:*
|
||||
|
||||
allowed_permissions: [create]
|
||||
uri: /process-instances/manage-procurement:procurement:core-contributor-invoice-management:*
|
||||
manage-procurement-instances:
|
||||
groups: ["core-contributor", "demo"]
|
||||
groups: ["demo"]
|
||||
users: []
|
||||
allowed_permissions: [create, read]
|
||||
uri: /v1.0/process-instances/manage-procurement:vendor-lifecycle-management:*
|
||||
manage-procurement-instance-logs:
|
||||
groups: ["core-contributor", "demo"]
|
||||
allowed_permissions: [create]
|
||||
uri: /process-instances/manage-procurement:vendor-lifecycle-management:*
|
||||
|
||||
manage-revenue-streams-instances-for-me:
|
||||
groups: ["demo"]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /v1.0/logs/manage-procurement:vendor-lifecycle-management:*
|
||||
uri: /process-instances/for-me/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/*
|
||||
manage-procurement-invoice-instances-for-me:
|
||||
groups: ["demo"]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /process-instances/for-me/manage-procurement:procurement:core-contributor-invoice-management:*
|
||||
manage-procurement-instances-for-me:
|
||||
groups: ["demo"]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /process-instances/for-me/manage-procurement:vendor-lifecycle-management:*
|
||||
|
||||
create-test-instances:
|
||||
groups: ["test"]
|
||||
users: []
|
||||
allowed_permissions: [create, read]
|
||||
uri: /process-instances/misc:test:*
|
||||
|
|
|
@ -4,58 +4,52 @@ groups:
|
|||
admin:
|
||||
users:
|
||||
[
|
||||
admin,
|
||||
jakub,
|
||||
kb,
|
||||
alex,
|
||||
dan,
|
||||
mike,
|
||||
jason,
|
||||
j,
|
||||
jarrad,
|
||||
elizabeth,
|
||||
jon,
|
||||
admin@status.im,
|
||||
jakub@status.im,
|
||||
jarrad@status.im,
|
||||
kb@sartography.com,
|
||||
alex@sartography.com,
|
||||
dan@sartography.com,
|
||||
mike@sartography.com,
|
||||
jason@sartography.com,
|
||||
j@sartography.com,
|
||||
elizabeth@sartography.com,
|
||||
jon@sartography.com,
|
||||
]
|
||||
|
||||
Finance Team:
|
||||
users:
|
||||
[
|
||||
jakub,
|
||||
alex,
|
||||
dan,
|
||||
mike,
|
||||
jason,
|
||||
j,
|
||||
amir,
|
||||
jarrad,
|
||||
elizabeth,
|
||||
jon,
|
||||
sasha,
|
||||
fin,
|
||||
fin1,
|
||||
jakub@status.im,
|
||||
amir@status.im,
|
||||
jarrad@status.im,
|
||||
sasha@status.im,
|
||||
fin@sartography.com,
|
||||
fin1@sartography.com,
|
||||
alex@sartography.com,
|
||||
dan@sartography.com,
|
||||
mike@sartography.com,
|
||||
jason@sartography.com,
|
||||
j@sartography.com,
|
||||
elizabeth@sartography.com,
|
||||
jon@sartography.com,
|
||||
]
|
||||
|
||||
demo:
|
||||
users:
|
||||
[
|
||||
core,
|
||||
fin,
|
||||
fin1,
|
||||
harmeet,
|
||||
sasha,
|
||||
manuchehr,
|
||||
lead,
|
||||
lead1
|
||||
harmeet@status.im,
|
||||
sasha@status.im,
|
||||
manuchehr@status.im,
|
||||
core@status.im,
|
||||
fin@status.im,
|
||||
fin1@status.im,
|
||||
lead@status.im,
|
||||
lead1@status.im
|
||||
]
|
||||
|
||||
core-contributor:
|
||||
users:
|
||||
[
|
||||
core,
|
||||
harmeet,
|
||||
]
|
||||
test:
|
||||
users: [natalia]
|
||||
users: [natalia@sartography.com]
|
||||
|
||||
permissions:
|
||||
admin:
|
||||
|
@ -64,109 +58,91 @@ permissions:
|
|||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /*
|
||||
|
||||
tasks-crud:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /v1.0/tasks/*
|
||||
|
||||
service-tasks:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /v1.0/service-tasks
|
||||
user-groups-for-current-user:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /v1.0/user-groups/for-current-user
|
||||
|
||||
|
||||
# read all for everybody
|
||||
# open system defaults for everybody
|
||||
read-all-process-groups:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /v1.0/process-groups/*
|
||||
uri: /process-groups/*
|
||||
read-all-process-models:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /v1.0/process-models/*
|
||||
uri: /process-models/*
|
||||
|
||||
# basic perms for everybody
|
||||
read-all-process-instances-for-me:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /v1.0/process-instances/for-me/*
|
||||
uri: /process-instances/for-me/*
|
||||
read-process-instance-reports:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /v1.0/process-instances/reports/*
|
||||
uri: /process-instances/reports/*
|
||||
processes-read:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /v1.0/processes
|
||||
uri: /processes
|
||||
service-tasks:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /service-tasks
|
||||
tasks-crud:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /tasks/*
|
||||
user-groups-for-current-user:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /user-groups/for-current-user
|
||||
|
||||
|
||||
manage-procurement-admin:
|
||||
groups: ["Project Lead"]
|
||||
users: []
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /v1.0/process-groups/manage-procurement:*
|
||||
manage-procurement-admin-slash:
|
||||
groups: ["Project Lead"]
|
||||
users: []
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /v1.0/process-groups/manage-procurement/*
|
||||
manage-procurement-admin-models:
|
||||
groups: ["Project Lead"]
|
||||
users: []
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /v1.0/process-models/manage-procurement:*
|
||||
manage-procurement-admin-models-slash:
|
||||
groups: ["Project Lead"]
|
||||
users: []
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /v1.0/process-models/manage-procurement/*
|
||||
manage-procurement-admin-instances:
|
||||
groups: ["Project Lead"]
|
||||
users: []
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /v1.0/process-instances/manage-procurement:*
|
||||
manage-procurement-admin-instances-slash:
|
||||
groups: ["Project Lead"]
|
||||
users: []
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /v1.0/process-instances/manage-procurement/*
|
||||
|
||||
finance-admin:
|
||||
groups: ["Finance Team"]
|
||||
users: []
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /v1.0/process-groups/manage-procurement:procurement:*
|
||||
uri: /process-groups/manage-procurement:procurement:*
|
||||
|
||||
manage-revenue-streams-instances:
|
||||
groups: ["core-contributor", "demo"]
|
||||
groups: ["demo"]
|
||||
users: []
|
||||
allowed_permissions: [create, read]
|
||||
uri: /v1.0/process-instances/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/*
|
||||
|
||||
allowed_permissions: [create]
|
||||
uri: /process-instances/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/*
|
||||
manage-procurement-invoice-instances:
|
||||
groups: ["core-contributor", "demo"]
|
||||
groups: ["demo"]
|
||||
users: []
|
||||
allowed_permissions: [create, read]
|
||||
uri: /v1.0/process-instances/manage-procurement:procurement:core-contributor-invoice-management:*
|
||||
|
||||
allowed_permissions: [create]
|
||||
uri: /process-instances/manage-procurement:procurement:core-contributor-invoice-management:*
|
||||
manage-procurement-instances:
|
||||
groups: ["core-contributor", "demo"]
|
||||
groups: ["demo"]
|
||||
users: []
|
||||
allowed_permissions: [create, read]
|
||||
uri: /v1.0/process-instances/manage-procurement:vendor-lifecycle-management:*
|
||||
allowed_permissions: [create]
|
||||
uri: /process-instances/manage-procurement:vendor-lifecycle-management:*
|
||||
|
||||
manage-revenue-streams-instances-for-me:
|
||||
groups: ["demo"]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /process-instances/for-me/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/*
|
||||
manage-procurement-invoice-instances-for-me:
|
||||
groups: ["demo"]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /process-instances/for-me/manage-procurement:procurement:core-contributor-invoice-management:*
|
||||
manage-procurement-instances-for-me:
|
||||
groups: ["demo"]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /process-instances/for-me/manage-procurement:vendor-lifecycle-management:*
|
||||
|
||||
create-test-instances:
|
||||
groups: ["test"]
|
||||
users: []
|
||||
allowed_permissions: [create, read]
|
||||
uri: /v1.0/process-instances/misc:test:*
|
||||
uri: /process-instances/misc:test:*
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
default_group: everybody
|
||||
|
||||
users:
|
||||
testadmin1:
|
||||
service: https://testing/openid/thing
|
||||
email: testadmin1@spiffworkflow.org
|
||||
password: admin
|
||||
preferred_username: El administrador de la muerte
|
||||
|
||||
groups:
|
||||
admin:
|
||||
users: [testadmin1, testadmin2]
|
||||
|
@ -14,7 +21,7 @@ permissions:
|
|||
admin:
|
||||
groups: [admin]
|
||||
users: []
|
||||
allowed_permissions: [create, read, update, delete, list, instantiate]
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /*
|
||||
|
||||
read-all:
|
||||
|
@ -27,29 +34,29 @@ permissions:
|
|||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /v1.0/tasks/*
|
||||
uri: /tasks/*
|
||||
|
||||
# TODO: all uris should really have the same structure
|
||||
finance-admin-group:
|
||||
groups: ["Finance Team"]
|
||||
users: [testuser4]
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /v1.0/process-groups/finance/*
|
||||
uri: /process-groups/finance/*
|
||||
|
||||
finance-admin-model:
|
||||
groups: ["Finance Team"]
|
||||
users: [testuser4]
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /v1.0/process-models/finance/*
|
||||
uri: /process-models/finance/*
|
||||
|
||||
finance-admin-model-lanes:
|
||||
groups: ["Finance Team"]
|
||||
users: [testuser4]
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /v1.0/process-models/finance:model_with_lanes/*
|
||||
uri: /process-models/finance:model_with_lanes/*
|
||||
|
||||
finance-admin-instance-run:
|
||||
groups: ["Finance Team"]
|
||||
users: [testuser4]
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /v1.0/process-instances/*
|
||||
uri: /process-instances/*
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
"""Api_version."""
|
||||
V1_API_PATH_PREFIX = "/v1.0"
|
|
@ -27,6 +27,9 @@ class GroupModel(FlaskBpmnGroupModel):
|
|||
identifier = db.Column(db.String(255))
|
||||
|
||||
user_group_assignments = relationship("UserGroupAssignmentModel", cascade="delete")
|
||||
user_group_assignments_waiting = relationship( # type: ignore
|
||||
"UserGroupAssignmentWaitingModel", cascade="delete"
|
||||
)
|
||||
users = relationship( # type: ignore
|
||||
"UserModel",
|
||||
viewonly=True,
|
||||
|
|
|
@ -32,14 +32,6 @@ class Permission(enum.Enum):
|
|||
update = "update"
|
||||
delete = "delete"
|
||||
|
||||
# maybe read to GET process_model/process-instances instead?
|
||||
list = "list"
|
||||
|
||||
# maybe use create instead on
|
||||
# POST http://localhost:7000/v1.0/process-models/category_number_one/call-activity/process-instances/*
|
||||
# POST http://localhost:7000/v1.0/process-models/category_number_one/call-activity/process-instances/332/run
|
||||
instantiate = "instantiate" # this is something you do to a process model
|
||||
|
||||
|
||||
class PermissionAssignmentModel(SpiffworkflowBaseDBModel):
|
||||
"""PermissionAssignmentModel."""
|
||||
|
|
|
@ -1,22 +1,15 @@
|
|||
"""User."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
import jwt
|
||||
import marshmallow
|
||||
from flask import current_app
|
||||
from flask_bpmn.api.api_error import ApiError
|
||||
from flask_bpmn.models.db import db
|
||||
from flask_bpmn.models.db import SpiffworkflowBaseDBModel
|
||||
from marshmallow import Schema
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.orm import validates
|
||||
|
||||
from spiffworkflow_backend.models.group import GroupModel
|
||||
from spiffworkflow_backend.services.authentication_service import (
|
||||
AuthenticationProviderTypes,
|
||||
)
|
||||
|
||||
|
||||
class UserNotFoundError(Exception):
|
||||
|
@ -28,14 +21,15 @@ class UserModel(SpiffworkflowBaseDBModel):
|
|||
|
||||
__tablename__ = "user"
|
||||
__table_args__ = (db.UniqueConstraint("service", "service_id", name="service_key"),)
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
# server and service id must be unique, not username.
|
||||
username = db.Column(db.String(255), nullable=False, unique=False)
|
||||
uid = db.Column(db.String(50), unique=True)
|
||||
service = db.Column(db.String(50), nullable=False, unique=False)
|
||||
username = db.Column(
|
||||
db.String(255), nullable=False, unique=True
|
||||
) # should always be a unique value
|
||||
service = db.Column(
|
||||
db.String(50), nullable=False, unique=False
|
||||
) # not 'openid' -- google, aws
|
||||
service_id = db.Column(db.String(255), nullable=False, unique=False)
|
||||
name = db.Column(db.String(255))
|
||||
display_name = db.Column(db.String(255))
|
||||
email = db.Column(db.String(255))
|
||||
updated_at_in_seconds: int = db.Column(db.Integer)
|
||||
created_at_in_seconds: int = db.Column(db.Integer)
|
||||
|
@ -49,21 +43,6 @@ class UserModel(SpiffworkflowBaseDBModel):
|
|||
)
|
||||
principal = relationship("PrincipalModel", uselist=False) # type: ignore
|
||||
|
||||
@validates("service")
|
||||
def validate_service(self, key: str, value: Any) -> str:
|
||||
"""Validate_service."""
|
||||
try:
|
||||
ap_type = getattr(AuthenticationProviderTypes, value, None)
|
||||
except Exception as e:
|
||||
raise ValueError(f"invalid service type: {value}") from e
|
||||
if ap_type is not None:
|
||||
ap_value: str = ap_type.value
|
||||
return ap_value
|
||||
raise ApiError(
|
||||
error_code="invalid_service",
|
||||
message=f"Could not validate service with value: {value}",
|
||||
)
|
||||
|
||||
def encode_auth_token(self) -> str:
|
||||
"""Generate the Auth Token.
|
||||
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
"""UserGroupAssignment."""
|
||||
from flask_bpmn.models.db import db
|
||||
from flask_bpmn.models.db import SpiffworkflowBaseDBModel
|
||||
from sqlalchemy import ForeignKey
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from spiffworkflow_backend.models.group import GroupModel
|
||||
|
||||
|
||||
class UserGroupAssignmentWaitingModel(SpiffworkflowBaseDBModel):
|
||||
"""When a user is assigned to a group, but that username does not exist.
|
||||
|
||||
We cache it here to be applied in the event the user does log in to the system.
|
||||
"""
|
||||
|
||||
MATCH_ALL_USERS = "*"
|
||||
__tablename__ = "user_group_assignment_waiting"
|
||||
__table_args__ = (
|
||||
db.UniqueConstraint(
|
||||
"username", "group_id", name="user_group_assignment_staged_unique"
|
||||
),
|
||||
)
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
username = db.Column(db.String(255), nullable=False)
|
||||
group_id = db.Column(ForeignKey(GroupModel.id), nullable=False)
|
||||
|
||||
group = relationship("GroupModel", overlaps="groups,user_group_assignments_waiting,users") # type: ignore
|
||||
|
||||
def is_match_all(self) -> bool:
|
||||
"""Is_match_all."""
|
||||
if self.username == self.MATCH_ALL_USERS:
|
||||
return True
|
||||
return False
|
|
@ -141,7 +141,7 @@ def process_model_save(process_model_id: str, file_name: str) -> Union[str, Resp
|
|||
@admin_blueprint.route("/process-models/<process_model_id>/run", methods=["GET"])
|
||||
def process_model_run(process_model_id: str) -> Union[str, Response]:
|
||||
"""Process_model_run."""
|
||||
user = UserService.create_user("internal", "Mr. Test", username="Mr. Test")
|
||||
user = UserService.create_user("Mr. Test", "internal", "Mr. Test")
|
||||
process_instance = (
|
||||
ProcessInstanceService.create_process_instance_from_process_model_identifier(
|
||||
process_model_id, user
|
||||
|
|
|
@ -111,6 +111,7 @@ def token() -> dict:
|
|||
"iat": time.time(),
|
||||
"exp": time.time() + 86400, # Expire after a day.
|
||||
"sub": user_name,
|
||||
"email": user_details["email"],
|
||||
"preferred_username": user_details.get("preferred_username", user_name),
|
||||
},
|
||||
client_secret,
|
||||
|
|
|
@ -76,7 +76,7 @@ def verify_token(
|
|||
except ApiError as ae: # API Error is only thrown in the token is outdated.
|
||||
# Try to refresh the token
|
||||
user = UserService.get_user_by_service_and_service_id(
|
||||
"open_id", decoded_token["sub"]
|
||||
decoded_token["iss"], decoded_token["sub"]
|
||||
)
|
||||
if user:
|
||||
refresh_token = AuthenticationService.get_refresh_token(user.id)
|
||||
|
@ -105,10 +105,12 @@ def verify_token(
|
|||
) from e
|
||||
|
||||
if (
|
||||
user_info is not None and "error" not in user_info
|
||||
user_info is not None
|
||||
and "error" not in user_info
|
||||
and "iss" in user_info
|
||||
): # not sure what to test yet
|
||||
user_model = (
|
||||
UserModel.query.filter(UserModel.service == "open_id")
|
||||
UserModel.query.filter(UserModel.service == user_info["iss"])
|
||||
.filter(UserModel.service_id == user_info["sub"])
|
||||
.first()
|
||||
)
|
||||
|
@ -341,9 +343,5 @@ def get_user_from_decoded_internal_token(decoded_token: dict) -> Optional[UserMo
|
|||
)
|
||||
if user:
|
||||
return user
|
||||
user = UserModel(
|
||||
username=service_id,
|
||||
service=service,
|
||||
service_id=service_id,
|
||||
)
|
||||
user = UserService.create_user(service_id, service, service_id)
|
||||
return user
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
"""Get_env."""
|
||||
from typing import Any
|
||||
|
||||
from spiffworkflow_backend.models.group import GroupModel
|
||||
from spiffworkflow_backend.models.group import GroupNotFoundError
|
||||
from spiffworkflow_backend.models.script_attributes_context import (
|
||||
ScriptAttributesContext,
|
||||
)
|
||||
from spiffworkflow_backend.models.user import UserModel
|
||||
from spiffworkflow_backend.models.user import UserNotFoundError
|
||||
from spiffworkflow_backend.scripts.script import Script
|
||||
from spiffworkflow_backend.services.user_service import UserService
|
||||
|
||||
|
||||
class AddUserToGroup(Script):
|
||||
"""AddUserToGroup."""
|
||||
|
||||
def get_description(self) -> str:
|
||||
"""Get_description."""
|
||||
return """Add a given user to a given group."""
|
||||
|
||||
def run(
|
||||
self,
|
||||
script_attributes_context: ScriptAttributesContext,
|
||||
*args: Any,
|
||||
**kwargs: Any,
|
||||
) -> Any:
|
||||
"""Run."""
|
||||
username = args[0]
|
||||
group_identifier = args[1]
|
||||
user = UserModel.query.filter_by(username=username).first()
|
||||
if user is None:
|
||||
raise UserNotFoundError(
|
||||
f"Script 'add_user_to_group' could not find a user with username: {username}"
|
||||
)
|
||||
|
||||
group = GroupModel.query.filter_by(identifier=group_identifier).first()
|
||||
if group is None:
|
||||
raise GroupNotFoundError(
|
||||
f"Script 'add_user_to_group' could not find group with identifier '{group_identifier}'."
|
||||
)
|
||||
|
||||
UserService.add_user_to_group(user, group)
|
|
@ -10,6 +10,11 @@ from spiffworkflow_backend.scripts.script import Script
|
|||
class FactService(Script):
|
||||
"""FactService."""
|
||||
|
||||
@staticmethod
|
||||
def requires_privileged_permissions() -> bool:
|
||||
"""We have deemed this function safe to run without elevated permissions."""
|
||||
return False
|
||||
|
||||
def get_description(self) -> str:
|
||||
"""Get_description."""
|
||||
return """Just your basic class that can pull in data from a few api endpoints and
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
"""Get_env."""
|
||||
from collections import OrderedDict
|
||||
from typing import Any
|
||||
|
||||
from spiffworkflow_backend.models.group import GroupModel
|
||||
from spiffworkflow_backend.models.permission_assignment import PermissionAssignmentModel
|
||||
from spiffworkflow_backend.models.permission_target import PermissionTargetModel
|
||||
from spiffworkflow_backend.models.principal import PrincipalModel
|
||||
from spiffworkflow_backend.models.script_attributes_context import (
|
||||
ScriptAttributesContext,
|
||||
)
|
||||
from spiffworkflow_backend.scripts.script import Script
|
||||
|
||||
|
||||
class GetAllPermissions(Script):
|
||||
"""GetAllPermissions."""
|
||||
|
||||
def get_description(self) -> str:
|
||||
"""Get_description."""
|
||||
return """Get all permissions currently in the system."""
|
||||
|
||||
def run(
|
||||
self,
|
||||
script_attributes_context: ScriptAttributesContext,
|
||||
*args: Any,
|
||||
**kwargs: Any,
|
||||
) -> Any:
|
||||
"""Run."""
|
||||
permission_assignments = (
|
||||
PermissionAssignmentModel.query.join(
|
||||
PrincipalModel,
|
||||
PrincipalModel.id == PermissionAssignmentModel.principal_id,
|
||||
)
|
||||
.join(GroupModel, GroupModel.id == PrincipalModel.group_id)
|
||||
.join(
|
||||
PermissionTargetModel,
|
||||
PermissionTargetModel.id
|
||||
== PermissionAssignmentModel.permission_target_id,
|
||||
)
|
||||
.add_columns(
|
||||
PermissionAssignmentModel.permission,
|
||||
PermissionTargetModel.uri,
|
||||
GroupModel.identifier.label("group_identifier"),
|
||||
)
|
||||
)
|
||||
|
||||
permissions: OrderedDict[tuple[str, str], list[str]] = OrderedDict()
|
||||
for pa in permission_assignments:
|
||||
permissions.setdefault((pa.group_identifier, pa.uri), []).append(
|
||||
pa.permission
|
||||
)
|
||||
|
||||
return [
|
||||
{"group_identifier": k[0], "uri": k[1], "permissions": sorted(v)}
|
||||
for k, v in permissions.items()
|
||||
]
|
|
@ -12,6 +12,11 @@ from spiffworkflow_backend.scripts.script import Script
|
|||
class GetCurrentUser(Script):
|
||||
"""GetCurrentUser."""
|
||||
|
||||
@staticmethod
|
||||
def requires_privileged_permissions() -> bool:
|
||||
"""We have deemed this function safe to run without elevated permissions."""
|
||||
return False
|
||||
|
||||
def get_description(self) -> str:
|
||||
"""Get_description."""
|
||||
return """Return the current user."""
|
||||
|
|
|
@ -10,6 +10,11 @@ from spiffworkflow_backend.scripts.script import Script
|
|||
class GetEnv(Script):
|
||||
"""GetEnv."""
|
||||
|
||||
@staticmethod
|
||||
def requires_privileged_permissions() -> bool:
|
||||
"""We have deemed this function safe to run without elevated permissions."""
|
||||
return False
|
||||
|
||||
def get_description(self) -> str:
|
||||
"""Get_description."""
|
||||
return """Returns the current environment - ie testing, staging, production."""
|
||||
|
|
|
@ -12,6 +12,11 @@ from spiffworkflow_backend.scripts.script import Script
|
|||
class GetFrontendUrl(Script):
|
||||
"""GetFrontendUrl."""
|
||||
|
||||
@staticmethod
|
||||
def requires_privileged_permissions() -> bool:
|
||||
"""We have deemed this function safe to run without elevated permissions."""
|
||||
return False
|
||||
|
||||
def get_description(self) -> str:
|
||||
"""Get_description."""
|
||||
return """Return the url to the frontend."""
|
||||
|
|
|
@ -12,6 +12,11 @@ from spiffworkflow_backend.scripts.script import Script
|
|||
class GetGroupMembers(Script):
|
||||
"""GetGroupMembers."""
|
||||
|
||||
@staticmethod
|
||||
def requires_privileged_permissions() -> bool:
|
||||
"""We have deemed this function safe to run without elevated permissions."""
|
||||
return False
|
||||
|
||||
def get_description(self) -> str:
|
||||
"""Get_description."""
|
||||
return """Return the list of usernames of the users in the given group."""
|
||||
|
|
|
@ -14,6 +14,11 @@ from spiffworkflow_backend.scripts.script import Script
|
|||
class GetLocaltime(Script):
|
||||
"""GetLocaltime."""
|
||||
|
||||
@staticmethod
|
||||
def requires_privileged_permissions() -> bool:
|
||||
"""We have deemed this function safe to run without elevated permissions."""
|
||||
return False
|
||||
|
||||
def get_description(self) -> str:
|
||||
"""Get_description."""
|
||||
return """Converts a Datetime object into a Datetime object for a specific timezone.
|
||||
|
|
|
@ -10,6 +10,11 @@ from spiffworkflow_backend.scripts.script import Script
|
|||
class GetProcessInfo(Script):
|
||||
"""GetProcessInfo."""
|
||||
|
||||
@staticmethod
|
||||
def requires_privileged_permissions() -> bool:
|
||||
"""We have deemed this function safe to run without elevated permissions."""
|
||||
return False
|
||||
|
||||
def get_description(self) -> str:
|
||||
"""Get_description."""
|
||||
return """Returns a dictionary of information about the currently running process."""
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
"""Get_env."""
|
||||
from typing import Any
|
||||
|
||||
from spiffworkflow_backend.models.script_attributes_context import (
|
||||
ScriptAttributesContext,
|
||||
)
|
||||
from spiffworkflow_backend.scripts.script import Script
|
||||
from spiffworkflow_backend.services.authorization_service import AuthorizationService
|
||||
|
||||
|
||||
class RecreatePermissions(Script):
|
||||
"""RecreatePermissions."""
|
||||
|
||||
def get_description(self) -> str:
|
||||
"""Get_description."""
|
||||
return """Add permissions using a dict.
|
||||
group_info: [
|
||||
{
|
||||
'name': group_identifier,
|
||||
'users': array_of_users,
|
||||
'permissions': [
|
||||
{
|
||||
'actions': array_of_actions - create, read, etc,
|
||||
'uri': target_uri
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
"""
|
||||
|
||||
def run(
|
||||
self,
|
||||
script_attributes_context: ScriptAttributesContext,
|
||||
*args: Any,
|
||||
**kwargs: Any,
|
||||
) -> Any:
|
||||
"""Run."""
|
||||
group_info = args[0]
|
||||
AuthorizationService.refresh_permissions(group_info)
|
|
@ -10,9 +10,12 @@ from typing import Callable
|
|||
|
||||
from flask_bpmn.api.api_error import ApiError
|
||||
|
||||
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
|
||||
from spiffworkflow_backend.models.process_instance import ProcessInstanceNotFoundError
|
||||
from spiffworkflow_backend.models.script_attributes_context import (
|
||||
ScriptAttributesContext,
|
||||
)
|
||||
from spiffworkflow_backend.services.authorization_service import AuthorizationService
|
||||
|
||||
# Generally speaking, having some global in a flask app is TERRIBLE.
|
||||
# This is here, because after loading the application this will never change under
|
||||
|
@ -20,6 +23,10 @@ from spiffworkflow_backend.models.script_attributes_context import (
|
|||
SCRIPT_SUB_CLASSES = None
|
||||
|
||||
|
||||
class ScriptUnauthorizedForUserError(Exception):
|
||||
"""ScriptUnauthorizedForUserError."""
|
||||
|
||||
|
||||
class Script:
|
||||
"""Provides an abstract class that defines how scripts should work, this must be extended in all Script Tasks."""
|
||||
|
||||
|
@ -43,6 +50,15 @@ class Script:
|
|||
+ "does not properly implement the run function.",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def requires_privileged_permissions() -> bool:
|
||||
"""It seems safer to default to True and make safe functions opt in for any user to run them.
|
||||
|
||||
To give access to script for a given user, add a 'create' permission with following target-uri:
|
||||
'/can-run-privileged-script/{script_name}'
|
||||
"""
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def generate_augmented_list(
|
||||
script_attributes_context: ScriptAttributesContext,
|
||||
|
@ -71,18 +87,50 @@ class Script:
|
|||
that we created.
|
||||
"""
|
||||
instance = subclass()
|
||||
return lambda *ar, **kw: subclass.run(
|
||||
instance,
|
||||
script_attributes_context,
|
||||
*ar,
|
||||
**kw,
|
||||
)
|
||||
|
||||
def check_script_permission() -> None:
|
||||
"""Check_script_permission."""
|
||||
if subclass.requires_privileged_permissions():
|
||||
script_function_name = get_script_function_name(subclass)
|
||||
uri = f"/can-run-privileged-script/{script_function_name}"
|
||||
process_instance = ProcessInstanceModel.query.filter_by(
|
||||
id=script_attributes_context.process_instance_id
|
||||
).first()
|
||||
if process_instance is None:
|
||||
raise ProcessInstanceNotFoundError(
|
||||
f"Could not find a process instance with id '{script_attributes_context.process_instance_id}' "
|
||||
f"when running script '{script_function_name}'"
|
||||
)
|
||||
user = process_instance.process_initiator
|
||||
has_permission = AuthorizationService.user_has_permission(
|
||||
user=user, permission="create", target_uri=uri
|
||||
)
|
||||
if not has_permission:
|
||||
raise ScriptUnauthorizedForUserError(
|
||||
f"User {user.username} does not have access to run privileged script '{script_function_name}'"
|
||||
)
|
||||
|
||||
def run_script_if_allowed(*ar: Any, **kw: Any) -> Any:
|
||||
"""Run_script_if_allowed."""
|
||||
check_script_permission()
|
||||
return subclass.run(
|
||||
instance,
|
||||
script_attributes_context,
|
||||
*ar,
|
||||
**kw,
|
||||
)
|
||||
|
||||
return run_script_if_allowed
|
||||
|
||||
def get_script_function_name(subclass: type[Script]) -> str:
|
||||
"""Get_script_function_name."""
|
||||
return subclass.__module__.split(".")[-1]
|
||||
|
||||
execlist = {}
|
||||
subclasses = Script.get_all_subclasses()
|
||||
for x in range(len(subclasses)):
|
||||
subclass = subclasses[x]
|
||||
execlist[subclass.__module__.split(".")[-1]] = make_closure(
|
||||
execlist[get_script_function_name(subclass)] = make_closure(
|
||||
subclass, script_attributes_context=script_attributes_context
|
||||
)
|
||||
return execlist
|
||||
|
|
|
@ -93,7 +93,7 @@ class AuthenticationService:
|
|||
+ f"?state={state}&"
|
||||
+ "response_type=code&"
|
||||
+ f"client_id={self.client_id()}&"
|
||||
+ "scope=openid&"
|
||||
+ "scope=openid profile email&"
|
||||
+ f"redirect_uri={return_redirect_url}"
|
||||
)
|
||||
return login_redirect_url
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
"""Authorization_service."""
|
||||
import inspect
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
from hashlib import sha256
|
||||
from hmac import compare_digest
|
||||
from hmac import HMAC
|
||||
from typing import Any
|
||||
from typing import Optional
|
||||
from typing import Set
|
||||
from typing import TypedDict
|
||||
from typing import Union
|
||||
|
||||
import jwt
|
||||
|
@ -19,6 +23,7 @@ from SpiffWorkflow.task import Task as SpiffTask # type: ignore
|
|||
from sqlalchemy import or_
|
||||
from sqlalchemy import text
|
||||
|
||||
from spiffworkflow_backend.helpers.api_version import V1_API_PATH_PREFIX
|
||||
from spiffworkflow_backend.models.group import GroupModel
|
||||
from spiffworkflow_backend.models.human_task import HumanTaskModel
|
||||
from spiffworkflow_backend.models.permission_assignment import PermissionAssignmentModel
|
||||
|
@ -45,6 +50,34 @@ class UserDoesNotHaveAccessToTaskError(Exception):
|
|||
"""UserDoesNotHaveAccessToTaskError."""
|
||||
|
||||
|
||||
class InvalidPermissionError(Exception):
|
||||
"""InvalidPermissionError."""
|
||||
|
||||
|
||||
@dataclass
|
||||
class PermissionToAssign:
|
||||
"""PermissionToAssign."""
|
||||
|
||||
permission: str
|
||||
target_uri: str
|
||||
|
||||
|
||||
PATH_SEGMENTS_FOR_PERMISSION_ALL = [
|
||||
"/logs",
|
||||
"/process-instances",
|
||||
"/process-instance-suspend",
|
||||
"/process-instance-terminate",
|
||||
"/task-data",
|
||||
]
|
||||
|
||||
|
||||
class DesiredPermissionDict(TypedDict):
|
||||
"""DesiredPermissionDict."""
|
||||
|
||||
group_identifiers: Set[str]
|
||||
permission_assignments: list[PermissionAssignmentModel]
|
||||
|
||||
|
||||
class AuthorizationService:
|
||||
"""Determine whether a user has permission to perform their request."""
|
||||
|
||||
|
@ -75,6 +108,7 @@ class AuthorizationService:
|
|||
) -> bool:
|
||||
"""Has_permission."""
|
||||
principal_ids = [p.id for p in principals]
|
||||
target_uri_normalized = target_uri.removeprefix(V1_API_PATH_PREFIX)
|
||||
|
||||
permission_assignments = (
|
||||
PermissionAssignmentModel.query.filter(
|
||||
|
@ -84,10 +118,12 @@ class AuthorizationService:
|
|||
.join(PermissionTargetModel)
|
||||
.filter(
|
||||
or_(
|
||||
text(f"'{target_uri}' LIKE permission_target.uri"),
|
||||
text(f"'{target_uri_normalized}' LIKE permission_target.uri"),
|
||||
# to check for exact matches as well
|
||||
# see test_user_can_access_base_path_when_given_wildcard_permission unit test
|
||||
text(f"'{target_uri}' = replace(permission_target.uri, '/%', '')"),
|
||||
text(
|
||||
f"'{target_uri_normalized}' = replace(replace(permission_target.uri, '/%', ''), ':%', '')"
|
||||
),
|
||||
)
|
||||
)
|
||||
.all()
|
||||
|
@ -127,17 +163,15 @@ class AuthorizationService:
|
|||
return cls.has_permission(principals, permission, target_uri)
|
||||
|
||||
@classmethod
|
||||
def delete_all_permissions_and_recreate(cls) -> None:
|
||||
"""Delete_all_permissions_and_recreate."""
|
||||
def delete_all_permissions(cls) -> None:
|
||||
"""Delete_all_permissions_and_recreate. EXCEPT For permissions for the current user?"""
|
||||
for model in [PermissionAssignmentModel, PermissionTargetModel]:
|
||||
db.session.query(model).delete()
|
||||
|
||||
# cascading to principals doesn't seem to work when attempting to delete all so do it like this instead
|
||||
for group in GroupModel.query.all():
|
||||
db.session.delete(group)
|
||||
|
||||
db.session.commit()
|
||||
cls.import_permissions_from_yaml_file()
|
||||
|
||||
@classmethod
|
||||
def associate_user_with_group(cls, user: UserModel, group: GroupModel) -> None:
|
||||
|
@ -155,7 +189,7 @@ class AuthorizationService:
|
|||
@classmethod
|
||||
def import_permissions_from_yaml_file(
|
||||
cls, raise_if_missing_user: bool = False
|
||||
) -> None:
|
||||
) -> DesiredPermissionDict:
|
||||
"""Import_permissions_from_yaml_file."""
|
||||
if current_app.config["SPIFFWORKFLOW_BACKEND_PERMISSIONS_FILE_NAME"] is None:
|
||||
raise (
|
||||
|
@ -169,13 +203,16 @@ class AuthorizationService:
|
|||
permission_configs = yaml.safe_load(file)
|
||||
|
||||
default_group = None
|
||||
unique_user_group_identifiers: Set[str] = set()
|
||||
if "default_group" in permission_configs:
|
||||
default_group_identifier = permission_configs["default_group"]
|
||||
default_group = GroupService.find_or_create_group(default_group_identifier)
|
||||
unique_user_group_identifiers.add(default_group_identifier)
|
||||
|
||||
if "groups" in permission_configs:
|
||||
for group_identifier, group_config in permission_configs["groups"].items():
|
||||
group = GroupService.find_or_create_group(group_identifier)
|
||||
unique_user_group_identifiers.add(group_identifier)
|
||||
for username in group_config["users"]:
|
||||
user = UserModel.query.filter_by(username=username).first()
|
||||
if user is None:
|
||||
|
@ -188,26 +225,25 @@ class AuthorizationService:
|
|||
continue
|
||||
cls.associate_user_with_group(user, group)
|
||||
|
||||
permission_assignments = []
|
||||
if "permissions" in permission_configs:
|
||||
for _permission_identifier, permission_config in permission_configs[
|
||||
"permissions"
|
||||
].items():
|
||||
uri = permission_config["uri"]
|
||||
uri_with_percent = re.sub(r"\*", "%", uri)
|
||||
permission_target = PermissionTargetModel.query.filter_by(
|
||||
uri=uri_with_percent
|
||||
).first()
|
||||
if permission_target is None:
|
||||
permission_target = PermissionTargetModel(uri=uri_with_percent)
|
||||
db.session.add(permission_target)
|
||||
db.session.commit()
|
||||
permission_target = cls.find_or_create_permission_target(uri)
|
||||
|
||||
for allowed_permission in permission_config["allowed_permissions"]:
|
||||
if "groups" in permission_config:
|
||||
for group_identifier in permission_config["groups"]:
|
||||
group = GroupService.find_or_create_group(group_identifier)
|
||||
cls.create_permission_for_principal(
|
||||
group.principal, permission_target, allowed_permission
|
||||
unique_user_group_identifiers.add(group_identifier)
|
||||
permission_assignments.append(
|
||||
cls.create_permission_for_principal(
|
||||
group.principal,
|
||||
permission_target,
|
||||
allowed_permission,
|
||||
)
|
||||
)
|
||||
if "users" in permission_config:
|
||||
for username in permission_config["users"]:
|
||||
|
@ -218,14 +254,35 @@ class AuthorizationService:
|
|||
.filter(UserModel.username == username)
|
||||
.first()
|
||||
)
|
||||
cls.create_permission_for_principal(
|
||||
principal, permission_target, allowed_permission
|
||||
permission_assignments.append(
|
||||
cls.create_permission_for_principal(
|
||||
principal, permission_target, allowed_permission
|
||||
)
|
||||
)
|
||||
|
||||
if default_group is not None:
|
||||
for user in UserModel.query.all():
|
||||
cls.associate_user_with_group(user, default_group)
|
||||
|
||||
return {
|
||||
"group_identifiers": unique_user_group_identifiers,
|
||||
"permission_assignments": permission_assignments,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def find_or_create_permission_target(cls, uri: str) -> PermissionTargetModel:
|
||||
"""Find_or_create_permission_target."""
|
||||
uri_with_percent = re.sub(r"\*", "%", uri)
|
||||
target_uri_normalized = uri_with_percent.removeprefix(V1_API_PATH_PREFIX)
|
||||
permission_target: Optional[
|
||||
PermissionTargetModel
|
||||
] = PermissionTargetModel.query.filter_by(uri=target_uri_normalized).first()
|
||||
if permission_target is None:
|
||||
permission_target = PermissionTargetModel(uri=target_uri_normalized)
|
||||
db.session.add(permission_target)
|
||||
db.session.commit()
|
||||
return permission_target
|
||||
|
||||
@classmethod
|
||||
def create_permission_for_principal(
|
||||
cls,
|
||||
|
@ -449,33 +506,48 @@ class AuthorizationService:
|
|||
@classmethod
|
||||
def create_user_from_sign_in(cls, user_info: dict) -> UserModel:
|
||||
"""Create_user_from_sign_in."""
|
||||
"""Name, family_name, given_name, middle_name, nickname, preferred_username,"""
|
||||
"""Profile, picture, website, gender, birthdate, zoneinfo, locale, and updated_at. """
|
||||
"""Email."""
|
||||
is_new_user = False
|
||||
user_model = (
|
||||
UserModel.query.filter(UserModel.service == "open_id")
|
||||
UserModel.query.filter(UserModel.service == user_info["iss"])
|
||||
.filter(UserModel.service_id == user_info["sub"])
|
||||
.first()
|
||||
)
|
||||
email = display_name = username = ""
|
||||
if "email" in user_info:
|
||||
username = user_info["email"]
|
||||
email = user_info["email"]
|
||||
else: # we fall back to the sub, which may be very ugly.
|
||||
username = user_info["sub"] + "@" + user_info["iss"]
|
||||
|
||||
if "preferred_username" in user_info:
|
||||
display_name = user_info["preferred_username"]
|
||||
elif "nickname" in user_info:
|
||||
display_name = user_info["nickname"]
|
||||
elif "name" in user_info:
|
||||
display_name = user_info["name"]
|
||||
|
||||
if user_model is None:
|
||||
current_app.logger.debug("create_user in login_return")
|
||||
is_new_user = True
|
||||
name = username = email = ""
|
||||
if "name" in user_info:
|
||||
name = user_info["name"]
|
||||
if "username" in user_info:
|
||||
username = user_info["username"]
|
||||
elif "preferred_username" in user_info:
|
||||
username = user_info["preferred_username"]
|
||||
if "email" in user_info:
|
||||
email = user_info["email"]
|
||||
user_model = UserService().create_user(
|
||||
service="open_id",
|
||||
service_id=user_info["sub"],
|
||||
name=name,
|
||||
username=username,
|
||||
service=user_info["iss"],
|
||||
service_id=user_info["sub"],
|
||||
email=email,
|
||||
display_name=display_name,
|
||||
)
|
||||
|
||||
else:
|
||||
# Update with the latest information
|
||||
user_model.username = username
|
||||
user_model.email = email
|
||||
user_model.display_name = display_name
|
||||
user_model.service = user_info["iss"]
|
||||
user_model.service_id = user_info["sub"]
|
||||
|
||||
# this may eventually get too slow.
|
||||
# when it does, be careful about backgrounding, because
|
||||
# the user will immediately need permissions to use the site.
|
||||
|
@ -490,6 +562,212 @@ class AuthorizationService:
|
|||
# this cannot be None so ignore mypy
|
||||
return user_model # type: ignore
|
||||
|
||||
@classmethod
|
||||
def get_permissions_to_assign(
|
||||
cls,
|
||||
permission_set: str,
|
||||
process_related_path_segment: str,
|
||||
target_uris: list[str],
|
||||
) -> list[PermissionToAssign]:
|
||||
"""Get_permissions_to_assign."""
|
||||
permissions = permission_set.split(",")
|
||||
if permission_set == "all":
|
||||
permissions = ["create", "read", "update", "delete"]
|
||||
|
||||
permissions_to_assign: list[PermissionToAssign] = []
|
||||
|
||||
# we were thinking that if you can start an instance, you ought to be able to view your own instances.
|
||||
if permission_set == "start":
|
||||
target_uri = f"/process-instances/{process_related_path_segment}"
|
||||
permissions_to_assign.append(
|
||||
PermissionToAssign(permission="create", target_uri=target_uri)
|
||||
)
|
||||
target_uri = f"/process-instances/for-me/{process_related_path_segment}"
|
||||
permissions_to_assign.append(
|
||||
PermissionToAssign(permission="read", target_uri=target_uri)
|
||||
)
|
||||
|
||||
else:
|
||||
if permission_set == "all":
|
||||
for path_segment in PATH_SEGMENTS_FOR_PERMISSION_ALL:
|
||||
target_uris.append(f"{path_segment}/{process_related_path_segment}")
|
||||
|
||||
for target_uri in target_uris:
|
||||
for permission in permissions:
|
||||
permissions_to_assign.append(
|
||||
PermissionToAssign(permission=permission, target_uri=target_uri)
|
||||
)
|
||||
|
||||
return permissions_to_assign
|
||||
|
||||
@classmethod
|
||||
def explode_permissions(
|
||||
cls, permission_set: str, target: str
|
||||
) -> list[PermissionToAssign]:
|
||||
"""Explodes given permissions to and returns list of PermissionToAssign objects.
|
||||
|
||||
These can be used to then iterate through and inserted into the database.
|
||||
Target Macros:
|
||||
ALL
|
||||
* gives access to ALL api endpoints - useful to give admin-like permissions
|
||||
PG:[process_group_identifier]
|
||||
* affects given process-group and all sub process-groups and process-models
|
||||
PM:[process_model_identifier]
|
||||
* affects given process-model
|
||||
BASIC
|
||||
* Basic access to complete tasks and use the site
|
||||
|
||||
Permission Macros:
|
||||
all
|
||||
* create, read, update, delete
|
||||
start
|
||||
* create process-instances (aka instantiate or start a process-model)
|
||||
* only works with PG and PM target macros
|
||||
"""
|
||||
permissions_to_assign: list[PermissionToAssign] = []
|
||||
permissions = permission_set.split(",")
|
||||
if permission_set == "all":
|
||||
permissions = ["create", "read", "update", "delete"]
|
||||
|
||||
if target.startswith("PG:"):
|
||||
process_group_identifier = (
|
||||
target.removeprefix("PG:").replace("/", ":").removeprefix(":")
|
||||
)
|
||||
process_related_path_segment = f"{process_group_identifier}:*"
|
||||
if process_group_identifier == "ALL":
|
||||
process_related_path_segment = "*"
|
||||
target_uris = [
|
||||
f"/process-groups/{process_related_path_segment}",
|
||||
f"/process-models/{process_related_path_segment}",
|
||||
]
|
||||
permissions_to_assign = (
|
||||
permissions_to_assign
|
||||
+ cls.get_permissions_to_assign(
|
||||
permission_set, process_related_path_segment, target_uris
|
||||
)
|
||||
)
|
||||
|
||||
elif target.startswith("PM:"):
|
||||
process_model_identifier = (
|
||||
target.removeprefix("PM:").replace("/", ":").removeprefix(":")
|
||||
)
|
||||
process_related_path_segment = f"{process_model_identifier}/*"
|
||||
|
||||
if process_model_identifier == "ALL":
|
||||
process_related_path_segment = "*"
|
||||
|
||||
target_uris = [f"/process-models/{process_related_path_segment}"]
|
||||
permissions_to_assign = (
|
||||
permissions_to_assign
|
||||
+ cls.get_permissions_to_assign(
|
||||
permission_set, process_related_path_segment, target_uris
|
||||
)
|
||||
)
|
||||
|
||||
elif permission_set == "start":
|
||||
raise InvalidPermissionError(
|
||||
"Permission 'start' is only available for macros PM and PG."
|
||||
)
|
||||
|
||||
elif target.startswith("BASIC"):
|
||||
permissions_to_assign.append(
|
||||
PermissionToAssign(
|
||||
permission="read", target_uri="/process-instances/for-me"
|
||||
)
|
||||
)
|
||||
permissions_to_assign.append(
|
||||
PermissionToAssign(permission="read", target_uri="/processes")
|
||||
)
|
||||
permissions_to_assign.append(
|
||||
PermissionToAssign(permission="read", target_uri="/service-tasks")
|
||||
)
|
||||
permissions_to_assign.append(
|
||||
PermissionToAssign(
|
||||
permission="read", target_uri="/user-groups/for-current-user"
|
||||
)
|
||||
)
|
||||
|
||||
for permission in ["create", "read", "update", "delete"]:
|
||||
permissions_to_assign.append(
|
||||
PermissionToAssign(
|
||||
permission=permission, target_uri="/process-instances/reports/*"
|
||||
)
|
||||
)
|
||||
permissions_to_assign.append(
|
||||
PermissionToAssign(permission=permission, target_uri="/tasks/*")
|
||||
)
|
||||
elif target == "ALL":
|
||||
for permission in permissions:
|
||||
permissions_to_assign.append(
|
||||
PermissionToAssign(permission=permission, target_uri="/*")
|
||||
)
|
||||
elif target.startswith("/"):
|
||||
for permission in permissions:
|
||||
permissions_to_assign.append(
|
||||
PermissionToAssign(permission=permission, target_uri=target)
|
||||
)
|
||||
else:
|
||||
raise InvalidPermissionError(
|
||||
f"Target uri '{target}' with permission set '{permission_set}' is invalid. "
|
||||
f"The target uri must either be a macro of PG, PM, BASIC, or ALL or an api uri."
|
||||
)
|
||||
|
||||
return permissions_to_assign
|
||||
|
||||
@classmethod
|
||||
def add_permission_from_uri_or_macro(
|
||||
cls, group_identifier: str, permission: str, target: str
|
||||
) -> list[PermissionAssignmentModel]:
|
||||
"""Add_permission_from_uri_or_macro."""
|
||||
group = GroupService.find_or_create_group(group_identifier)
|
||||
permissions_to_assign = cls.explode_permissions(permission, target)
|
||||
permission_assignments = []
|
||||
for permission_to_assign in permissions_to_assign:
|
||||
permission_target = cls.find_or_create_permission_target(
|
||||
permission_to_assign.target_uri
|
||||
)
|
||||
permission_assignments.append(
|
||||
cls.create_permission_for_principal(
|
||||
group.principal, permission_target, permission_to_assign.permission
|
||||
)
|
||||
)
|
||||
return permission_assignments
|
||||
|
||||
@classmethod
|
||||
def refresh_permissions(cls, group_info: list[dict[str, Any]]) -> None:
|
||||
"""Adds new permission assignments and deletes old ones."""
|
||||
initial_permission_assignments = PermissionAssignmentModel.query.all()
|
||||
result = cls.import_permissions_from_yaml_file()
|
||||
desired_permission_assignments = result["permission_assignments"]
|
||||
desired_group_identifiers = result["group_identifiers"]
|
||||
|
||||
for group in group_info:
|
||||
for username in group["users"]:
|
||||
GroupService.add_user_to_group_or_add_to_waiting(
|
||||
username, group["name"]
|
||||
)
|
||||
for permission in group["permissions"]:
|
||||
for crud_op in permission["actions"]:
|
||||
desired_permission_assignments.extend(
|
||||
cls.add_permission_from_uri_or_macro(
|
||||
group_identifier=group["name"],
|
||||
target=permission["uri"],
|
||||
permission=crud_op,
|
||||
)
|
||||
)
|
||||
desired_group_identifiers.add(group["name"])
|
||||
|
||||
for ipa in initial_permission_assignments:
|
||||
if ipa not in desired_permission_assignments:
|
||||
db.session.delete(ipa)
|
||||
|
||||
groups_to_delete = GroupModel.query.filter(
|
||||
GroupModel.identifier.not_in(desired_group_identifiers)
|
||||
).all()
|
||||
for gtd in groups_to_delete:
|
||||
db.session.delete(gtd)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
class KeycloakAuthorization:
|
||||
"""Interface with Keycloak server."""
|
||||
|
|
|
@ -4,6 +4,7 @@ from typing import Optional
|
|||
from flask_bpmn.models.db import db
|
||||
|
||||
from spiffworkflow_backend.models.group import GroupModel
|
||||
from spiffworkflow_backend.models.user import UserModel
|
||||
from spiffworkflow_backend.services.user_service import UserService
|
||||
|
||||
|
||||
|
@ -22,3 +23,15 @@ class GroupService:
|
|||
db.session.commit()
|
||||
UserService.create_principal(group.id, id_column_name="group_id")
|
||||
return group
|
||||
|
||||
@classmethod
|
||||
def add_user_to_group_or_add_to_waiting(
|
||||
cls, username: str, group_identifier: str
|
||||
) -> None:
|
||||
"""Add_user_to_group_or_add_to_waiting."""
|
||||
group = cls.find_or_create_group(group_identifier)
|
||||
user = UserModel.query.filter_by(username=username).first()
|
||||
if user:
|
||||
UserService.add_user_to_group(user, group)
|
||||
else:
|
||||
UserService.add_waiting_group_assignment(username, group)
|
||||
|
|
|
@ -151,6 +151,7 @@ class CustomBpmnScriptEngine(PythonScriptEngine): # type: ignore
|
|||
"time": time,
|
||||
"decimal": decimal,
|
||||
"_strptime": _strptime,
|
||||
"enumerate": enumerate,
|
||||
}
|
||||
|
||||
# This will overwrite the standard builtins
|
||||
|
|
|
@ -414,13 +414,16 @@ class ProcessInstanceReportService:
|
|||
)
|
||||
|
||||
if report_filter.with_tasks_assigned_to_my_group is True:
|
||||
group_model_join_conditions = [GroupModel.id == HumanTaskModel.lane_assignment_id]
|
||||
group_model_join_conditions = [
|
||||
GroupModel.id == HumanTaskModel.lane_assignment_id
|
||||
]
|
||||
if report_filter.user_group_identifier:
|
||||
group_model_join_conditions.append(GroupModel.identifier == report_filter.user_group_identifier)
|
||||
group_model_join_conditions.append(
|
||||
GroupModel.identifier == report_filter.user_group_identifier
|
||||
)
|
||||
process_instance_query = process_instance_query.join(HumanTaskModel)
|
||||
process_instance_query = process_instance_query.join(
|
||||
GroupModel,
|
||||
and_(*group_model_join_conditions)
|
||||
GroupModel, and_(*group_model_join_conditions)
|
||||
)
|
||||
process_instance_query = process_instance_query.join(
|
||||
UserGroupAssignmentModel,
|
||||
|
|
|
@ -17,7 +17,8 @@ from spiffworkflow_backend.models.task import MultiInstanceType
|
|||
from spiffworkflow_backend.models.task import Task
|
||||
from spiffworkflow_backend.models.user import UserModel
|
||||
from spiffworkflow_backend.services.authorization_service import AuthorizationService
|
||||
from spiffworkflow_backend.services.git_service import GitService, GitCommandError
|
||||
from spiffworkflow_backend.services.git_service import GitCommandError
|
||||
from spiffworkflow_backend.services.git_service import GitService
|
||||
from spiffworkflow_backend.services.process_instance_processor import (
|
||||
ProcessInstanceProcessor,
|
||||
)
|
||||
|
@ -38,7 +39,7 @@ class ProcessInstanceService:
|
|||
"""Get_process_instance_from_spec."""
|
||||
try:
|
||||
current_git_revision = GitService.get_current_revision()
|
||||
except GitCommandError as ge:
|
||||
except GitCommandError:
|
||||
current_git_revision = ""
|
||||
process_instance_model = ProcessInstanceModel(
|
||||
status=ProcessInstanceStatus.not_started.value,
|
||||
|
|
|
@ -224,10 +224,10 @@ class ProcessModelService(FileSystemService):
|
|||
new_process_model_list = []
|
||||
for process_model in process_models:
|
||||
uri = f"/v1.0/process-instances/{process_model.id.replace('/', ':')}"
|
||||
result = AuthorizationService.user_has_permission(
|
||||
has_permission = AuthorizationService.user_has_permission(
|
||||
user=user, permission="create", target_uri=uri
|
||||
)
|
||||
if result:
|
||||
if has_permission:
|
||||
new_process_model_list.append(process_model)
|
||||
return new_process_model_list
|
||||
|
||||
|
|
|
@ -13,6 +13,9 @@ from spiffworkflow_backend.models.human_task_user import HumanTaskUserModel
|
|||
from spiffworkflow_backend.models.principal import PrincipalModel
|
||||
from spiffworkflow_backend.models.user import UserModel
|
||||
from spiffworkflow_backend.models.user_group_assignment import UserGroupAssignmentModel
|
||||
from spiffworkflow_backend.models.user_group_assignment_waiting import (
|
||||
UserGroupAssignmentWaitingModel,
|
||||
)
|
||||
|
||||
|
||||
class UserService:
|
||||
|
@ -21,11 +24,11 @@ class UserService:
|
|||
@classmethod
|
||||
def create_user(
|
||||
cls,
|
||||
username: str,
|
||||
service: str,
|
||||
service_id: str,
|
||||
name: Optional[str] = "",
|
||||
username: Optional[str] = "",
|
||||
email: Optional[str] = "",
|
||||
display_name: Optional[str] = "",
|
||||
) -> UserModel:
|
||||
"""Create_user."""
|
||||
user_model: Optional[UserModel] = (
|
||||
|
@ -41,8 +44,8 @@ class UserService:
|
|||
username=username,
|
||||
service=service,
|
||||
service_id=service_id,
|
||||
name=name,
|
||||
email=email,
|
||||
display_name=display_name,
|
||||
)
|
||||
db.session.add(user_model)
|
||||
|
||||
|
@ -55,6 +58,7 @@ class UserService:
|
|||
message=f"Could not add user {username}",
|
||||
) from e
|
||||
cls.create_principal(user_model.id)
|
||||
UserService().apply_waiting_group_assignments(user_model)
|
||||
return user_model
|
||||
|
||||
else:
|
||||
|
@ -69,45 +73,12 @@ class UserService:
|
|||
)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def find_or_create_user(
|
||||
cls,
|
||||
service: str,
|
||||
service_id: str,
|
||||
name: Optional[str] = None,
|
||||
username: Optional[str] = None,
|
||||
email: Optional[str] = None,
|
||||
) -> UserModel:
|
||||
"""Find_or_create_user."""
|
||||
user_model: UserModel
|
||||
try:
|
||||
user_model = cls.create_user(
|
||||
service=service,
|
||||
service_id=service_id,
|
||||
name=name,
|
||||
username=username,
|
||||
email=email,
|
||||
)
|
||||
except ApiError:
|
||||
user_model = (
|
||||
UserModel.query.filter(UserModel.service == service)
|
||||
.filter(UserModel.service_id == service_id)
|
||||
.first()
|
||||
)
|
||||
return user_model
|
||||
|
||||
# Returns true if the current user is logged in.
|
||||
@staticmethod
|
||||
def has_user() -> bool:
|
||||
"""Has_user."""
|
||||
return "token" in g and bool(g.token) and "user" in g and bool(g.user)
|
||||
|
||||
# Returns true if the given user uid is different from the current user's uid.
|
||||
@staticmethod
|
||||
def is_different_user(uid: str) -> bool:
|
||||
"""Is_different_user."""
|
||||
return UserService.has_user() and uid is not None and uid is not g.user.uid
|
||||
|
||||
@staticmethod
|
||||
def current_user() -> Any:
|
||||
"""Current_user."""
|
||||
|
@ -117,20 +88,6 @@ class UserService:
|
|||
)
|
||||
return g.user
|
||||
|
||||
@staticmethod
|
||||
def in_list(uids: list[str]) -> bool:
|
||||
"""Returns true if the current user's id is in the given list of ids.
|
||||
|
||||
False if there is no user, or the user is not in the list.
|
||||
"""
|
||||
if (
|
||||
UserService.has_user()
|
||||
): # If someone is logged in, lock tasks that don't belong to them.
|
||||
user = UserService.current_user()
|
||||
if user.uid in uids:
|
||||
return True
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def get_principal_by_user_id(user_id: int) -> PrincipalModel:
|
||||
"""Get_principal_by_user_id."""
|
||||
|
@ -173,8 +130,57 @@ class UserService:
|
|||
@classmethod
|
||||
def add_user_to_group(cls, user: UserModel, group: GroupModel) -> None:
|
||||
"""Add_user_to_group."""
|
||||
ugam = UserGroupAssignmentModel(user_id=user.id, group_id=group.id)
|
||||
db.session.add(ugam)
|
||||
exists = (
|
||||
UserGroupAssignmentModel()
|
||||
.query.filter_by(user_id=user.id)
|
||||
.filter_by(group_id=group.id)
|
||||
.count()
|
||||
)
|
||||
if not exists:
|
||||
ugam = UserGroupAssignmentModel(user_id=user.id, group_id=group.id)
|
||||
db.session.add(ugam)
|
||||
db.session.commit()
|
||||
|
||||
@classmethod
|
||||
def add_waiting_group_assignment(cls, username: str, group: GroupModel) -> None:
|
||||
"""Add_waiting_group_assignment."""
|
||||
wugam = (
|
||||
UserGroupAssignmentWaitingModel()
|
||||
.query.filter_by(username=username)
|
||||
.filter_by(group_id=group.id)
|
||||
.first()
|
||||
)
|
||||
if not wugam:
|
||||
wugam = UserGroupAssignmentWaitingModel(
|
||||
username=username, group_id=group.id
|
||||
)
|
||||
db.session.add(wugam)
|
||||
db.session.commit()
|
||||
if wugam.is_match_all():
|
||||
for user in UserModel.query.all():
|
||||
cls.add_user_to_group(user, group)
|
||||
|
||||
@classmethod
|
||||
def apply_waiting_group_assignments(cls, user: UserModel) -> None:
|
||||
"""Apply_waiting_group_assignments."""
|
||||
waiting = (
|
||||
UserGroupAssignmentWaitingModel()
|
||||
.query.filter(UserGroupAssignmentWaitingModel.username == user.username)
|
||||
.all()
|
||||
)
|
||||
for assignment in waiting:
|
||||
cls.add_user_to_group(user, assignment.group)
|
||||
db.session.delete(assignment)
|
||||
wildcard = (
|
||||
UserGroupAssignmentWaitingModel()
|
||||
.query.filter(
|
||||
UserGroupAssignmentWaitingModel.username
|
||||
== UserGroupAssignmentWaitingModel.MATCH_ALL_USERS
|
||||
)
|
||||
.all()
|
||||
)
|
||||
for assignment in wildcard:
|
||||
cls.add_user_to_group(user, assignment.group)
|
||||
db.session.commit()
|
||||
|
||||
@staticmethod
|
||||
|
|
|
@ -41,7 +41,7 @@ class BaseTest:
|
|||
if isinstance(user, UserModel):
|
||||
return user
|
||||
|
||||
user = UserService.create_user("internal", username, username=username)
|
||||
user = UserService.create_user(username, "internal", username)
|
||||
if isinstance(user, UserModel):
|
||||
return user
|
||||
|
||||
|
@ -324,13 +324,9 @@ class BaseTest:
|
|||
permission_names: Optional[list[str]] = None,
|
||||
) -> UserModel:
|
||||
"""Add_permissions_to_user."""
|
||||
permission_target = PermissionTargetModel.query.filter_by(
|
||||
uri=target_uri
|
||||
).first()
|
||||
if permission_target is None:
|
||||
permission_target = PermissionTargetModel(uri=target_uri)
|
||||
db.session.add(permission_target)
|
||||
db.session.commit()
|
||||
permission_target = AuthorizationService.find_or_create_permission_target(
|
||||
target_uri
|
||||
)
|
||||
|
||||
if permission_names is None:
|
||||
permission_names = [member.name for member in Permission]
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
"""Test_authentication."""
|
||||
import base64
|
||||
|
||||
import jwt
|
||||
from flask import Flask
|
||||
from flask.testing import FlaskClient
|
||||
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
|
||||
|
@ -44,13 +47,16 @@ class TestFlaskOpenId(BaseTest):
|
|||
client: FlaskClient,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
) -> None:
|
||||
"""Test_get_token."""
|
||||
code = "testadmin1:1234123412341234"
|
||||
|
||||
"""It should be possible to get a token."""
|
||||
code = (
|
||||
"c3BpZmZ3b3JrZmxvdy1iYWNrZW5kOkpYZVFFeG0wSmhRUEx1bWdIdElJcWY1MmJEYWxIejBx"
|
||||
)
|
||||
backend_basic_auth_string = code
|
||||
backend_basic_auth_bytes = bytes(backend_basic_auth_string, encoding="ascii")
|
||||
backend_basic_auth = base64.b64encode(backend_basic_auth_bytes)
|
||||
headers = {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
"Authorization": f"Basic {code}",
|
||||
"Authorization": f"Basic {backend_basic_auth.decode('utf-8')}",
|
||||
}
|
||||
data = {
|
||||
"grant_type": "authorization_code",
|
||||
|
@ -59,3 +65,13 @@ class TestFlaskOpenId(BaseTest):
|
|||
}
|
||||
response = client.post("/openid/token", data=data, headers=headers)
|
||||
assert response
|
||||
assert response.is_json
|
||||
assert "access_token" in response.json
|
||||
assert "id_token" in response.json
|
||||
assert "refresh_token" in response.json
|
||||
|
||||
decoded_token = jwt.decode(
|
||||
response.json["id_token"], options={"verify_signature": False}
|
||||
)
|
||||
assert "iss" in decoded_token
|
||||
assert "email" in decoded_token
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
"""Test_get_localtime."""
|
||||
from flask.app import Flask
|
||||
from flask.testing import FlaskClient
|
||||
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
|
||||
|
||||
from spiffworkflow_backend.models.script_attributes_context import (
|
||||
ScriptAttributesContext,
|
||||
)
|
||||
from spiffworkflow_backend.models.user import UserModel
|
||||
from spiffworkflow_backend.scripts.get_all_permissions import GetAllPermissions
|
||||
from spiffworkflow_backend.services.authorization_service import AuthorizationService
|
||||
|
||||
|
||||
class TestGetAllPermissions(BaseTest):
|
||||
"""TestGetAllPermissions."""
|
||||
|
||||
def test_can_get_all_permissions(
|
||||
self,
|
||||
app: Flask,
|
||||
client: FlaskClient,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
with_super_admin_user: UserModel,
|
||||
) -> None:
|
||||
"""Test_can_get_all_permissions."""
|
||||
self.find_or_create_user("test_user")
|
||||
|
||||
# now that we have everything, try to clear it out...
|
||||
script_attributes_context = ScriptAttributesContext(
|
||||
task=None,
|
||||
environment_identifier="testing",
|
||||
process_instance_id=1,
|
||||
process_model_identifier="my_test_user",
|
||||
)
|
||||
AuthorizationService.add_permission_from_uri_or_macro(
|
||||
permission="start", target="PG:hey:group", group_identifier="my_test_group"
|
||||
)
|
||||
AuthorizationService.add_permission_from_uri_or_macro(
|
||||
permission="all", target="/tasks", group_identifier="my_test_group"
|
||||
)
|
||||
|
||||
expected_permissions = [
|
||||
{
|
||||
"group_identifier": "my_test_group",
|
||||
"uri": "/process-instances/hey:group:%",
|
||||
"permissions": ["create"],
|
||||
},
|
||||
{
|
||||
"group_identifier": "my_test_group",
|
||||
"uri": "/process-instances/for-me/hey:group:%",
|
||||
"permissions": ["read"],
|
||||
},
|
||||
{
|
||||
"group_identifier": "my_test_group",
|
||||
"uri": "/tasks",
|
||||
"permissions": ["create", "delete", "read", "update"],
|
||||
},
|
||||
]
|
||||
|
||||
permissions = GetAllPermissions().run(script_attributes_context)
|
||||
assert permissions == expected_permissions
|
|
@ -24,7 +24,6 @@ class TestSaveProcessInstanceMetadata(BaseTest):
|
|||
with_super_admin_user: UserModel,
|
||||
) -> None:
|
||||
"""Test_can_save_process_instance_metadata."""
|
||||
initiator_user = self.find_or_create_user("initiator_user")
|
||||
self.create_process_group(
|
||||
client, with_super_admin_user, "test_group", "test_group"
|
||||
)
|
||||
|
@ -34,7 +33,7 @@ class TestSaveProcessInstanceMetadata(BaseTest):
|
|||
process_model_source_directory="save_process_instance_metadata",
|
||||
)
|
||||
process_instance = self.create_process_instance_from_process_model(
|
||||
process_model=process_model, user=initiator_user
|
||||
process_model=process_model, user=with_super_admin_user
|
||||
)
|
||||
processor = ProcessInstanceProcessor(process_instance)
|
||||
processor.do_engine_steps(save=True)
|
||||
|
|
|
@ -4,9 +4,12 @@ from flask import Flask
|
|||
from flask.testing import FlaskClient
|
||||
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
|
||||
|
||||
from spiffworkflow_backend.models.group import GroupModel
|
||||
from spiffworkflow_backend.models.user import UserModel
|
||||
from spiffworkflow_backend.models.user import UserNotFoundError
|
||||
from spiffworkflow_backend.services.authorization_service import AuthorizationService
|
||||
from spiffworkflow_backend.services.authorization_service import InvalidPermissionError
|
||||
from spiffworkflow_backend.services.group_service import GroupService
|
||||
from spiffworkflow_backend.services.process_instance_processor import (
|
||||
ProcessInstanceProcessor,
|
||||
)
|
||||
|
@ -14,6 +17,7 @@ from spiffworkflow_backend.services.process_instance_service import (
|
|||
ProcessInstanceService,
|
||||
)
|
||||
from spiffworkflow_backend.services.process_model_service import ProcessModelService
|
||||
from spiffworkflow_backend.services.user_service import UserService
|
||||
|
||||
|
||||
class TestAuthorizationService(BaseTest):
|
||||
|
@ -134,8 +138,339 @@ class TestAuthorizationService(BaseTest):
|
|||
human_task.task_name, processor.bpmn_process_instance
|
||||
)
|
||||
finance_user = AuthorizationService.create_user_from_sign_in(
|
||||
{"username": "testuser2", "sub": "open_id"}
|
||||
{
|
||||
"username": "testuser2",
|
||||
"sub": "testuser2",
|
||||
"iss": "https://test.stuff",
|
||||
"email": "testuser2",
|
||||
}
|
||||
)
|
||||
ProcessInstanceService.complete_form_task(
|
||||
processor, spiff_task, {}, finance_user, human_task
|
||||
)
|
||||
|
||||
def test_explode_permissions_all_on_process_group(
|
||||
self,
|
||||
app: Flask,
|
||||
client: FlaskClient,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
) -> None:
|
||||
"""Test_explode_permissions_all_on_process_group."""
|
||||
expected_permissions = [
|
||||
("/logs/some-process-group:some-process-model:*", "create"),
|
||||
("/logs/some-process-group:some-process-model:*", "delete"),
|
||||
("/logs/some-process-group:some-process-model:*", "read"),
|
||||
("/logs/some-process-group:some-process-model:*", "update"),
|
||||
("/process-groups/some-process-group:some-process-model:*", "create"),
|
||||
("/process-groups/some-process-group:some-process-model:*", "delete"),
|
||||
("/process-groups/some-process-group:some-process-model:*", "read"),
|
||||
("/process-groups/some-process-group:some-process-model:*", "update"),
|
||||
(
|
||||
"/process-instance-suspend/some-process-group:some-process-model:*",
|
||||
"create",
|
||||
),
|
||||
(
|
||||
"/process-instance-suspend/some-process-group:some-process-model:*",
|
||||
"delete",
|
||||
),
|
||||
(
|
||||
"/process-instance-suspend/some-process-group:some-process-model:*",
|
||||
"read",
|
||||
),
|
||||
(
|
||||
"/process-instance-suspend/some-process-group:some-process-model:*",
|
||||
"update",
|
||||
),
|
||||
(
|
||||
"/process-instance-terminate/some-process-group:some-process-model:*",
|
||||
"create",
|
||||
),
|
||||
(
|
||||
"/process-instance-terminate/some-process-group:some-process-model:*",
|
||||
"delete",
|
||||
),
|
||||
(
|
||||
"/process-instance-terminate/some-process-group:some-process-model:*",
|
||||
"read",
|
||||
),
|
||||
(
|
||||
"/process-instance-terminate/some-process-group:some-process-model:*",
|
||||
"update",
|
||||
),
|
||||
("/process-instances/some-process-group:some-process-model:*", "create"),
|
||||
("/process-instances/some-process-group:some-process-model:*", "delete"),
|
||||
("/process-instances/some-process-group:some-process-model:*", "read"),
|
||||
("/process-instances/some-process-group:some-process-model:*", "update"),
|
||||
("/process-models/some-process-group:some-process-model:*", "create"),
|
||||
("/process-models/some-process-group:some-process-model:*", "delete"),
|
||||
("/process-models/some-process-group:some-process-model:*", "read"),
|
||||
("/process-models/some-process-group:some-process-model:*", "update"),
|
||||
("/task-data/some-process-group:some-process-model:*", "create"),
|
||||
("/task-data/some-process-group:some-process-model:*", "delete"),
|
||||
("/task-data/some-process-group:some-process-model:*", "read"),
|
||||
("/task-data/some-process-group:some-process-model:*", "update"),
|
||||
]
|
||||
permissions_to_assign = AuthorizationService.explode_permissions(
|
||||
"all", "PG:/some-process-group/some-process-model"
|
||||
)
|
||||
permissions_to_assign_tuples = sorted(
|
||||
[(p.target_uri, p.permission) for p in permissions_to_assign]
|
||||
)
|
||||
assert permissions_to_assign_tuples == expected_permissions
|
||||
|
||||
def test_explode_permissions_start_on_process_group(
|
||||
self,
|
||||
app: Flask,
|
||||
client: FlaskClient,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
) -> None:
|
||||
"""Test_explode_permissions_start_on_process_group."""
|
||||
expected_permissions = [
|
||||
(
|
||||
"/process-instances/for-me/some-process-group:some-process-model:*",
|
||||
"read",
|
||||
),
|
||||
("/process-instances/some-process-group:some-process-model:*", "create"),
|
||||
]
|
||||
permissions_to_assign = AuthorizationService.explode_permissions(
|
||||
"start", "PG:/some-process-group/some-process-model"
|
||||
)
|
||||
permissions_to_assign_tuples = sorted(
|
||||
[(p.target_uri, p.permission) for p in permissions_to_assign]
|
||||
)
|
||||
assert permissions_to_assign_tuples == expected_permissions
|
||||
|
||||
def test_explode_permissions_all_on_process_model(
|
||||
self,
|
||||
app: Flask,
|
||||
client: FlaskClient,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
) -> None:
|
||||
"""Test_explode_permissions_all_on_process_model."""
|
||||
expected_permissions = [
|
||||
("/logs/some-process-group:some-process-model/*", "create"),
|
||||
("/logs/some-process-group:some-process-model/*", "delete"),
|
||||
("/logs/some-process-group:some-process-model/*", "read"),
|
||||
("/logs/some-process-group:some-process-model/*", "update"),
|
||||
(
|
||||
"/process-instance-suspend/some-process-group:some-process-model/*",
|
||||
"create",
|
||||
),
|
||||
(
|
||||
"/process-instance-suspend/some-process-group:some-process-model/*",
|
||||
"delete",
|
||||
),
|
||||
(
|
||||
"/process-instance-suspend/some-process-group:some-process-model/*",
|
||||
"read",
|
||||
),
|
||||
(
|
||||
"/process-instance-suspend/some-process-group:some-process-model/*",
|
||||
"update",
|
||||
),
|
||||
(
|
||||
"/process-instance-terminate/some-process-group:some-process-model/*",
|
||||
"create",
|
||||
),
|
||||
(
|
||||
"/process-instance-terminate/some-process-group:some-process-model/*",
|
||||
"delete",
|
||||
),
|
||||
(
|
||||
"/process-instance-terminate/some-process-group:some-process-model/*",
|
||||
"read",
|
||||
),
|
||||
(
|
||||
"/process-instance-terminate/some-process-group:some-process-model/*",
|
||||
"update",
|
||||
),
|
||||
("/process-instances/some-process-group:some-process-model/*", "create"),
|
||||
("/process-instances/some-process-group:some-process-model/*", "delete"),
|
||||
("/process-instances/some-process-group:some-process-model/*", "read"),
|
||||
("/process-instances/some-process-group:some-process-model/*", "update"),
|
||||
("/process-models/some-process-group:some-process-model/*", "create"),
|
||||
("/process-models/some-process-group:some-process-model/*", "delete"),
|
||||
("/process-models/some-process-group:some-process-model/*", "read"),
|
||||
("/process-models/some-process-group:some-process-model/*", "update"),
|
||||
("/task-data/some-process-group:some-process-model/*", "create"),
|
||||
("/task-data/some-process-group:some-process-model/*", "delete"),
|
||||
("/task-data/some-process-group:some-process-model/*", "read"),
|
||||
("/task-data/some-process-group:some-process-model/*", "update"),
|
||||
]
|
||||
permissions_to_assign = AuthorizationService.explode_permissions(
|
||||
"all", "PM:/some-process-group/some-process-model"
|
||||
)
|
||||
permissions_to_assign_tuples = sorted(
|
||||
[(p.target_uri, p.permission) for p in permissions_to_assign]
|
||||
)
|
||||
assert permissions_to_assign_tuples == expected_permissions
|
||||
|
||||
def test_explode_permissions_start_on_process_model(
|
||||
self,
|
||||
app: Flask,
|
||||
client: FlaskClient,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
) -> None:
|
||||
"""Test_explode_permissions_start_on_process_model."""
|
||||
expected_permissions = [
|
||||
(
|
||||
"/process-instances/for-me/some-process-group:some-process-model/*",
|
||||
"read",
|
||||
),
|
||||
("/process-instances/some-process-group:some-process-model/*", "create"),
|
||||
]
|
||||
permissions_to_assign = AuthorizationService.explode_permissions(
|
||||
"start", "PM:/some-process-group/some-process-model"
|
||||
)
|
||||
permissions_to_assign_tuples = sorted(
|
||||
[(p.target_uri, p.permission) for p in permissions_to_assign]
|
||||
)
|
||||
assert permissions_to_assign_tuples == expected_permissions
|
||||
|
||||
def test_explode_permissions_basic(
|
||||
self,
|
||||
app: Flask,
|
||||
client: FlaskClient,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
) -> None:
|
||||
"""Test_explode_permissions_basic."""
|
||||
expected_permissions = [
|
||||
("/process-instances/for-me", "read"),
|
||||
("/process-instances/reports/*", "create"),
|
||||
("/process-instances/reports/*", "delete"),
|
||||
("/process-instances/reports/*", "read"),
|
||||
("/process-instances/reports/*", "update"),
|
||||
("/processes", "read"),
|
||||
("/service-tasks", "read"),
|
||||
("/tasks/*", "create"),
|
||||
("/tasks/*", "delete"),
|
||||
("/tasks/*", "read"),
|
||||
("/tasks/*", "update"),
|
||||
("/user-groups/for-current-user", "read"),
|
||||
]
|
||||
permissions_to_assign = AuthorizationService.explode_permissions("all", "BASIC")
|
||||
permissions_to_assign_tuples = sorted(
|
||||
[(p.target_uri, p.permission) for p in permissions_to_assign]
|
||||
)
|
||||
assert permissions_to_assign_tuples == expected_permissions
|
||||
|
||||
def test_explode_permissions_all(
|
||||
self,
|
||||
app: Flask,
|
||||
client: FlaskClient,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
) -> None:
|
||||
"""Test_explode_permissions_all."""
|
||||
expected_permissions = [
|
||||
("/*", "create"),
|
||||
("/*", "delete"),
|
||||
("/*", "read"),
|
||||
("/*", "update"),
|
||||
]
|
||||
permissions_to_assign = AuthorizationService.explode_permissions("all", "ALL")
|
||||
permissions_to_assign_tuples = sorted(
|
||||
[(p.target_uri, p.permission) for p in permissions_to_assign]
|
||||
)
|
||||
assert permissions_to_assign_tuples == expected_permissions
|
||||
|
||||
def test_explode_permissions_with_target_uri(
|
||||
self,
|
||||
app: Flask,
|
||||
client: FlaskClient,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
) -> None:
|
||||
"""Test_explode_permissions_with_target_uri."""
|
||||
expected_permissions = [
|
||||
("/hey/model", "create"),
|
||||
("/hey/model", "delete"),
|
||||
("/hey/model", "read"),
|
||||
("/hey/model", "update"),
|
||||
]
|
||||
permissions_to_assign = AuthorizationService.explode_permissions(
|
||||
"all", "/hey/model"
|
||||
)
|
||||
permissions_to_assign_tuples = sorted(
|
||||
[(p.target_uri, p.permission) for p in permissions_to_assign]
|
||||
)
|
||||
assert permissions_to_assign_tuples == expected_permissions
|
||||
|
||||
def test_granting_access_to_group_gives_access_to_group_and_subgroups(
|
||||
self,
|
||||
app: Flask,
|
||||
client: FlaskClient,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
) -> None:
|
||||
"""Test_granting_access_to_group_gives_access_to_group_and_subgroups."""
|
||||
user = self.find_or_create_user(username="user_one")
|
||||
user_group = GroupService.find_or_create_group("group_one")
|
||||
UserService.add_user_to_group(user, user_group)
|
||||
AuthorizationService.add_permission_from_uri_or_macro(
|
||||
user_group.identifier, "read", "PG:hey"
|
||||
)
|
||||
self.assert_user_has_permission(user, "read", "/v1.0/process-groups/hey")
|
||||
self.assert_user_has_permission(user, "read", "/v1.0/process-groups/hey:yo")
|
||||
|
||||
def test_explode_permissions_with_invalid_target_uri(
|
||||
self,
|
||||
app: Flask,
|
||||
client: FlaskClient,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
) -> None:
|
||||
"""Test_explode_permissions_with_invalid_target_uri."""
|
||||
with pytest.raises(InvalidPermissionError):
|
||||
AuthorizationService.explode_permissions("all", "BAD_MACRO")
|
||||
|
||||
def test_explode_permissions_with_start_to_incorrect_target(
|
||||
self,
|
||||
app: Flask,
|
||||
client: FlaskClient,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
) -> None:
|
||||
"""Test_explode_permissions_with_start_to_incorrect_target."""
|
||||
with pytest.raises(InvalidPermissionError):
|
||||
AuthorizationService.explode_permissions("start", "/hey/model")
|
||||
|
||||
def test_can_refresh_permissions(
|
||||
self,
|
||||
app: Flask,
|
||||
client: FlaskClient,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
) -> None:
|
||||
"""Test_can_refresh_permissions."""
|
||||
user = self.find_or_create_user(username="user_one")
|
||||
admin_user = self.find_or_create_user(username="testadmin1")
|
||||
|
||||
# this group is not mentioned so it will get deleted
|
||||
GroupService.find_or_create_group("group_two")
|
||||
assert GroupModel.query.filter_by(identifier="group_two").first() is not None
|
||||
|
||||
group_info = [
|
||||
{
|
||||
"users": ["user_one"],
|
||||
"name": "group_one",
|
||||
"permissions": [{"actions": ["create", "read"], "uri": "PG:hey"}],
|
||||
}
|
||||
]
|
||||
AuthorizationService.refresh_permissions(group_info)
|
||||
assert GroupModel.query.filter_by(identifier="group_two").first() is None
|
||||
assert GroupModel.query.filter_by(identifier="group_one").first() is not None
|
||||
self.assert_user_has_permission(admin_user, "create", "/anything-they-want")
|
||||
self.assert_user_has_permission(user, "read", "/v1.0/process-groups/hey")
|
||||
self.assert_user_has_permission(user, "read", "/v1.0/process-groups/hey:yo")
|
||||
self.assert_user_has_permission(user, "create", "/v1.0/process-groups/hey:yo")
|
||||
|
||||
group_info = [
|
||||
{
|
||||
"users": ["user_one"],
|
||||
"name": "group_one",
|
||||
"permissions": [{"actions": ["read"], "uri": "PG:hey"}],
|
||||
}
|
||||
]
|
||||
AuthorizationService.refresh_permissions(group_info)
|
||||
assert GroupModel.query.filter_by(identifier="group_one").first() is not None
|
||||
self.assert_user_has_permission(user, "read", "/v1.0/process-groups/hey")
|
||||
self.assert_user_has_permission(user, "read", "/v1.0/process-groups/hey:yo")
|
||||
self.assert_user_has_permission(
|
||||
user, "create", "/v1.0/process-groups/hey:yo", expected_result=False
|
||||
)
|
||||
self.assert_user_has_permission(admin_user, "create", "/anything-they-want")
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
"""Process Model."""
|
||||
from flask.app import Flask
|
||||
from flask.testing import FlaskClient
|
||||
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
|
||||
|
||||
from spiffworkflow_backend.models.user_group_assignment_waiting import (
|
||||
UserGroupAssignmentWaitingModel,
|
||||
)
|
||||
from spiffworkflow_backend.services.group_service import GroupService
|
||||
from spiffworkflow_backend.services.user_service import UserService
|
||||
|
||||
|
||||
class TestUserService(BaseTest):
|
||||
"""TestUserService."""
|
||||
|
||||
def test_assigning_a_group_to_a_user_before_the_user_is_created(
|
||||
self,
|
||||
app: Flask,
|
||||
client: FlaskClient,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
) -> None:
|
||||
"""Test_waiting_group_assignments."""
|
||||
a_test_group = GroupService.find_or_create_group("aTestGroup")
|
||||
UserService.add_waiting_group_assignment("initiator_user", a_test_group)
|
||||
initiator_user = self.find_or_create_user("initiator_user")
|
||||
assert initiator_user.groups[0] == a_test_group
|
||||
|
||||
def test_assigning_a_group_to_all_users_updates_new_users(
|
||||
self,
|
||||
app: Flask,
|
||||
client: FlaskClient,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
) -> None:
|
||||
"""Test_waiting_group_assignments."""
|
||||
everybody_group = GroupService.find_or_create_group("everybodyGroup")
|
||||
UserService.add_waiting_group_assignment(
|
||||
UserGroupAssignmentWaitingModel.MATCH_ALL_USERS, everybody_group
|
||||
)
|
||||
initiator_user = self.find_or_create_user("initiator_user")
|
||||
assert initiator_user.groups[0] == everybody_group
|
||||
|
||||
def test_assigning_a_group_to_all_users_updates_existing_users(
|
||||
self,
|
||||
app: Flask,
|
||||
client: FlaskClient,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
) -> None:
|
||||
"""Test_waiting_group_assignments."""
|
||||
initiator_user = self.find_or_create_user("initiator_user")
|
||||
everybody_group = GroupService.find_or_create_group("everybodyGroup")
|
||||
UserService.add_waiting_group_assignment(
|
||||
UserGroupAssignmentWaitingModel.MATCH_ALL_USERS, everybody_group
|
||||
)
|
||||
assert initiator_user.groups[0] == everybody_group
|
|
@ -56,6 +56,8 @@ export default function ProcessModelEditDiagram() {
|
|||
const [processSearchEventBus, setProcessSearchEventBus] = useState<any>(null);
|
||||
const [processSearchElement, setProcessSearchElement] = useState<any>(null);
|
||||
const [processes, setProcesses] = useState<ProcessReference[]>([]);
|
||||
const [displaySaveFileMessage, setDisplaySaveFileMessage] =
|
||||
useState<boolean>(false);
|
||||
|
||||
const handleShowMarkdownEditor = () => setShowMarkdownEditor(true);
|
||||
|
||||
|
@ -79,10 +81,10 @@ export default function ProcessModelEditDiagram() {
|
|||
|
||||
interface ScriptUnitTestResult {
|
||||
result: boolean;
|
||||
context: object;
|
||||
error: string;
|
||||
line_number: number;
|
||||
offset: number;
|
||||
context?: object;
|
||||
error?: string;
|
||||
line_number?: number;
|
||||
offset?: number;
|
||||
}
|
||||
|
||||
const [currentScriptUnitTest, setCurrentScriptUnitTest] =
|
||||
|
@ -157,6 +159,7 @@ export default function ProcessModelEditDiagram() {
|
|||
};
|
||||
|
||||
const navigateToProcessModelFile = (_result: any) => {
|
||||
setDisplaySaveFileMessage(true);
|
||||
if (!params.file_name) {
|
||||
const fileNameWithExtension = `${newFileName}.${searchParams.get(
|
||||
'file_type'
|
||||
|
@ -167,9 +170,8 @@ export default function ProcessModelEditDiagram() {
|
|||
}
|
||||
};
|
||||
|
||||
const [displaySaveFileMessage, setDisplaySaveFileMessage] =
|
||||
useState<boolean>(false);
|
||||
const saveDiagram = (bpmnXML: any, fileName = params.file_name) => {
|
||||
setDisplaySaveFileMessage(false);
|
||||
setErrorMessage(null);
|
||||
setBpmnXmlForDiagramRendering(bpmnXML);
|
||||
|
||||
|
@ -204,7 +206,6 @@ export default function ProcessModelEditDiagram() {
|
|||
// after saving the file, make sure we null out newFileName
|
||||
// so it does not get used over the params
|
||||
setNewFileName('');
|
||||
setDisplaySaveFileMessage(true);
|
||||
};
|
||||
|
||||
const onDeleteFile = (fileName = params.file_name) => {
|
||||
|
@ -477,6 +478,21 @@ export default function ProcessModelEditDiagram() {
|
|||
|
||||
const runCurrentUnitTest = () => {
|
||||
if (currentScriptUnitTest && scriptElement) {
|
||||
let inputJson = '';
|
||||
let expectedJson = '';
|
||||
try {
|
||||
inputJson = JSON.parse(currentScriptUnitTest.inputJson.value);
|
||||
expectedJson = JSON.parse(
|
||||
currentScriptUnitTest.expectedOutputJson.value
|
||||
);
|
||||
} catch (e) {
|
||||
setScriptUnitTestResult({
|
||||
result: false,
|
||||
error: 'The JSON provided contains a formatting error.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
resetUnitTextResult();
|
||||
HttpService.makeCallToBackend({
|
||||
path: `/process-models/${modifiedProcessModelId}/script-unit-tests/run`,
|
||||
|
@ -485,31 +501,29 @@ export default function ProcessModelEditDiagram() {
|
|||
postBody: {
|
||||
bpmn_task_identifier: (scriptElement as any).id,
|
||||
python_script: scriptText,
|
||||
input_json: JSON.parse(currentScriptUnitTest.inputJson.value),
|
||||
expected_output_json: JSON.parse(
|
||||
currentScriptUnitTest.expectedOutputJson.value
|
||||
),
|
||||
input_json: inputJson,
|
||||
expected_output_json: expectedJson,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const unitTestFailureElement = () => {
|
||||
if (
|
||||
scriptUnitTestResult &&
|
||||
scriptUnitTestResult.result === false &&
|
||||
!scriptUnitTestResult.line_number
|
||||
) {
|
||||
let errorStringElement = null;
|
||||
if (scriptUnitTestResult.error) {
|
||||
errorStringElement = (
|
||||
<span>
|
||||
Received error when running script:{' '}
|
||||
{JSON.stringify(scriptUnitTestResult.error)}
|
||||
</span>
|
||||
);
|
||||
if (scriptUnitTestResult && scriptUnitTestResult.result === false) {
|
||||
let errorMessage = '';
|
||||
if (scriptUnitTestResult.context) {
|
||||
errorMessage = 'Unexpected result. Please see the comparison below.';
|
||||
} else if (scriptUnitTestResult.line_number) {
|
||||
errorMessage = `Error encountered running the script. Please check the code around line ${scriptUnitTestResult.line_number}`;
|
||||
} else {
|
||||
errorMessage = `Error encountered running the script. ${JSON.stringify(
|
||||
scriptUnitTestResult.error
|
||||
)}`;
|
||||
}
|
||||
let errorStringElement = <span>{errorMessage}</span>;
|
||||
|
||||
let errorContextElement = null;
|
||||
|
||||
if (scriptUnitTestResult.context) {
|
||||
errorStringElement = (
|
||||
<span>Unexpected result. Please see the comparison below.</span>
|
||||
|
@ -580,16 +594,22 @@ export default function ProcessModelEditDiagram() {
|
|||
</Col>
|
||||
);
|
||||
}
|
||||
const inputJson = JSON.stringify(
|
||||
JSON.parse(currentScriptUnitTest.inputJson.value),
|
||||
null,
|
||||
' '
|
||||
);
|
||||
const outputJson = JSON.stringify(
|
||||
JSON.parse(currentScriptUnitTest.expectedOutputJson.value),
|
||||
null,
|
||||
' '
|
||||
);
|
||||
let inputJson = currentScriptUnitTest.inputJson.value;
|
||||
let outputJson = currentScriptUnitTest.expectedOutputJson.value;
|
||||
try {
|
||||
inputJson = JSON.stringify(
|
||||
JSON.parse(currentScriptUnitTest.inputJson.value),
|
||||
null,
|
||||
' '
|
||||
);
|
||||
outputJson = JSON.stringify(
|
||||
JSON.parse(currentScriptUnitTest.expectedOutputJson.value),
|
||||
null,
|
||||
' '
|
||||
);
|
||||
} catch (e) {
|
||||
// Attemping to format the json failed -- it's invalid.
|
||||
}
|
||||
|
||||
return (
|
||||
<main>
|
||||
|
|
Loading…
Reference in New Issue