Merge pull request #84 from sartography/feature/bpmn_user_permissions
Feature/bpmn user permissions
This commit is contained in:
commit
bcc939d6f3
|
@ -7,7 +7,8 @@ def main() -> None:
|
||||||
"""Main."""
|
"""Main."""
|
||||||
app = get_hacked_up_app_for_script()
|
app = get_hacked_up_app_for_script()
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
AuthorizationService.delete_all_permissions_and_recreate()
|
AuthorizationService.delete_all_permissions()
|
||||||
|
AuthorizationService.import_permissions_from_yaml_file()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -424,6 +424,7 @@
|
||||||
"enabled" : true,
|
"enabled" : true,
|
||||||
"totp" : false,
|
"totp" : false,
|
||||||
"emailVerified" : false,
|
"emailVerified" : false,
|
||||||
|
"email": "admin@status.im",
|
||||||
"firstName" : "",
|
"firstName" : "",
|
||||||
"lastName" : "",
|
"lastName" : "",
|
||||||
"credentials" : [ {
|
"credentials" : [ {
|
||||||
|
@ -446,6 +447,7 @@
|
||||||
"enabled" : true,
|
"enabled" : true,
|
||||||
"totp" : false,
|
"totp" : false,
|
||||||
"emailVerified" : false,
|
"emailVerified" : false,
|
||||||
|
"email": "alex@sartography.com",
|
||||||
"credentials" : [ {
|
"credentials" : [ {
|
||||||
"id" : "81a61a3b-228d-42b3-b39a-f62d8e7f57ca",
|
"id" : "81a61a3b-228d-42b3-b39a-f62d8e7f57ca",
|
||||||
"type" : "password",
|
"type" : "password",
|
||||||
|
@ -465,6 +467,7 @@
|
||||||
"enabled" : true,
|
"enabled" : true,
|
||||||
"totp" : false,
|
"totp" : false,
|
||||||
"emailVerified" : false,
|
"emailVerified" : false,
|
||||||
|
"email": "amir@status.im",
|
||||||
"credentials" : [ {
|
"credentials" : [ {
|
||||||
"id" : "e589f3ad-bf7b-4756-89f7-7894c03c2831",
|
"id" : "e589f3ad-bf7b-4756-89f7-7894c03c2831",
|
||||||
"type" : "password",
|
"type" : "password",
|
||||||
|
@ -484,6 +487,7 @@
|
||||||
"enabled" : true,
|
"enabled" : true,
|
||||||
"totp" : false,
|
"totp" : false,
|
||||||
"emailVerified" : false,
|
"emailVerified" : false,
|
||||||
|
"email": "ciadmin1@status.im",
|
||||||
"credentials" : [ {
|
"credentials" : [ {
|
||||||
"id" : "111b5ea1-c2ab-470a-a16b-2373bc94de7a",
|
"id" : "111b5ea1-c2ab-470a-a16b-2373bc94de7a",
|
||||||
"type" : "password",
|
"type" : "password",
|
||||||
|
@ -506,6 +510,7 @@
|
||||||
"enabled" : true,
|
"enabled" : true,
|
||||||
"totp" : false,
|
"totp" : false,
|
||||||
"emailVerified" : false,
|
"emailVerified" : false,
|
||||||
|
"email": "ciuser1@status.im",
|
||||||
"credentials" : [ {
|
"credentials" : [ {
|
||||||
"id" : "762f36e9-47af-44da-8520-cf09d752497a",
|
"id" : "762f36e9-47af-44da-8520-cf09d752497a",
|
||||||
"type" : "password",
|
"type" : "password",
|
||||||
|
@ -528,6 +533,7 @@
|
||||||
"enabled" : true,
|
"enabled" : true,
|
||||||
"totp" : false,
|
"totp" : false,
|
||||||
"emailVerified" : false,
|
"emailVerified" : false,
|
||||||
|
"email": "core@status.im",
|
||||||
"firstName" : "",
|
"firstName" : "",
|
||||||
"lastName" : "",
|
"lastName" : "",
|
||||||
"credentials" : [ {
|
"credentials" : [ {
|
||||||
|
@ -550,6 +556,7 @@
|
||||||
"enabled" : true,
|
"enabled" : true,
|
||||||
"totp" : false,
|
"totp" : false,
|
||||||
"emailVerified" : false,
|
"emailVerified" : false,
|
||||||
|
"email": "dan@sartography.com",
|
||||||
"credentials" : [ {
|
"credentials" : [ {
|
||||||
"id" : "d517c520-f500-4542-80e5-7144daef1e32",
|
"id" : "d517c520-f500-4542-80e5-7144daef1e32",
|
||||||
"type" : "password",
|
"type" : "password",
|
||||||
|
@ -569,6 +576,7 @@
|
||||||
"enabled" : true,
|
"enabled" : true,
|
||||||
"totp" : false,
|
"totp" : false,
|
||||||
"emailVerified" : false,
|
"emailVerified" : false,
|
||||||
|
"email": "daniel@sartography.com",
|
||||||
"credentials" : [ {
|
"credentials" : [ {
|
||||||
"id" : "f240495c-265b-42fc-99db-46928580d07d",
|
"id" : "f240495c-265b-42fc-99db-46928580d07d",
|
||||||
"type" : "password",
|
"type" : "password",
|
||||||
|
@ -588,6 +596,7 @@
|
||||||
"enabled" : true,
|
"enabled" : true,
|
||||||
"totp" : false,
|
"totp" : false,
|
||||||
"emailVerified" : false,
|
"emailVerified" : false,
|
||||||
|
"email": "elizabeth@sartography.com",
|
||||||
"credentials" : [ {
|
"credentials" : [ {
|
||||||
"id" : "ae951ec8-9fc9-4f1b-b340-bbbe463ae5c2",
|
"id" : "ae951ec8-9fc9-4f1b-b340-bbbe463ae5c2",
|
||||||
"type" : "password",
|
"type" : "password",
|
||||||
|
@ -607,6 +616,7 @@
|
||||||
"enabled" : true,
|
"enabled" : true,
|
||||||
"totp" : false,
|
"totp" : false,
|
||||||
"emailVerified" : false,
|
"emailVerified" : false,
|
||||||
|
"email": "fin@status.im",
|
||||||
"firstName" : "",
|
"firstName" : "",
|
||||||
"lastName" : "",
|
"lastName" : "",
|
||||||
"credentials" : [ {
|
"credentials" : [ {
|
||||||
|
@ -629,6 +639,7 @@
|
||||||
"enabled" : true,
|
"enabled" : true,
|
||||||
"totp" : false,
|
"totp" : false,
|
||||||
"emailVerified" : false,
|
"emailVerified" : false,
|
||||||
|
"email": "fin1@status.im",
|
||||||
"firstName" : "",
|
"firstName" : "",
|
||||||
"lastName" : "",
|
"lastName" : "",
|
||||||
"credentials" : [ {
|
"credentials" : [ {
|
||||||
|
@ -651,6 +662,7 @@
|
||||||
"enabled" : true,
|
"enabled" : true,
|
||||||
"totp" : false,
|
"totp" : false,
|
||||||
"emailVerified" : false,
|
"emailVerified" : false,
|
||||||
|
"email": "finance_user1@status.im",
|
||||||
"credentials" : [ {
|
"credentials" : [ {
|
||||||
"id" : "f14722ec-13a7-4d35-a4ec-0475d405ae58",
|
"id" : "f14722ec-13a7-4d35-a4ec-0475d405ae58",
|
||||||
"type" : "password",
|
"type" : "password",
|
||||||
|
@ -670,6 +682,7 @@
|
||||||
"enabled" : true,
|
"enabled" : true,
|
||||||
"totp" : false,
|
"totp" : false,
|
||||||
"emailVerified" : false,
|
"emailVerified" : false,
|
||||||
|
"email": "harmeet@status.im",
|
||||||
"credentials" : [ {
|
"credentials" : [ {
|
||||||
"id" : "89c26090-9bd3-46ac-b038-883d02e3f125",
|
"id" : "89c26090-9bd3-46ac-b038-883d02e3f125",
|
||||||
"type" : "password",
|
"type" : "password",
|
||||||
|
@ -689,6 +702,7 @@
|
||||||
"enabled" : true,
|
"enabled" : true,
|
||||||
"totp" : false,
|
"totp" : false,
|
||||||
"emailVerified" : false,
|
"emailVerified" : false,
|
||||||
|
"email": "j@status.im",
|
||||||
"firstName" : "",
|
"firstName" : "",
|
||||||
"lastName" : "",
|
"lastName" : "",
|
||||||
"credentials" : [ {
|
"credentials" : [ {
|
||||||
|
@ -711,6 +725,7 @@
|
||||||
"enabled" : true,
|
"enabled" : true,
|
||||||
"totp" : false,
|
"totp" : false,
|
||||||
"emailVerified" : false,
|
"emailVerified" : false,
|
||||||
|
"email": "jakub@status.im",
|
||||||
"credentials" : [ {
|
"credentials" : [ {
|
||||||
"id" : "ce141fa5-b8d5-4bbe-93e7-22e7119f97c2",
|
"id" : "ce141fa5-b8d5-4bbe-93e7-22e7119f97c2",
|
||||||
"type" : "password",
|
"type" : "password",
|
||||||
|
@ -730,6 +745,7 @@
|
||||||
"enabled" : true,
|
"enabled" : true,
|
||||||
"totp" : false,
|
"totp" : false,
|
||||||
"emailVerified" : false,
|
"emailVerified" : false,
|
||||||
|
"email": "jarrad@status.im",
|
||||||
"credentials" : [ {
|
"credentials" : [ {
|
||||||
"id" : "113e0343-1069-476d-83f9-21d98edb9cfa",
|
"id" : "113e0343-1069-476d-83f9-21d98edb9cfa",
|
||||||
"type" : "password",
|
"type" : "password",
|
||||||
|
@ -749,6 +765,7 @@
|
||||||
"enabled" : true,
|
"enabled" : true,
|
||||||
"totp" : false,
|
"totp" : false,
|
||||||
"emailVerified" : false,
|
"emailVerified" : false,
|
||||||
|
"email": "jason@sartography.com",
|
||||||
"credentials" : [ {
|
"credentials" : [ {
|
||||||
"id" : "40abf32e-f0cc-4a17-8231-1a69a02c1b0b",
|
"id" : "40abf32e-f0cc-4a17-8231-1a69a02c1b0b",
|
||||||
"type" : "password",
|
"type" : "password",
|
||||||
|
@ -768,6 +785,7 @@
|
||||||
"enabled" : true,
|
"enabled" : true,
|
||||||
"totp" : false,
|
"totp" : false,
|
||||||
"emailVerified" : false,
|
"emailVerified" : false,
|
||||||
|
"email": "jon@sartography.com",
|
||||||
"credentials" : [ {
|
"credentials" : [ {
|
||||||
"id" : "8b520e01-5b9b-44ab-9ee8-505bd0831a45",
|
"id" : "8b520e01-5b9b-44ab-9ee8-505bd0831a45",
|
||||||
"type" : "password",
|
"type" : "password",
|
||||||
|
@ -787,6 +805,7 @@
|
||||||
"enabled" : true,
|
"enabled" : true,
|
||||||
"totp" : false,
|
"totp" : false,
|
||||||
"emailVerified" : false,
|
"emailVerified" : false,
|
||||||
|
"email": "kb@sartography.com",
|
||||||
"credentials" : [ {
|
"credentials" : [ {
|
||||||
"id" : "2c0be363-038f-48f1-86d6-91fdd28657cf",
|
"id" : "2c0be363-038f-48f1-86d6-91fdd28657cf",
|
||||||
"type" : "password",
|
"type" : "password",
|
||||||
|
@ -806,6 +825,7 @@
|
||||||
"enabled" : true,
|
"enabled" : true,
|
||||||
"totp" : false,
|
"totp" : false,
|
||||||
"emailVerified" : false,
|
"emailVerified" : false,
|
||||||
|
"email": "lead@status.im",
|
||||||
"firstName" : "",
|
"firstName" : "",
|
||||||
"lastName" : "",
|
"lastName" : "",
|
||||||
"credentials" : [ {
|
"credentials" : [ {
|
||||||
|
@ -828,6 +848,7 @@
|
||||||
"enabled" : true,
|
"enabled" : true,
|
||||||
"totp" : false,
|
"totp" : false,
|
||||||
"emailVerified" : false,
|
"emailVerified" : false,
|
||||||
|
"email": "lead1@status.im",
|
||||||
"firstName" : "",
|
"firstName" : "",
|
||||||
"lastName" : "",
|
"lastName" : "",
|
||||||
"credentials" : [ {
|
"credentials" : [ {
|
||||||
|
@ -850,6 +871,7 @@
|
||||||
"enabled" : true,
|
"enabled" : true,
|
||||||
"totp" : false,
|
"totp" : false,
|
||||||
"emailVerified" : false,
|
"emailVerified" : false,
|
||||||
|
"email": "manuchehr@status.im",
|
||||||
"credentials" : [ {
|
"credentials" : [ {
|
||||||
"id" : "07dabf55-b5d3-4f98-abba-3334086ecf5e",
|
"id" : "07dabf55-b5d3-4f98-abba-3334086ecf5e",
|
||||||
"type" : "password",
|
"type" : "password",
|
||||||
|
@ -869,6 +891,7 @@
|
||||||
"enabled" : true,
|
"enabled" : true,
|
||||||
"totp" : false,
|
"totp" : false,
|
||||||
"emailVerified" : false,
|
"emailVerified" : false,
|
||||||
|
"email": "mike@sartography.com",
|
||||||
"credentials" : [ {
|
"credentials" : [ {
|
||||||
"id" : "1ed375fb-0f1a-4c2a-9243-2477242cf7bd",
|
"id" : "1ed375fb-0f1a-4c2a-9243-2477242cf7bd",
|
||||||
"type" : "password",
|
"type" : "password",
|
||||||
|
@ -888,6 +911,7 @@
|
||||||
"enabled" : true,
|
"enabled" : true,
|
||||||
"totp" : false,
|
"totp" : false,
|
||||||
"emailVerified" : false,
|
"emailVerified" : false,
|
||||||
|
"email": "natalia@sartography.com",
|
||||||
"credentials" : [ {
|
"credentials" : [ {
|
||||||
"id" : "b6aa9936-39cc-4931-bfeb-60e6753de5ba",
|
"id" : "b6aa9936-39cc-4931-bfeb-60e6753de5ba",
|
||||||
"type" : "password",
|
"type" : "password",
|
||||||
|
@ -907,6 +931,7 @@
|
||||||
"enabled" : true,
|
"enabled" : true,
|
||||||
"totp" : false,
|
"totp" : false,
|
||||||
"emailVerified" : false,
|
"emailVerified" : false,
|
||||||
|
"email": "sasha@status.im",
|
||||||
"credentials" : [ {
|
"credentials" : [ {
|
||||||
"id" : "4a170af4-6f0c-4e7b-b70c-e674edf619df",
|
"id" : "4a170af4-6f0c-4e7b-b70c-e674edf619df",
|
||||||
"type" : "password",
|
"type" : "password",
|
||||||
|
@ -926,6 +951,7 @@
|
||||||
"enabled" : true,
|
"enabled" : true,
|
||||||
"totp" : false,
|
"totp" : false,
|
||||||
"emailVerified" : false,
|
"emailVerified" : false,
|
||||||
|
"email": "service-account@status.im",
|
||||||
"serviceAccountClientId" : "spiffworkflow-backend",
|
"serviceAccountClientId" : "spiffworkflow-backend",
|
||||||
"credentials" : [ ],
|
"credentials" : [ ],
|
||||||
"disableableCredentialTypes" : [ ],
|
"disableableCredentialTypes" : [ ],
|
||||||
|
@ -943,6 +969,7 @@
|
||||||
"enabled" : true,
|
"enabled" : true,
|
||||||
"totp" : false,
|
"totp" : false,
|
||||||
"emailVerified" : false,
|
"emailVerified" : false,
|
||||||
|
"email": "service-account-withauth@status.im",
|
||||||
"serviceAccountClientId" : "withAuth",
|
"serviceAccountClientId" : "withAuth",
|
||||||
"credentials" : [ ],
|
"credentials" : [ ],
|
||||||
"disableableCredentialTypes" : [ ],
|
"disableableCredentialTypes" : [ ],
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
"""empty message
|
"""empty message
|
||||||
|
|
||||||
Revision ID: b99a4cb94b5b
|
Revision ID: 67197b02b0c1
|
||||||
Revises:
|
Revises:
|
||||||
Create Date: 2022-12-20 10:45:08.295317
|
Create Date: 2022-12-20 15:05:31.545567
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from alembic import op
|
from alembic import op
|
||||||
|
@ -10,7 +10,7 @@ import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision = 'b99a4cb94b5b'
|
revision = '67197b02b0c1'
|
||||||
down_revision = None
|
down_revision = None
|
||||||
branch_labels = None
|
branch_labels = None
|
||||||
depends_on = None
|
depends_on = None
|
||||||
|
@ -72,16 +72,15 @@ def upgrade():
|
||||||
op.create_table('user',
|
op.create_table('user',
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
sa.Column('username', sa.String(length=255), 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', sa.String(length=50), nullable=False),
|
||||||
sa.Column('service_id', sa.String(length=255), 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('email', sa.String(length=255), nullable=True),
|
||||||
sa.Column('updated_at_in_seconds', sa.Integer(), nullable=True),
|
sa.Column('updated_at_in_seconds', sa.Integer(), nullable=True),
|
||||||
sa.Column('created_at_in_seconds', sa.Integer(), nullable=True),
|
sa.Column('created_at_in_seconds', sa.Integer(), nullable=True),
|
||||||
sa.PrimaryKeyConstraint('id'),
|
sa.PrimaryKeyConstraint('id'),
|
||||||
sa.UniqueConstraint('service', 'service_id', name='service_key'),
|
sa.UniqueConstraint('service', 'service_id', name='service_key'),
|
||||||
sa.UniqueConstraint('uid')
|
sa.UniqueConstraint('username')
|
||||||
)
|
)
|
||||||
op.create_table('message_correlation_property',
|
op.create_table('message_correlation_property',
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
@ -176,6 +175,14 @@ def upgrade():
|
||||||
sa.PrimaryKeyConstraint('id'),
|
sa.PrimaryKeyConstraint('id'),
|
||||||
sa.UniqueConstraint('user_id', 'group_id', name='user_group_assignment_unique')
|
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',
|
op.create_table('human_task',
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
sa.Column('process_instance_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_table('message_correlation')
|
||||||
op.drop_index(op.f('ix_human_task_completed'), table_name='human_task')
|
op.drop_index(op.f('ix_human_task_completed'), table_name='human_task')
|
||||||
op.drop_table('human_task')
|
op.drop_table('human_task')
|
||||||
|
op.drop_table('user_group_assignment_waiting')
|
||||||
op.drop_table('user_group_assignment')
|
op.drop_table('user_group_assignment')
|
||||||
op.drop_table('secret')
|
op.drop_table('secret')
|
||||||
op.drop_table('refresh_token')
|
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
|
import spiffworkflow_backend.load_database_models # noqa: F401
|
||||||
from spiffworkflow_backend.config import setup_config
|
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.admin_blueprint.admin_blueprint import admin_blueprint
|
||||||
from spiffworkflow_backend.routes.openid_blueprint.openid_blueprint import (
|
from spiffworkflow_backend.routes.openid_blueprint.openid_blueprint import (
|
||||||
openid_blueprint,
|
openid_blueprint,
|
||||||
|
@ -117,7 +118,7 @@ def create_app() -> flask.app.Flask:
|
||||||
]
|
]
|
||||||
CORS(app, origins=origins_re, max_age=3600)
|
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)
|
mail = Mail(app)
|
||||||
app.config["MAIL_APP"] = mail
|
app.config["MAIL_APP"] = mail
|
||||||
|
|
|
@ -10,55 +10,52 @@ groups:
|
||||||
admin:
|
admin:
|
||||||
users:
|
users:
|
||||||
[
|
[
|
||||||
admin,
|
admin@status.im,
|
||||||
jakub,
|
jakub@status.im,
|
||||||
kb,
|
jarrad@status.im,
|
||||||
alex,
|
kb@sartography.com,
|
||||||
dan,
|
alex@sartography.com,
|
||||||
mike,
|
dan@sartography.com,
|
||||||
jason,
|
mike@sartography.com,
|
||||||
jarrad,
|
jason@sartography.com,
|
||||||
elizabeth,
|
j@sartography.com,
|
||||||
jon,
|
elizabeth@sartography.com,
|
||||||
|
jon@sartography.com,
|
||||||
]
|
]
|
||||||
|
|
||||||
Finance Team:
|
Finance Team:
|
||||||
users:
|
users:
|
||||||
[
|
[
|
||||||
jakub,
|
jakub@status.im,
|
||||||
alex,
|
amir@status.im,
|
||||||
dan,
|
jarrad@status.im,
|
||||||
mike,
|
sasha@status.im,
|
||||||
jason,
|
fin@sartography.com,
|
||||||
amir,
|
fin1@sartography.com,
|
||||||
jarrad,
|
alex@sartography.com,
|
||||||
elizabeth,
|
dan@sartography.com,
|
||||||
jon,
|
mike@sartography.com,
|
||||||
sasha,
|
jason@sartography.com,
|
||||||
fin,
|
j@sartography.com,
|
||||||
fin1,
|
elizabeth@sartography.com,
|
||||||
|
jon@sartography.com,
|
||||||
]
|
]
|
||||||
|
|
||||||
demo:
|
demo:
|
||||||
users:
|
users:
|
||||||
[
|
[
|
||||||
core,
|
harmeet@status.im,
|
||||||
fin,
|
sasha@status.im,
|
||||||
fin1,
|
manuchehr@status.im,
|
||||||
harmeet,
|
core@status.im,
|
||||||
jason,
|
fin@status.im,
|
||||||
sasha,
|
fin1@status.im,
|
||||||
manuchehr,
|
lead@status.im,
|
||||||
lead,
|
lead1@status.im
|
||||||
lead1
|
|
||||||
]
|
]
|
||||||
|
|
||||||
core-contributor:
|
test:
|
||||||
users:
|
users: [natalia@sartography.com]
|
||||||
[
|
|
||||||
core,
|
|
||||||
harmeet,
|
|
||||||
]
|
|
||||||
|
|
||||||
admin-ro:
|
admin-ro:
|
||||||
users:
|
users:
|
||||||
|
@ -66,16 +63,12 @@ groups:
|
||||||
j,
|
j,
|
||||||
]
|
]
|
||||||
|
|
||||||
test:
|
|
||||||
users: [natalia]
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
admin:
|
admin:
|
||||||
groups: [admin]
|
groups: [admin]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [create, read, update, delete]
|
allowed_permissions: [create, read, update, delete]
|
||||||
uri: /*
|
uri: /*
|
||||||
|
|
||||||
admin-readonly:
|
admin-readonly:
|
||||||
groups: [admin-ro]
|
groups: [admin-ro]
|
||||||
users: []
|
users: []
|
||||||
|
@ -85,121 +78,93 @@ permissions:
|
||||||
groups: [admin-ro]
|
groups: [admin-ro]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [create, read, update, delete]
|
allowed_permissions: [create, read, update, delete]
|
||||||
uri: /v1.0/process-instances/*
|
uri: /process-instances/*
|
||||||
|
|
||||||
tasks-crud:
|
# open system defaults for everybody
|
||||||
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
|
|
||||||
read-all-process-groups:
|
read-all-process-groups:
|
||||||
groups: [everybody]
|
groups: [everybody]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [read]
|
allowed_permissions: [read]
|
||||||
uri: /v1.0/process-groups/*
|
uri: /process-groups/*
|
||||||
read-all-process-models:
|
read-all-process-models:
|
||||||
groups: [everybody]
|
groups: [everybody]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [read]
|
allowed_permissions: [read]
|
||||||
uri: /v1.0/process-models/*
|
uri: /process-models/*
|
||||||
|
|
||||||
|
# basic perms for everybody
|
||||||
read-all-process-instances-for-me:
|
read-all-process-instances-for-me:
|
||||||
groups: [everybody]
|
groups: [everybody]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [read]
|
allowed_permissions: [read]
|
||||||
uri: /v1.0/process-instances/for-me/*
|
uri: /process-instances/for-me/*
|
||||||
read-process-instance-reports:
|
read-process-instance-reports:
|
||||||
groups: [everybody]
|
groups: [everybody]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [create, read, update, delete]
|
allowed_permissions: [create, read, update, delete]
|
||||||
uri: /v1.0/process-instances/reports/*
|
uri: /process-instances/reports/*
|
||||||
processes-read:
|
processes-read:
|
||||||
groups: [everybody]
|
groups: [everybody]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [read]
|
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:
|
finance-admin:
|
||||||
groups: ["Finance Team"]
|
groups: ["Finance Team"]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [create, read, update, delete]
|
allowed_permissions: [create, read, update, delete]
|
||||||
uri: /v1.0/process-groups/manage-procurement:procurement:*
|
uri: /process-groups/manage-procurement:procurement:*
|
||||||
|
|
||||||
manage-revenue-streams-instances:
|
manage-revenue-streams-instances:
|
||||||
groups: ["core-contributor", "demo"]
|
groups: ["demo"]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [create, read]
|
allowed_permissions: [create]
|
||||||
uri: /v1.0/process-instances/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/*
|
uri: /process-instances/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/*
|
||||||
|
|
||||||
manage-procurement-invoice-instances:
|
manage-procurement-invoice-instances:
|
||||||
groups: ["core-contributor", "demo"]
|
groups: ["demo"]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [create, read]
|
allowed_permissions: [create]
|
||||||
uri: /v1.0/process-instances/manage-procurement:procurement:core-contributor-invoice-management:*
|
uri: /process-instances/manage-procurement:procurement:core-contributor-invoice-management:*
|
||||||
|
|
||||||
manage-procurement-instances:
|
manage-procurement-instances:
|
||||||
groups: ["core-contributor", "demo"]
|
groups: ["demo"]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [create, read]
|
allowed_permissions: [create]
|
||||||
uri: /v1.0/process-instances/manage-procurement:vendor-lifecycle-management:*
|
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:
|
create-test-instances:
|
||||||
groups: ["test"]
|
groups: ["test"]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [create, read]
|
allowed_permissions: [create, read]
|
||||||
uri: /v1.0/process-instances/misc:test:*
|
uri: /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/*
|
|
||||||
|
|
|
@ -2,14 +2,17 @@ default_group: everybody
|
||||||
|
|
||||||
users:
|
users:
|
||||||
admin:
|
admin:
|
||||||
|
service: local_open_id
|
||||||
email: admin@spiffworkflow.org
|
email: admin@spiffworkflow.org
|
||||||
password: admin
|
password: admin
|
||||||
preferred_username: Admin
|
preferred_username: Admin
|
||||||
nelson:
|
nelson:
|
||||||
|
service: local_open_id
|
||||||
email: nelson@spiffworkflow.org
|
email: nelson@spiffworkflow.org
|
||||||
password: nelson
|
password: nelson
|
||||||
preferred_username: Nelson
|
preferred_username: Nelson
|
||||||
malala:
|
malala:
|
||||||
|
service: local_open_id
|
||||||
email: malala@spiffworkflow.org
|
email: malala@spiffworkflow.org
|
||||||
password: malala
|
password: malala
|
||||||
preferred_username: Malala
|
preferred_username: Malala
|
||||||
|
@ -18,17 +21,17 @@ groups:
|
||||||
admin:
|
admin:
|
||||||
users:
|
users:
|
||||||
[
|
[
|
||||||
admin,
|
admin@spiffworkflow.org,
|
||||||
]
|
]
|
||||||
Education:
|
Education:
|
||||||
users:
|
users:
|
||||||
[
|
[
|
||||||
malala
|
malala@spiffworkflow.org
|
||||||
]
|
]
|
||||||
President:
|
President:
|
||||||
users:
|
users:
|
||||||
[
|
[
|
||||||
nelson
|
nelson@spiffworkflow.org
|
||||||
]
|
]
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
|
@ -44,45 +47,44 @@ permissions:
|
||||||
groups: [everybody]
|
groups: [everybody]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [create, read, update, delete]
|
allowed_permissions: [create, read, update, delete]
|
||||||
uri: /v1.0/tasks/*
|
uri: /tasks/*
|
||||||
|
|
||||||
# Everyone can see everything (all groups, and processes are visible)
|
# Everyone can see everything (all groups, and processes are visible)
|
||||||
read-all-process-groups:
|
read-all-process-groups:
|
||||||
groups: [ everybody ]
|
groups: [ everybody ]
|
||||||
users: [ ]
|
users: [ ]
|
||||||
allowed_permissions: [ read ]
|
allowed_permissions: [ read ]
|
||||||
uri: /v1.0/process-groups/*
|
uri: /process-groups/*
|
||||||
read-all-process-models:
|
read-all-process-models:
|
||||||
groups: [ everybody ]
|
groups: [ everybody ]
|
||||||
users: [ ]
|
users: [ ]
|
||||||
allowed_permissions: [ read ]
|
allowed_permissions: [ read ]
|
||||||
uri: /v1.0/process-models/*
|
uri: /process-models/*
|
||||||
read-all-process-instance:
|
read-all-process-instance:
|
||||||
groups: [ everybody ]
|
groups: [ everybody ]
|
||||||
users: [ ]
|
users: [ ]
|
||||||
allowed_permissions: [ read ]
|
allowed_permissions: [ read ]
|
||||||
uri: /v1.0/process-instances/*
|
uri: /process-instances/*
|
||||||
read-process-instance-reports:
|
read-process-instance-reports:
|
||||||
groups: [ everybody ]
|
groups: [ everybody ]
|
||||||
users: [ ]
|
users: [ ]
|
||||||
allowed_permissions: [ read ]
|
allowed_permissions: [ read ]
|
||||||
uri: /v1.0/process-instances/reports/*
|
uri: /process-instances/reports/*
|
||||||
processes-read:
|
processes-read:
|
||||||
groups: [ everybody ]
|
groups: [ everybody ]
|
||||||
users: [ ]
|
users: [ ]
|
||||||
allowed_permissions: [ read ]
|
allowed_permissions: [ read ]
|
||||||
uri: /v1.0/processes
|
uri: /processes
|
||||||
|
# Members of the Education group can change the processes under "education".
|
||||||
# Members of the Education group can change they processes work.
|
|
||||||
education-admin:
|
education-admin:
|
||||||
groups: ["Education", "President"]
|
groups: ["Education", "President"]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [create, read, update, delete]
|
allowed_permissions: [create, read, update, delete]
|
||||||
uri: /v1.0/process-groups/education:*
|
uri: /process-groups/education:*
|
||||||
|
|
||||||
# Anyone can start an education process.
|
# Anyone can start an education process.
|
||||||
education-everybody:
|
education-everybody:
|
||||||
groups: [everybody]
|
groups: [everybody]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [create, read]
|
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:
|
admin:
|
||||||
users:
|
users:
|
||||||
[
|
[
|
||||||
admin,
|
admin@status.im,
|
||||||
jakub,
|
jakub@status.im,
|
||||||
kb,
|
jarrad@status.im,
|
||||||
alex,
|
kb@sartography.com,
|
||||||
dan,
|
alex@sartography.com,
|
||||||
mike,
|
dan@sartography.com,
|
||||||
jason,
|
mike@sartography.com,
|
||||||
j,
|
jason@sartography.com,
|
||||||
jarrad,
|
j@sartography.com,
|
||||||
elizabeth,
|
elizabeth@sartography.com,
|
||||||
jon,
|
jon@sartography.com,
|
||||||
natalia,
|
|
||||||
]
|
]
|
||||||
|
|
||||||
Finance Team:
|
Finance Team:
|
||||||
users:
|
users:
|
||||||
[
|
[
|
||||||
jakub,
|
jakub@status.im,
|
||||||
alex,
|
amir@status.im,
|
||||||
dan,
|
jarrad@status.im,
|
||||||
mike,
|
sasha@status.im,
|
||||||
jason,
|
fin@sartography.com,
|
||||||
j,
|
fin1@sartography.com,
|
||||||
amir,
|
alex@sartography.com,
|
||||||
jarrad,
|
dan@sartography.com,
|
||||||
elizabeth,
|
mike@sartography.com,
|
||||||
jon,
|
jason@sartography.com,
|
||||||
natalia,
|
j@sartography.com,
|
||||||
sasha,
|
elizabeth@sartography.com,
|
||||||
fin,
|
jon@sartography.com,
|
||||||
fin1,
|
|
||||||
]
|
]
|
||||||
|
|
||||||
demo:
|
demo:
|
||||||
users:
|
users:
|
||||||
[
|
[
|
||||||
core,
|
harmeet@status.im,
|
||||||
fin,
|
sasha@status.im,
|
||||||
fin1,
|
manuchehr@status.im,
|
||||||
harmeet,
|
core@status.im,
|
||||||
sasha,
|
fin@status.im,
|
||||||
manuchehr,
|
fin1@status.im,
|
||||||
lead,
|
lead@status.im,
|
||||||
lead1
|
lead1@status.im
|
||||||
]
|
|
||||||
|
|
||||||
core-contributor:
|
|
||||||
users:
|
|
||||||
[
|
|
||||||
core,
|
|
||||||
harmeet,
|
|
||||||
]
|
]
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
|
@ -67,104 +58,86 @@ permissions:
|
||||||
groups: [admin]
|
groups: [admin]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [create, read, update, delete]
|
allowed_permissions: [create, read, update, delete]
|
||||||
uri: /v1.0/process-instances/*
|
uri: /process-instances/*
|
||||||
|
|
||||||
tasks-crud:
|
# open system defaults for everybody
|
||||||
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
|
|
||||||
read-all-process-groups:
|
read-all-process-groups:
|
||||||
groups: [everybody]
|
groups: [everybody]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [read]
|
allowed_permissions: [read]
|
||||||
uri: /v1.0/process-groups/*
|
uri: /process-groups/*
|
||||||
read-all-process-models:
|
read-all-process-models:
|
||||||
groups: [everybody]
|
groups: [everybody]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [read]
|
allowed_permissions: [read]
|
||||||
uri: /v1.0/process-models/*
|
uri: /process-models/*
|
||||||
|
|
||||||
|
# basic perms for everybody
|
||||||
read-all-process-instances-for-me:
|
read-all-process-instances-for-me:
|
||||||
groups: [everybody]
|
groups: [everybody]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [read]
|
allowed_permissions: [read]
|
||||||
uri: /v1.0/process-instances/for-me/*
|
uri: /process-instances/for-me/*
|
||||||
manage-process-instance-reports:
|
read-process-instance-reports:
|
||||||
groups: [everybody]
|
groups: [everybody]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [create, read, update, delete]
|
allowed_permissions: [create, read, update, delete]
|
||||||
uri: /v1.0/process-instances/reports/*
|
uri: /process-instances/reports/*
|
||||||
processes-read:
|
processes-read:
|
||||||
groups: [everybody]
|
groups: [everybody]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [read]
|
allowed_permissions: [read]
|
||||||
uri: /v1.0/processes
|
uri: /processes
|
||||||
|
service-tasks:
|
||||||
|
groups: [everybody]
|
||||||
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"]
|
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [read]
|
allowed_permissions: [read]
|
||||||
uri: /v1.0/logs/manage-procurement:*
|
uri: /service-tasks
|
||||||
manage-procurement-admin-instance-logs-slash:
|
tasks-crud:
|
||||||
groups: ["Project Lead"]
|
groups: [everybody]
|
||||||
|
users: []
|
||||||
|
allowed_permissions: [create, read, update, delete]
|
||||||
|
uri: /tasks/*
|
||||||
|
user-groups-for-current-user:
|
||||||
|
groups: [everybody]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [read]
|
allowed_permissions: [read]
|
||||||
uri: /v1.0/logs/manage-procurement/*
|
uri: /user-groups/for-current-user
|
||||||
|
|
||||||
manage-revenue-streams-instances:
|
manage-revenue-streams-instances:
|
||||||
groups: ["core-contributor", "demo"]
|
groups: ["demo"]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [create, read]
|
allowed_permissions: [create]
|
||||||
uri: /v1.0/process-instances/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/*
|
uri: /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/*
|
|
||||||
|
|
||||||
manage-procurement-invoice-instances:
|
manage-procurement-invoice-instances:
|
||||||
groups: ["core-contributor", "demo"]
|
groups: ["demo"]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [create, read]
|
allowed_permissions: [create]
|
||||||
uri: /v1.0/process-instances/manage-procurement:procurement:core-contributor-invoice-management:*
|
uri: /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:*
|
|
||||||
|
|
||||||
manage-procurement-instances:
|
manage-procurement-instances:
|
||||||
groups: ["core-contributor", "demo"]
|
groups: ["demo"]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [create, read]
|
allowed_permissions: [create]
|
||||||
uri: /v1.0/process-instances/manage-procurement:vendor-lifecycle-management:*
|
uri: /process-instances/manage-procurement:vendor-lifecycle-management:*
|
||||||
manage-procurement-instance-logs:
|
|
||||||
groups: ["core-contributor", "demo"]
|
manage-revenue-streams-instances-for-me:
|
||||||
|
groups: ["demo"]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [read]
|
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:
|
admin:
|
||||||
users:
|
users:
|
||||||
[
|
[
|
||||||
admin,
|
admin@status.im,
|
||||||
jakub,
|
jakub@status.im,
|
||||||
kb,
|
jarrad@status.im,
|
||||||
alex,
|
kb@sartography.com,
|
||||||
dan,
|
alex@sartography.com,
|
||||||
mike,
|
dan@sartography.com,
|
||||||
jason,
|
mike@sartography.com,
|
||||||
j,
|
jason@sartography.com,
|
||||||
jarrad,
|
j@sartography.com,
|
||||||
elizabeth,
|
elizabeth@sartography.com,
|
||||||
jon,
|
jon@sartography.com,
|
||||||
]
|
]
|
||||||
|
|
||||||
Finance Team:
|
Finance Team:
|
||||||
users:
|
users:
|
||||||
[
|
[
|
||||||
jakub,
|
jakub@status.im,
|
||||||
alex,
|
amir@status.im,
|
||||||
dan,
|
jarrad@status.im,
|
||||||
mike,
|
sasha@status.im,
|
||||||
jason,
|
fin@sartography.com,
|
||||||
j,
|
fin1@sartography.com,
|
||||||
amir,
|
alex@sartography.com,
|
||||||
jarrad,
|
dan@sartography.com,
|
||||||
elizabeth,
|
mike@sartography.com,
|
||||||
jon,
|
jason@sartography.com,
|
||||||
sasha,
|
j@sartography.com,
|
||||||
fin,
|
elizabeth@sartography.com,
|
||||||
fin1,
|
jon@sartography.com,
|
||||||
]
|
]
|
||||||
|
|
||||||
demo:
|
demo:
|
||||||
users:
|
users:
|
||||||
[
|
[
|
||||||
core,
|
harmeet@status.im,
|
||||||
fin,
|
sasha@status.im,
|
||||||
fin1,
|
manuchehr@status.im,
|
||||||
harmeet,
|
core@status.im,
|
||||||
sasha,
|
fin@status.im,
|
||||||
manuchehr,
|
fin1@status.im,
|
||||||
lead,
|
lead@status.im,
|
||||||
lead1
|
lead1@status.im
|
||||||
]
|
]
|
||||||
|
|
||||||
core-contributor:
|
|
||||||
users:
|
|
||||||
[
|
|
||||||
core,
|
|
||||||
harmeet,
|
|
||||||
]
|
|
||||||
test:
|
test:
|
||||||
users: [natalia]
|
users: [natalia@sartography.com]
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
admin:
|
admin:
|
||||||
|
@ -64,109 +58,91 @@ permissions:
|
||||||
allowed_permissions: [create, read, update, delete]
|
allowed_permissions: [create, read, update, delete]
|
||||||
uri: /*
|
uri: /*
|
||||||
|
|
||||||
tasks-crud:
|
# open system defaults for everybody
|
||||||
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
|
|
||||||
read-all-process-groups:
|
read-all-process-groups:
|
||||||
groups: [everybody]
|
groups: [everybody]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [read]
|
allowed_permissions: [read]
|
||||||
uri: /v1.0/process-groups/*
|
uri: /process-groups/*
|
||||||
read-all-process-models:
|
read-all-process-models:
|
||||||
groups: [everybody]
|
groups: [everybody]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [read]
|
allowed_permissions: [read]
|
||||||
uri: /v1.0/process-models/*
|
uri: /process-models/*
|
||||||
|
|
||||||
|
# basic perms for everybody
|
||||||
read-all-process-instances-for-me:
|
read-all-process-instances-for-me:
|
||||||
groups: [everybody]
|
groups: [everybody]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [read]
|
allowed_permissions: [read]
|
||||||
uri: /v1.0/process-instances/for-me/*
|
uri: /process-instances/for-me/*
|
||||||
read-process-instance-reports:
|
read-process-instance-reports:
|
||||||
groups: [everybody]
|
groups: [everybody]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [create, read, update, delete]
|
allowed_permissions: [create, read, update, delete]
|
||||||
uri: /v1.0/process-instances/reports/*
|
uri: /process-instances/reports/*
|
||||||
processes-read:
|
processes-read:
|
||||||
groups: [everybody]
|
groups: [everybody]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [read]
|
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:
|
finance-admin:
|
||||||
groups: ["Finance Team"]
|
groups: ["Finance Team"]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [create, read, update, delete]
|
allowed_permissions: [create, read, update, delete]
|
||||||
uri: /v1.0/process-groups/manage-procurement:procurement:*
|
uri: /process-groups/manage-procurement:procurement:*
|
||||||
|
|
||||||
manage-revenue-streams-instances:
|
manage-revenue-streams-instances:
|
||||||
groups: ["core-contributor", "demo"]
|
groups: ["demo"]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [create, read]
|
allowed_permissions: [create]
|
||||||
uri: /v1.0/process-instances/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/*
|
uri: /process-instances/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/*
|
||||||
|
|
||||||
manage-procurement-invoice-instances:
|
manage-procurement-invoice-instances:
|
||||||
groups: ["core-contributor", "demo"]
|
groups: ["demo"]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [create, read]
|
allowed_permissions: [create]
|
||||||
uri: /v1.0/process-instances/manage-procurement:procurement:core-contributor-invoice-management:*
|
uri: /process-instances/manage-procurement:procurement:core-contributor-invoice-management:*
|
||||||
|
|
||||||
manage-procurement-instances:
|
manage-procurement-instances:
|
||||||
groups: ["core-contributor", "demo"]
|
groups: ["demo"]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [create, read]
|
allowed_permissions: [create]
|
||||||
uri: /v1.0/process-instances/manage-procurement:vendor-lifecycle-management:*
|
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:
|
create-test-instances:
|
||||||
groups: ["test"]
|
groups: ["test"]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [create, read]
|
allowed_permissions: [create, read]
|
||||||
uri: /v1.0/process-instances/misc:test:*
|
uri: /process-instances/misc:test:*
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
default_group: everybody
|
default_group: everybody
|
||||||
|
|
||||||
|
users:
|
||||||
|
testadmin1:
|
||||||
|
service: https://testing/openid/thing
|
||||||
|
email: testadmin1@spiffworkflow.org
|
||||||
|
password: admin
|
||||||
|
preferred_username: El administrador de la muerte
|
||||||
|
|
||||||
groups:
|
groups:
|
||||||
admin:
|
admin:
|
||||||
users: [testadmin1, testadmin2]
|
users: [testadmin1, testadmin2]
|
||||||
|
@ -14,7 +21,7 @@ permissions:
|
||||||
admin:
|
admin:
|
||||||
groups: [admin]
|
groups: [admin]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [create, read, update, delete, list, instantiate]
|
allowed_permissions: [create, read, update, delete]
|
||||||
uri: /*
|
uri: /*
|
||||||
|
|
||||||
read-all:
|
read-all:
|
||||||
|
@ -27,29 +34,29 @@ permissions:
|
||||||
groups: [everybody]
|
groups: [everybody]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [create, read, update, delete]
|
allowed_permissions: [create, read, update, delete]
|
||||||
uri: /v1.0/tasks/*
|
uri: /tasks/*
|
||||||
|
|
||||||
# TODO: all uris should really have the same structure
|
# TODO: all uris should really have the same structure
|
||||||
finance-admin-group:
|
finance-admin-group:
|
||||||
groups: ["Finance Team"]
|
groups: ["Finance Team"]
|
||||||
users: [testuser4]
|
users: [testuser4]
|
||||||
allowed_permissions: [create, read, update, delete]
|
allowed_permissions: [create, read, update, delete]
|
||||||
uri: /v1.0/process-groups/finance/*
|
uri: /process-groups/finance/*
|
||||||
|
|
||||||
finance-admin-model:
|
finance-admin-model:
|
||||||
groups: ["Finance Team"]
|
groups: ["Finance Team"]
|
||||||
users: [testuser4]
|
users: [testuser4]
|
||||||
allowed_permissions: [create, read, update, delete]
|
allowed_permissions: [create, read, update, delete]
|
||||||
uri: /v1.0/process-models/finance/*
|
uri: /process-models/finance/*
|
||||||
|
|
||||||
finance-admin-model-lanes:
|
finance-admin-model-lanes:
|
||||||
groups: ["Finance Team"]
|
groups: ["Finance Team"]
|
||||||
users: [testuser4]
|
users: [testuser4]
|
||||||
allowed_permissions: [create, read, update, delete]
|
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:
|
finance-admin-instance-run:
|
||||||
groups: ["Finance Team"]
|
groups: ["Finance Team"]
|
||||||
users: [testuser4]
|
users: [testuser4]
|
||||||
allowed_permissions: [create, read, update, delete]
|
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))
|
identifier = db.Column(db.String(255))
|
||||||
|
|
||||||
user_group_assignments = relationship("UserGroupAssignmentModel", cascade="delete")
|
user_group_assignments = relationship("UserGroupAssignmentModel", cascade="delete")
|
||||||
|
user_group_assignments_waiting = relationship( # type: ignore
|
||||||
|
"UserGroupAssignmentWaitingModel", cascade="delete"
|
||||||
|
)
|
||||||
users = relationship( # type: ignore
|
users = relationship( # type: ignore
|
||||||
"UserModel",
|
"UserModel",
|
||||||
viewonly=True,
|
viewonly=True,
|
||||||
|
|
|
@ -32,14 +32,6 @@ class Permission(enum.Enum):
|
||||||
update = "update"
|
update = "update"
|
||||||
delete = "delete"
|
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):
|
class PermissionAssignmentModel(SpiffworkflowBaseDBModel):
|
||||||
"""PermissionAssignmentModel."""
|
"""PermissionAssignmentModel."""
|
||||||
|
|
|
@ -1,22 +1,15 @@
|
||||||
"""User."""
|
"""User."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
import jwt
|
import jwt
|
||||||
import marshmallow
|
import marshmallow
|
||||||
from flask import current_app
|
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 db
|
||||||
from flask_bpmn.models.db import SpiffworkflowBaseDBModel
|
from flask_bpmn.models.db import SpiffworkflowBaseDBModel
|
||||||
from marshmallow import Schema
|
from marshmallow import Schema
|
||||||
from sqlalchemy.orm import relationship
|
from sqlalchemy.orm import relationship
|
||||||
from sqlalchemy.orm import validates
|
|
||||||
|
|
||||||
from spiffworkflow_backend.models.group import GroupModel
|
from spiffworkflow_backend.models.group import GroupModel
|
||||||
from spiffworkflow_backend.services.authentication_service import (
|
|
||||||
AuthenticationProviderTypes,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class UserNotFoundError(Exception):
|
class UserNotFoundError(Exception):
|
||||||
|
@ -28,14 +21,15 @@ class UserModel(SpiffworkflowBaseDBModel):
|
||||||
|
|
||||||
__tablename__ = "user"
|
__tablename__ = "user"
|
||||||
__table_args__ = (db.UniqueConstraint("service", "service_id", name="service_key"),)
|
__table_args__ = (db.UniqueConstraint("service", "service_id", name="service_key"),)
|
||||||
|
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
# server and service id must be unique, not username.
|
username = db.Column(
|
||||||
username = db.Column(db.String(255), nullable=False, unique=False)
|
db.String(255), nullable=False, unique=True
|
||||||
uid = db.Column(db.String(50), unique=True)
|
) # should always be a unique value
|
||||||
service = db.Column(db.String(50), nullable=False, unique=False)
|
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)
|
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))
|
email = db.Column(db.String(255))
|
||||||
updated_at_in_seconds: int = db.Column(db.Integer)
|
updated_at_in_seconds: int = db.Column(db.Integer)
|
||||||
created_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
|
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:
|
def encode_auth_token(self) -> str:
|
||||||
"""Generate the Auth Token.
|
"""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"])
|
@admin_blueprint.route("/process-models/<process_model_id>/run", methods=["GET"])
|
||||||
def process_model_run(process_model_id: str) -> Union[str, Response]:
|
def process_model_run(process_model_id: str) -> Union[str, Response]:
|
||||||
"""Process_model_run."""
|
"""Process_model_run."""
|
||||||
user = UserService.create_user("internal", "Mr. Test", username="Mr. Test")
|
user = UserService.create_user("Mr. Test", "internal", "Mr. Test")
|
||||||
process_instance = (
|
process_instance = (
|
||||||
ProcessInstanceService.create_process_instance_from_process_model_identifier(
|
ProcessInstanceService.create_process_instance_from_process_model_identifier(
|
||||||
process_model_id, user
|
process_model_id, user
|
||||||
|
|
|
@ -111,6 +111,7 @@ def token() -> dict:
|
||||||
"iat": time.time(),
|
"iat": time.time(),
|
||||||
"exp": time.time() + 86400, # Expire after a day.
|
"exp": time.time() + 86400, # Expire after a day.
|
||||||
"sub": user_name,
|
"sub": user_name,
|
||||||
|
"email": user_details["email"],
|
||||||
"preferred_username": user_details.get("preferred_username", user_name),
|
"preferred_username": user_details.get("preferred_username", user_name),
|
||||||
},
|
},
|
||||||
client_secret,
|
client_secret,
|
||||||
|
|
|
@ -76,7 +76,7 @@ def verify_token(
|
||||||
except ApiError as ae: # API Error is only thrown in the token is outdated.
|
except ApiError as ae: # API Error is only thrown in the token is outdated.
|
||||||
# Try to refresh the token
|
# Try to refresh the token
|
||||||
user = UserService.get_user_by_service_and_service_id(
|
user = UserService.get_user_by_service_and_service_id(
|
||||||
"open_id", decoded_token["sub"]
|
decoded_token["iss"], decoded_token["sub"]
|
||||||
)
|
)
|
||||||
if user:
|
if user:
|
||||||
refresh_token = AuthenticationService.get_refresh_token(user.id)
|
refresh_token = AuthenticationService.get_refresh_token(user.id)
|
||||||
|
@ -105,10 +105,12 @@ def verify_token(
|
||||||
) from e
|
) from e
|
||||||
|
|
||||||
if (
|
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
|
): # not sure what to test yet
|
||||||
user_model = (
|
user_model = (
|
||||||
UserModel.query.filter(UserModel.service == "open_id")
|
UserModel.query.filter(UserModel.service == user_info["iss"])
|
||||||
.filter(UserModel.service_id == user_info["sub"])
|
.filter(UserModel.service_id == user_info["sub"])
|
||||||
.first()
|
.first()
|
||||||
)
|
)
|
||||||
|
@ -341,9 +343,5 @@ def get_user_from_decoded_internal_token(decoded_token: dict) -> Optional[UserMo
|
||||||
)
|
)
|
||||||
if user:
|
if user:
|
||||||
return user
|
return user
|
||||||
user = UserModel(
|
user = UserService.create_user(service_id, service, service_id)
|
||||||
username=service_id,
|
|
||||||
service=service,
|
|
||||||
service_id=service_id,
|
|
||||||
)
|
|
||||||
return user
|
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):
|
class FactService(Script):
|
||||||
"""FactService."""
|
"""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:
|
def get_description(self) -> str:
|
||||||
"""Get_description."""
|
"""Get_description."""
|
||||||
return """Just your basic class that can pull in data from a few api endpoints and
|
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):
|
class GetCurrentUser(Script):
|
||||||
"""GetCurrentUser."""
|
"""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:
|
def get_description(self) -> str:
|
||||||
"""Get_description."""
|
"""Get_description."""
|
||||||
return """Return the current user."""
|
return """Return the current user."""
|
||||||
|
|
|
@ -10,6 +10,11 @@ from spiffworkflow_backend.scripts.script import Script
|
||||||
class GetEnv(Script):
|
class GetEnv(Script):
|
||||||
"""GetEnv."""
|
"""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:
|
def get_description(self) -> str:
|
||||||
"""Get_description."""
|
"""Get_description."""
|
||||||
return """Returns the current environment - ie testing, staging, production."""
|
return """Returns the current environment - ie testing, staging, production."""
|
||||||
|
|
|
@ -12,6 +12,11 @@ from spiffworkflow_backend.scripts.script import Script
|
||||||
class GetFrontendUrl(Script):
|
class GetFrontendUrl(Script):
|
||||||
"""GetFrontendUrl."""
|
"""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:
|
def get_description(self) -> str:
|
||||||
"""Get_description."""
|
"""Get_description."""
|
||||||
return """Return the url to the frontend."""
|
return """Return the url to the frontend."""
|
||||||
|
|
|
@ -12,6 +12,11 @@ from spiffworkflow_backend.scripts.script import Script
|
||||||
class GetGroupMembers(Script):
|
class GetGroupMembers(Script):
|
||||||
"""GetGroupMembers."""
|
"""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:
|
def get_description(self) -> str:
|
||||||
"""Get_description."""
|
"""Get_description."""
|
||||||
return """Return the list of usernames of the users in the given group."""
|
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):
|
class GetLocaltime(Script):
|
||||||
"""GetLocaltime."""
|
"""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:
|
def get_description(self) -> str:
|
||||||
"""Get_description."""
|
"""Get_description."""
|
||||||
return """Converts a Datetime object into a Datetime object for a specific timezone.
|
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):
|
class GetProcessInfo(Script):
|
||||||
"""GetProcessInfo."""
|
"""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:
|
def get_description(self) -> str:
|
||||||
"""Get_description."""
|
"""Get_description."""
|
||||||
return """Returns a dictionary of information about the currently running process."""
|
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 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 (
|
from spiffworkflow_backend.models.script_attributes_context import (
|
||||||
ScriptAttributesContext,
|
ScriptAttributesContext,
|
||||||
)
|
)
|
||||||
|
from spiffworkflow_backend.services.authorization_service import AuthorizationService
|
||||||
|
|
||||||
# Generally speaking, having some global in a flask app is TERRIBLE.
|
# Generally speaking, having some global in a flask app is TERRIBLE.
|
||||||
# This is here, because after loading the application this will never change under
|
# 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
|
SCRIPT_SUB_CLASSES = None
|
||||||
|
|
||||||
|
|
||||||
|
class ScriptUnauthorizedForUserError(Exception):
|
||||||
|
"""ScriptUnauthorizedForUserError."""
|
||||||
|
|
||||||
|
|
||||||
class Script:
|
class Script:
|
||||||
"""Provides an abstract class that defines how scripts should work, this must be extended in all Script Tasks."""
|
"""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.",
|
+ "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
|
@staticmethod
|
||||||
def generate_augmented_list(
|
def generate_augmented_list(
|
||||||
script_attributes_context: ScriptAttributesContext,
|
script_attributes_context: ScriptAttributesContext,
|
||||||
|
@ -71,18 +87,50 @@ class Script:
|
||||||
that we created.
|
that we created.
|
||||||
"""
|
"""
|
||||||
instance = subclass()
|
instance = subclass()
|
||||||
return lambda *ar, **kw: subclass.run(
|
|
||||||
instance,
|
def check_script_permission() -> None:
|
||||||
script_attributes_context,
|
"""Check_script_permission."""
|
||||||
*ar,
|
if subclass.requires_privileged_permissions():
|
||||||
**kw,
|
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 = {}
|
execlist = {}
|
||||||
subclasses = Script.get_all_subclasses()
|
subclasses = Script.get_all_subclasses()
|
||||||
for x in range(len(subclasses)):
|
for x in range(len(subclasses)):
|
||||||
subclass = subclasses[x]
|
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
|
subclass, script_attributes_context=script_attributes_context
|
||||||
)
|
)
|
||||||
return execlist
|
return execlist
|
||||||
|
|
|
@ -93,7 +93,7 @@ class AuthenticationService:
|
||||||
+ f"?state={state}&"
|
+ f"?state={state}&"
|
||||||
+ "response_type=code&"
|
+ "response_type=code&"
|
||||||
+ f"client_id={self.client_id()}&"
|
+ f"client_id={self.client_id()}&"
|
||||||
+ "scope=openid&"
|
+ "scope=openid profile email&"
|
||||||
+ f"redirect_uri={return_redirect_url}"
|
+ f"redirect_uri={return_redirect_url}"
|
||||||
)
|
)
|
||||||
return login_redirect_url
|
return login_redirect_url
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
"""Authorization_service."""
|
"""Authorization_service."""
|
||||||
import inspect
|
import inspect
|
||||||
import re
|
import re
|
||||||
|
from dataclasses import dataclass
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
from hmac import compare_digest
|
from hmac import compare_digest
|
||||||
from hmac import HMAC
|
from hmac import HMAC
|
||||||
|
from typing import Any
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
from typing import Set
|
||||||
|
from typing import TypedDict
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import jwt
|
import jwt
|
||||||
|
@ -19,6 +23,7 @@ from SpiffWorkflow.task import Task as SpiffTask # type: ignore
|
||||||
from sqlalchemy import or_
|
from sqlalchemy import or_
|
||||||
from sqlalchemy import text
|
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.group import GroupModel
|
||||||
from spiffworkflow_backend.models.human_task import HumanTaskModel
|
from spiffworkflow_backend.models.human_task import HumanTaskModel
|
||||||
from spiffworkflow_backend.models.permission_assignment import PermissionAssignmentModel
|
from spiffworkflow_backend.models.permission_assignment import PermissionAssignmentModel
|
||||||
|
@ -45,6 +50,34 @@ class UserDoesNotHaveAccessToTaskError(Exception):
|
||||||
"""UserDoesNotHaveAccessToTaskError."""
|
"""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:
|
class AuthorizationService:
|
||||||
"""Determine whether a user has permission to perform their request."""
|
"""Determine whether a user has permission to perform their request."""
|
||||||
|
|
||||||
|
@ -75,6 +108,7 @@ class AuthorizationService:
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""Has_permission."""
|
"""Has_permission."""
|
||||||
principal_ids = [p.id for p in principals]
|
principal_ids = [p.id for p in principals]
|
||||||
|
target_uri_normalized = target_uri.removeprefix(V1_API_PATH_PREFIX)
|
||||||
|
|
||||||
permission_assignments = (
|
permission_assignments = (
|
||||||
PermissionAssignmentModel.query.filter(
|
PermissionAssignmentModel.query.filter(
|
||||||
|
@ -84,10 +118,12 @@ class AuthorizationService:
|
||||||
.join(PermissionTargetModel)
|
.join(PermissionTargetModel)
|
||||||
.filter(
|
.filter(
|
||||||
or_(
|
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
|
# to check for exact matches as well
|
||||||
# see test_user_can_access_base_path_when_given_wildcard_permission unit test
|
# 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()
|
.all()
|
||||||
|
@ -127,17 +163,15 @@ class AuthorizationService:
|
||||||
return cls.has_permission(principals, permission, target_uri)
|
return cls.has_permission(principals, permission, target_uri)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def delete_all_permissions_and_recreate(cls) -> None:
|
def delete_all_permissions(cls) -> None:
|
||||||
"""Delete_all_permissions_and_recreate."""
|
"""Delete_all_permissions_and_recreate. EXCEPT For permissions for the current user?"""
|
||||||
for model in [PermissionAssignmentModel, PermissionTargetModel]:
|
for model in [PermissionAssignmentModel, PermissionTargetModel]:
|
||||||
db.session.query(model).delete()
|
db.session.query(model).delete()
|
||||||
|
|
||||||
# cascading to principals doesn't seem to work when attempting to delete all so do it like this instead
|
# 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():
|
for group in GroupModel.query.all():
|
||||||
db.session.delete(group)
|
db.session.delete(group)
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
cls.import_permissions_from_yaml_file()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def associate_user_with_group(cls, user: UserModel, group: GroupModel) -> None:
|
def associate_user_with_group(cls, user: UserModel, group: GroupModel) -> None:
|
||||||
|
@ -155,7 +189,7 @@ class AuthorizationService:
|
||||||
@classmethod
|
@classmethod
|
||||||
def import_permissions_from_yaml_file(
|
def import_permissions_from_yaml_file(
|
||||||
cls, raise_if_missing_user: bool = False
|
cls, raise_if_missing_user: bool = False
|
||||||
) -> None:
|
) -> DesiredPermissionDict:
|
||||||
"""Import_permissions_from_yaml_file."""
|
"""Import_permissions_from_yaml_file."""
|
||||||
if current_app.config["SPIFFWORKFLOW_BACKEND_PERMISSIONS_FILE_NAME"] is None:
|
if current_app.config["SPIFFWORKFLOW_BACKEND_PERMISSIONS_FILE_NAME"] is None:
|
||||||
raise (
|
raise (
|
||||||
|
@ -169,13 +203,16 @@ class AuthorizationService:
|
||||||
permission_configs = yaml.safe_load(file)
|
permission_configs = yaml.safe_load(file)
|
||||||
|
|
||||||
default_group = None
|
default_group = None
|
||||||
|
unique_user_group_identifiers: Set[str] = set()
|
||||||
if "default_group" in permission_configs:
|
if "default_group" in permission_configs:
|
||||||
default_group_identifier = permission_configs["default_group"]
|
default_group_identifier = permission_configs["default_group"]
|
||||||
default_group = GroupService.find_or_create_group(default_group_identifier)
|
default_group = GroupService.find_or_create_group(default_group_identifier)
|
||||||
|
unique_user_group_identifiers.add(default_group_identifier)
|
||||||
|
|
||||||
if "groups" in permission_configs:
|
if "groups" in permission_configs:
|
||||||
for group_identifier, group_config in permission_configs["groups"].items():
|
for group_identifier, group_config in permission_configs["groups"].items():
|
||||||
group = GroupService.find_or_create_group(group_identifier)
|
group = GroupService.find_or_create_group(group_identifier)
|
||||||
|
unique_user_group_identifiers.add(group_identifier)
|
||||||
for username in group_config["users"]:
|
for username in group_config["users"]:
|
||||||
user = UserModel.query.filter_by(username=username).first()
|
user = UserModel.query.filter_by(username=username).first()
|
||||||
if user is None:
|
if user is None:
|
||||||
|
@ -188,26 +225,25 @@ class AuthorizationService:
|
||||||
continue
|
continue
|
||||||
cls.associate_user_with_group(user, group)
|
cls.associate_user_with_group(user, group)
|
||||||
|
|
||||||
|
permission_assignments = []
|
||||||
if "permissions" in permission_configs:
|
if "permissions" in permission_configs:
|
||||||
for _permission_identifier, permission_config in permission_configs[
|
for _permission_identifier, permission_config in permission_configs[
|
||||||
"permissions"
|
"permissions"
|
||||||
].items():
|
].items():
|
||||||
uri = permission_config["uri"]
|
uri = permission_config["uri"]
|
||||||
uri_with_percent = re.sub(r"\*", "%", uri)
|
permission_target = cls.find_or_create_permission_target(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()
|
|
||||||
|
|
||||||
for allowed_permission in permission_config["allowed_permissions"]:
|
for allowed_permission in permission_config["allowed_permissions"]:
|
||||||
if "groups" in permission_config:
|
if "groups" in permission_config:
|
||||||
for group_identifier in permission_config["groups"]:
|
for group_identifier in permission_config["groups"]:
|
||||||
group = GroupService.find_or_create_group(group_identifier)
|
group = GroupService.find_or_create_group(group_identifier)
|
||||||
cls.create_permission_for_principal(
|
unique_user_group_identifiers.add(group_identifier)
|
||||||
group.principal, permission_target, allowed_permission
|
permission_assignments.append(
|
||||||
|
cls.create_permission_for_principal(
|
||||||
|
group.principal,
|
||||||
|
permission_target,
|
||||||
|
allowed_permission,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
if "users" in permission_config:
|
if "users" in permission_config:
|
||||||
for username in permission_config["users"]:
|
for username in permission_config["users"]:
|
||||||
|
@ -218,14 +254,35 @@ class AuthorizationService:
|
||||||
.filter(UserModel.username == username)
|
.filter(UserModel.username == username)
|
||||||
.first()
|
.first()
|
||||||
)
|
)
|
||||||
cls.create_permission_for_principal(
|
permission_assignments.append(
|
||||||
principal, permission_target, allowed_permission
|
cls.create_permission_for_principal(
|
||||||
|
principal, permission_target, allowed_permission
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if default_group is not None:
|
if default_group is not None:
|
||||||
for user in UserModel.query.all():
|
for user in UserModel.query.all():
|
||||||
cls.associate_user_with_group(user, default_group)
|
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
|
@classmethod
|
||||||
def create_permission_for_principal(
|
def create_permission_for_principal(
|
||||||
cls,
|
cls,
|
||||||
|
@ -449,33 +506,48 @@ class AuthorizationService:
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_user_from_sign_in(cls, user_info: dict) -> UserModel:
|
def create_user_from_sign_in(cls, user_info: dict) -> UserModel:
|
||||||
"""Create_user_from_sign_in."""
|
"""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
|
is_new_user = False
|
||||||
user_model = (
|
user_model = (
|
||||||
UserModel.query.filter(UserModel.service == "open_id")
|
UserModel.query.filter(UserModel.service == user_info["iss"])
|
||||||
.filter(UserModel.service_id == user_info["sub"])
|
.filter(UserModel.service_id == user_info["sub"])
|
||||||
.first()
|
.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:
|
if user_model is None:
|
||||||
current_app.logger.debug("create_user in login_return")
|
current_app.logger.debug("create_user in login_return")
|
||||||
is_new_user = True
|
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(
|
user_model = UserService().create_user(
|
||||||
service="open_id",
|
|
||||||
service_id=user_info["sub"],
|
|
||||||
name=name,
|
|
||||||
username=username,
|
username=username,
|
||||||
|
service=user_info["iss"],
|
||||||
|
service_id=user_info["sub"],
|
||||||
email=email,
|
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.
|
# this may eventually get too slow.
|
||||||
# when it does, be careful about backgrounding, because
|
# when it does, be careful about backgrounding, because
|
||||||
# the user will immediately need permissions to use the site.
|
# the user will immediately need permissions to use the site.
|
||||||
|
@ -490,6 +562,212 @@ class AuthorizationService:
|
||||||
# this cannot be None so ignore mypy
|
# this cannot be None so ignore mypy
|
||||||
return user_model # type: ignore
|
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:
|
class KeycloakAuthorization:
|
||||||
"""Interface with Keycloak server."""
|
"""Interface with Keycloak server."""
|
||||||
|
|
|
@ -4,6 +4,7 @@ from typing import Optional
|
||||||
from flask_bpmn.models.db import db
|
from flask_bpmn.models.db import db
|
||||||
|
|
||||||
from spiffworkflow_backend.models.group import GroupModel
|
from spiffworkflow_backend.models.group import GroupModel
|
||||||
|
from spiffworkflow_backend.models.user import UserModel
|
||||||
from spiffworkflow_backend.services.user_service import UserService
|
from spiffworkflow_backend.services.user_service import UserService
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,3 +23,15 @@ class GroupService:
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
UserService.create_principal(group.id, id_column_name="group_id")
|
UserService.create_principal(group.id, id_column_name="group_id")
|
||||||
return group
|
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,
|
"time": time,
|
||||||
"decimal": decimal,
|
"decimal": decimal,
|
||||||
"_strptime": _strptime,
|
"_strptime": _strptime,
|
||||||
|
"enumerate": enumerate,
|
||||||
}
|
}
|
||||||
|
|
||||||
# This will overwrite the standard builtins
|
# This will overwrite the standard builtins
|
||||||
|
|
|
@ -414,13 +414,16 @@ class ProcessInstanceReportService:
|
||||||
)
|
)
|
||||||
|
|
||||||
if report_filter.with_tasks_assigned_to_my_group is True:
|
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:
|
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(HumanTaskModel)
|
||||||
process_instance_query = process_instance_query.join(
|
process_instance_query = process_instance_query.join(
|
||||||
GroupModel,
|
GroupModel, and_(*group_model_join_conditions)
|
||||||
and_(*group_model_join_conditions)
|
|
||||||
)
|
)
|
||||||
process_instance_query = process_instance_query.join(
|
process_instance_query = process_instance_query.join(
|
||||||
UserGroupAssignmentModel,
|
UserGroupAssignmentModel,
|
||||||
|
|
|
@ -17,7 +17,8 @@ from spiffworkflow_backend.models.task import MultiInstanceType
|
||||||
from spiffworkflow_backend.models.task import Task
|
from spiffworkflow_backend.models.task import Task
|
||||||
from spiffworkflow_backend.models.user import UserModel
|
from spiffworkflow_backend.models.user import UserModel
|
||||||
from spiffworkflow_backend.services.authorization_service import AuthorizationService
|
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 (
|
from spiffworkflow_backend.services.process_instance_processor import (
|
||||||
ProcessInstanceProcessor,
|
ProcessInstanceProcessor,
|
||||||
)
|
)
|
||||||
|
@ -38,7 +39,7 @@ class ProcessInstanceService:
|
||||||
"""Get_process_instance_from_spec."""
|
"""Get_process_instance_from_spec."""
|
||||||
try:
|
try:
|
||||||
current_git_revision = GitService.get_current_revision()
|
current_git_revision = GitService.get_current_revision()
|
||||||
except GitCommandError as ge:
|
except GitCommandError:
|
||||||
current_git_revision = ""
|
current_git_revision = ""
|
||||||
process_instance_model = ProcessInstanceModel(
|
process_instance_model = ProcessInstanceModel(
|
||||||
status=ProcessInstanceStatus.not_started.value,
|
status=ProcessInstanceStatus.not_started.value,
|
||||||
|
|
|
@ -224,10 +224,10 @@ class ProcessModelService(FileSystemService):
|
||||||
new_process_model_list = []
|
new_process_model_list = []
|
||||||
for process_model in process_models:
|
for process_model in process_models:
|
||||||
uri = f"/v1.0/process-instances/{process_model.id.replace('/', ':')}"
|
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
|
user=user, permission="create", target_uri=uri
|
||||||
)
|
)
|
||||||
if result:
|
if has_permission:
|
||||||
new_process_model_list.append(process_model)
|
new_process_model_list.append(process_model)
|
||||||
return new_process_model_list
|
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.principal import PrincipalModel
|
||||||
from spiffworkflow_backend.models.user import UserModel
|
from spiffworkflow_backend.models.user import UserModel
|
||||||
from spiffworkflow_backend.models.user_group_assignment import UserGroupAssignmentModel
|
from spiffworkflow_backend.models.user_group_assignment import UserGroupAssignmentModel
|
||||||
|
from spiffworkflow_backend.models.user_group_assignment_waiting import (
|
||||||
|
UserGroupAssignmentWaitingModel,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class UserService:
|
class UserService:
|
||||||
|
@ -21,11 +24,11 @@ class UserService:
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_user(
|
def create_user(
|
||||||
cls,
|
cls,
|
||||||
|
username: str,
|
||||||
service: str,
|
service: str,
|
||||||
service_id: str,
|
service_id: str,
|
||||||
name: Optional[str] = "",
|
|
||||||
username: Optional[str] = "",
|
|
||||||
email: Optional[str] = "",
|
email: Optional[str] = "",
|
||||||
|
display_name: Optional[str] = "",
|
||||||
) -> UserModel:
|
) -> UserModel:
|
||||||
"""Create_user."""
|
"""Create_user."""
|
||||||
user_model: Optional[UserModel] = (
|
user_model: Optional[UserModel] = (
|
||||||
|
@ -41,8 +44,8 @@ class UserService:
|
||||||
username=username,
|
username=username,
|
||||||
service=service,
|
service=service,
|
||||||
service_id=service_id,
|
service_id=service_id,
|
||||||
name=name,
|
|
||||||
email=email,
|
email=email,
|
||||||
|
display_name=display_name,
|
||||||
)
|
)
|
||||||
db.session.add(user_model)
|
db.session.add(user_model)
|
||||||
|
|
||||||
|
@ -55,6 +58,7 @@ class UserService:
|
||||||
message=f"Could not add user {username}",
|
message=f"Could not add user {username}",
|
||||||
) from e
|
) from e
|
||||||
cls.create_principal(user_model.id)
|
cls.create_principal(user_model.id)
|
||||||
|
UserService().apply_waiting_group_assignments(user_model)
|
||||||
return user_model
|
return user_model
|
||||||
|
|
||||||
else:
|
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.
|
# Returns true if the current user is logged in.
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def has_user() -> bool:
|
def has_user() -> bool:
|
||||||
"""Has_user."""
|
"""Has_user."""
|
||||||
return "token" in g and bool(g.token) and "user" in g and bool(g.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
|
@staticmethod
|
||||||
def current_user() -> Any:
|
def current_user() -> Any:
|
||||||
"""Current_user."""
|
"""Current_user."""
|
||||||
|
@ -117,20 +88,6 @@ class UserService:
|
||||||
)
|
)
|
||||||
return g.user
|
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
|
@staticmethod
|
||||||
def get_principal_by_user_id(user_id: int) -> PrincipalModel:
|
def get_principal_by_user_id(user_id: int) -> PrincipalModel:
|
||||||
"""Get_principal_by_user_id."""
|
"""Get_principal_by_user_id."""
|
||||||
|
@ -173,8 +130,57 @@ class UserService:
|
||||||
@classmethod
|
@classmethod
|
||||||
def add_user_to_group(cls, user: UserModel, group: GroupModel) -> None:
|
def add_user_to_group(cls, user: UserModel, group: GroupModel) -> None:
|
||||||
"""Add_user_to_group."""
|
"""Add_user_to_group."""
|
||||||
ugam = UserGroupAssignmentModel(user_id=user.id, group_id=group.id)
|
exists = (
|
||||||
db.session.add(ugam)
|
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()
|
db.session.commit()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
|
@ -41,7 +41,7 @@ class BaseTest:
|
||||||
if isinstance(user, UserModel):
|
if isinstance(user, UserModel):
|
||||||
return user
|
return user
|
||||||
|
|
||||||
user = UserService.create_user("internal", username, username=username)
|
user = UserService.create_user(username, "internal", username)
|
||||||
if isinstance(user, UserModel):
|
if isinstance(user, UserModel):
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
@ -324,13 +324,9 @@ class BaseTest:
|
||||||
permission_names: Optional[list[str]] = None,
|
permission_names: Optional[list[str]] = None,
|
||||||
) -> UserModel:
|
) -> UserModel:
|
||||||
"""Add_permissions_to_user."""
|
"""Add_permissions_to_user."""
|
||||||
permission_target = PermissionTargetModel.query.filter_by(
|
permission_target = AuthorizationService.find_or_create_permission_target(
|
||||||
uri=target_uri
|
target_uri
|
||||||
).first()
|
)
|
||||||
if permission_target is None:
|
|
||||||
permission_target = PermissionTargetModel(uri=target_uri)
|
|
||||||
db.session.add(permission_target)
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
if permission_names is None:
|
if permission_names is None:
|
||||||
permission_names = [member.name for member in Permission]
|
permission_names = [member.name for member in Permission]
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
"""Test_authentication."""
|
"""Test_authentication."""
|
||||||
|
import base64
|
||||||
|
|
||||||
|
import jwt
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
from flask.testing import FlaskClient
|
from flask.testing import FlaskClient
|
||||||
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
|
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
|
||||||
|
@ -44,13 +47,16 @@ class TestFlaskOpenId(BaseTest):
|
||||||
client: FlaskClient,
|
client: FlaskClient,
|
||||||
with_db_and_bpmn_file_cleanup: None,
|
with_db_and_bpmn_file_cleanup: None,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
"""Test_get_token."""
|
||||||
|
code = "testadmin1:1234123412341234"
|
||||||
|
|
||||||
"""It should be possible to get a token."""
|
"""It should be possible to get a token."""
|
||||||
code = (
|
backend_basic_auth_string = code
|
||||||
"c3BpZmZ3b3JrZmxvdy1iYWNrZW5kOkpYZVFFeG0wSmhRUEx1bWdIdElJcWY1MmJEYWxIejBx"
|
backend_basic_auth_bytes = bytes(backend_basic_auth_string, encoding="ascii")
|
||||||
)
|
backend_basic_auth = base64.b64encode(backend_basic_auth_bytes)
|
||||||
headers = {
|
headers = {
|
||||||
"Content-Type": "application/x-www-form-urlencoded",
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
"Authorization": f"Basic {code}",
|
"Authorization": f"Basic {backend_basic_auth.decode('utf-8')}",
|
||||||
}
|
}
|
||||||
data = {
|
data = {
|
||||||
"grant_type": "authorization_code",
|
"grant_type": "authorization_code",
|
||||||
|
@ -59,3 +65,13 @@ class TestFlaskOpenId(BaseTest):
|
||||||
}
|
}
|
||||||
response = client.post("/openid/token", data=data, headers=headers)
|
response = client.post("/openid/token", data=data, headers=headers)
|
||||||
assert response
|
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,
|
with_super_admin_user: UserModel,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test_can_save_process_instance_metadata."""
|
"""Test_can_save_process_instance_metadata."""
|
||||||
initiator_user = self.find_or_create_user("initiator_user")
|
|
||||||
self.create_process_group(
|
self.create_process_group(
|
||||||
client, with_super_admin_user, "test_group", "test_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_model_source_directory="save_process_instance_metadata",
|
||||||
)
|
)
|
||||||
process_instance = self.create_process_instance_from_process_model(
|
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 = ProcessInstanceProcessor(process_instance)
|
||||||
processor.do_engine_steps(save=True)
|
processor.do_engine_steps(save=True)
|
||||||
|
|
|
@ -4,9 +4,12 @@ from flask import Flask
|
||||||
from flask.testing import FlaskClient
|
from flask.testing import FlaskClient
|
||||||
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
|
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 UserModel
|
||||||
from spiffworkflow_backend.models.user import UserNotFoundError
|
from spiffworkflow_backend.models.user import UserNotFoundError
|
||||||
from spiffworkflow_backend.services.authorization_service import AuthorizationService
|
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 (
|
from spiffworkflow_backend.services.process_instance_processor import (
|
||||||
ProcessInstanceProcessor,
|
ProcessInstanceProcessor,
|
||||||
)
|
)
|
||||||
|
@ -14,6 +17,7 @@ from spiffworkflow_backend.services.process_instance_service import (
|
||||||
ProcessInstanceService,
|
ProcessInstanceService,
|
||||||
)
|
)
|
||||||
from spiffworkflow_backend.services.process_model_service import ProcessModelService
|
from spiffworkflow_backend.services.process_model_service import ProcessModelService
|
||||||
|
from spiffworkflow_backend.services.user_service import UserService
|
||||||
|
|
||||||
|
|
||||||
class TestAuthorizationService(BaseTest):
|
class TestAuthorizationService(BaseTest):
|
||||||
|
@ -134,8 +138,339 @@ class TestAuthorizationService(BaseTest):
|
||||||
human_task.task_name, processor.bpmn_process_instance
|
human_task.task_name, processor.bpmn_process_instance
|
||||||
)
|
)
|
||||||
finance_user = AuthorizationService.create_user_from_sign_in(
|
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(
|
ProcessInstanceService.complete_form_task(
|
||||||
processor, spiff_task, {}, finance_user, human_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 [processSearchEventBus, setProcessSearchEventBus] = useState<any>(null);
|
||||||
const [processSearchElement, setProcessSearchElement] = useState<any>(null);
|
const [processSearchElement, setProcessSearchElement] = useState<any>(null);
|
||||||
const [processes, setProcesses] = useState<ProcessReference[]>([]);
|
const [processes, setProcesses] = useState<ProcessReference[]>([]);
|
||||||
|
const [displaySaveFileMessage, setDisplaySaveFileMessage] =
|
||||||
|
useState<boolean>(false);
|
||||||
|
|
||||||
const handleShowMarkdownEditor = () => setShowMarkdownEditor(true);
|
const handleShowMarkdownEditor = () => setShowMarkdownEditor(true);
|
||||||
|
|
||||||
|
@ -79,10 +81,10 @@ export default function ProcessModelEditDiagram() {
|
||||||
|
|
||||||
interface ScriptUnitTestResult {
|
interface ScriptUnitTestResult {
|
||||||
result: boolean;
|
result: boolean;
|
||||||
context: object;
|
context?: object;
|
||||||
error: string;
|
error?: string;
|
||||||
line_number: number;
|
line_number?: number;
|
||||||
offset: number;
|
offset?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [currentScriptUnitTest, setCurrentScriptUnitTest] =
|
const [currentScriptUnitTest, setCurrentScriptUnitTest] =
|
||||||
|
@ -157,6 +159,7 @@ export default function ProcessModelEditDiagram() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const navigateToProcessModelFile = (_result: any) => {
|
const navigateToProcessModelFile = (_result: any) => {
|
||||||
|
setDisplaySaveFileMessage(true);
|
||||||
if (!params.file_name) {
|
if (!params.file_name) {
|
||||||
const fileNameWithExtension = `${newFileName}.${searchParams.get(
|
const fileNameWithExtension = `${newFileName}.${searchParams.get(
|
||||||
'file_type'
|
'file_type'
|
||||||
|
@ -167,9 +170,8 @@ export default function ProcessModelEditDiagram() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const [displaySaveFileMessage, setDisplaySaveFileMessage] =
|
|
||||||
useState<boolean>(false);
|
|
||||||
const saveDiagram = (bpmnXML: any, fileName = params.file_name) => {
|
const saveDiagram = (bpmnXML: any, fileName = params.file_name) => {
|
||||||
|
setDisplaySaveFileMessage(false);
|
||||||
setErrorMessage(null);
|
setErrorMessage(null);
|
||||||
setBpmnXmlForDiagramRendering(bpmnXML);
|
setBpmnXmlForDiagramRendering(bpmnXML);
|
||||||
|
|
||||||
|
@ -204,7 +206,6 @@ export default function ProcessModelEditDiagram() {
|
||||||
// after saving the file, make sure we null out newFileName
|
// after saving the file, make sure we null out newFileName
|
||||||
// so it does not get used over the params
|
// so it does not get used over the params
|
||||||
setNewFileName('');
|
setNewFileName('');
|
||||||
setDisplaySaveFileMessage(true);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDeleteFile = (fileName = params.file_name) => {
|
const onDeleteFile = (fileName = params.file_name) => {
|
||||||
|
@ -477,6 +478,21 @@ export default function ProcessModelEditDiagram() {
|
||||||
|
|
||||||
const runCurrentUnitTest = () => {
|
const runCurrentUnitTest = () => {
|
||||||
if (currentScriptUnitTest && scriptElement) {
|
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();
|
resetUnitTextResult();
|
||||||
HttpService.makeCallToBackend({
|
HttpService.makeCallToBackend({
|
||||||
path: `/process-models/${modifiedProcessModelId}/script-unit-tests/run`,
|
path: `/process-models/${modifiedProcessModelId}/script-unit-tests/run`,
|
||||||
|
@ -485,31 +501,29 @@ export default function ProcessModelEditDiagram() {
|
||||||
postBody: {
|
postBody: {
|
||||||
bpmn_task_identifier: (scriptElement as any).id,
|
bpmn_task_identifier: (scriptElement as any).id,
|
||||||
python_script: scriptText,
|
python_script: scriptText,
|
||||||
input_json: JSON.parse(currentScriptUnitTest.inputJson.value),
|
input_json: inputJson,
|
||||||
expected_output_json: JSON.parse(
|
expected_output_json: expectedJson,
|
||||||
currentScriptUnitTest.expectedOutputJson.value
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const unitTestFailureElement = () => {
|
const unitTestFailureElement = () => {
|
||||||
if (
|
if (scriptUnitTestResult && scriptUnitTestResult.result === false) {
|
||||||
scriptUnitTestResult &&
|
let errorMessage = '';
|
||||||
scriptUnitTestResult.result === false &&
|
if (scriptUnitTestResult.context) {
|
||||||
!scriptUnitTestResult.line_number
|
errorMessage = 'Unexpected result. Please see the comparison below.';
|
||||||
) {
|
} else if (scriptUnitTestResult.line_number) {
|
||||||
let errorStringElement = null;
|
errorMessage = `Error encountered running the script. Please check the code around line ${scriptUnitTestResult.line_number}`;
|
||||||
if (scriptUnitTestResult.error) {
|
} else {
|
||||||
errorStringElement = (
|
errorMessage = `Error encountered running the script. ${JSON.stringify(
|
||||||
<span>
|
scriptUnitTestResult.error
|
||||||
Received error when running script:{' '}
|
)}`;
|
||||||
{JSON.stringify(scriptUnitTestResult.error)}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
let errorStringElement = <span>{errorMessage}</span>;
|
||||||
|
|
||||||
let errorContextElement = null;
|
let errorContextElement = null;
|
||||||
|
|
||||||
if (scriptUnitTestResult.context) {
|
if (scriptUnitTestResult.context) {
|
||||||
errorStringElement = (
|
errorStringElement = (
|
||||||
<span>Unexpected result. Please see the comparison below.</span>
|
<span>Unexpected result. Please see the comparison below.</span>
|
||||||
|
@ -580,16 +594,22 @@ export default function ProcessModelEditDiagram() {
|
||||||
</Col>
|
</Col>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const inputJson = JSON.stringify(
|
let inputJson = currentScriptUnitTest.inputJson.value;
|
||||||
JSON.parse(currentScriptUnitTest.inputJson.value),
|
let outputJson = currentScriptUnitTest.expectedOutputJson.value;
|
||||||
null,
|
try {
|
||||||
' '
|
inputJson = JSON.stringify(
|
||||||
);
|
JSON.parse(currentScriptUnitTest.inputJson.value),
|
||||||
const outputJson = JSON.stringify(
|
null,
|
||||||
JSON.parse(currentScriptUnitTest.expectedOutputJson.value),
|
' '
|
||||||
null,
|
);
|
||||||
' '
|
outputJson = JSON.stringify(
|
||||||
);
|
JSON.parse(currentScriptUnitTest.expectedOutputJson.value),
|
||||||
|
null,
|
||||||
|
' '
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
// Attemping to format the json failed -- it's invalid.
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main>
|
<main>
|
||||||
|
|
Loading…
Reference in New Issue