Reworking DrawMGT Roles and Permissions

Permissions Problems in the Current Implementation

The current system was originally programmed with low-level library functions that use the current contract and group. The transactions have been updated so that data from other contracts/groups can be viewed/updated. Unfortunately, the permissions checking in cases where the data is not in the corrent contract is usually incorrect.

  1. The current permission system uses the current contract/group from the global session, in spite of the actual contract/group of the record (e.g. document/revision/task/etc.) being operated on.
    • Possible fix:
      • Store the objects actual contract/group in the current session frame.
      • Create a glow function that checks permissions, using the parameters
        • contractId of data being operated on
        • groupId of data being operated on
        • objectId
        • objectTypeId
        • objectSubtypeId (might be needed for task notes?)
      • Always allow BTs and FSMs to be started in spite of the user's permissions, but have the BT check if the data can be viewed/updated. A possible solution:
        • Determine FSMs and BTs that can always be started (mainly the document details and task detail FSMs/BTs)
        • Set appropriate FSMs to public in TransactionMap.php

        • Create an exception list in TransactionMap.php that lists BTs that can always be started, 'BusinessTransactionBase.newBT' don't call 'Authorizer::isAuthorized' if the BT is on the exception list.

        • Implement explicit permissions checking in the BTs, e.g. performing role checking based on the actual contract/group of the data. This method could use the function mentioned in the above point.
        • Implement optional argument 'object' to the constructor of classes extending ButtonArrayBase (e.g. CommentDetailButtons) to enable/disable buttons based on the actual data. (Set TransactionGuard with actual 'contractId', 'groupId' at the start of the 'init' method and restore TransactionGuard with values from session at the end.)

  2. For viewing tasks and their linked documents, the finite state machine will not allow instantiation of a FSM or BT if you don't have the required role in your current contract, in spite of having permission because you are a comment subscriber.
    • Some kind of exception processing will be required to handle:
      • Display of tasks when you are a subscriber (but don't otherwise have rights to view the task or its attached documents)

Additional Requirements

Requirements for Documents and Revisions

  1. Separate creating/updating of drawings/revisions from performing workflow steps. E.g. separate user roles for:
    • Viewing documents
    • Creating/updating documents/revisions (should create/update be separate?)
    • Performing workflow steps (see below):
  2. Workflow roles only let you perform a single workflow step:
    • Receiver
    • Designer
    • Checker
    • Approver
    • Release
    • Submitter - Probably allows you to do new/update/transmit submittal

Requirements for Tasks

  1. Need to have separate roles for the following operations:
    • Creating a task
    • Updating a task
    • Viewing a task
    • Adding a task note
    • Editing task notes? (currently only users withe manager role can do that)
    • Adding/removing subscribers (should disappear from detail screen?)

Proposed Implementation

Role Table Updates

Change roles according to the table follows:

Role

Permitted transactions

Notes

DView (12)

DrawingView, RevisionView

Rename View; Remove CommentView, SubmitView

DNew (30)

DrawingNew, RevisionNew

New role, remove DrawingNew, RevisionNew from roles Sbm, App1-4, Rel

DUpd (31)

DrawingUpd, RevisionUpd

New role, remove DrawingUpd, Revisionupd from roles Sbm, App1-4, Rel

CView (32)

CommentView

New role

CNew (33)

CommentNew

New role

CUpd (34)

CommentUpd

New role

Pseudo Code for Changes to the Library and Application Code

Scenario for the new/update code for the fictional class 'SomeObject'

lib/venture/lib/Authorizer.php

  class Authorizer {

    function isAuthorized( $transactionName, $userId=null, 
                           $contractId=null, $groupId=null ) 
    {
      ... existing code ...
      return $this->isTransactionAuthorized( $tranName, $userId, $contractId, $groupId );
    }

    function isTransactionAuthorized( $tranName, $userId=null, $contractId=null, $groupId=null ) {
       ...New code that uses Profiles and creates a select from Roles based on contractId and groupId
    }
  }

lib/venture/lib/BusinessTransactionBase.php

  class BusinessTransactionBase {

    function newBT( $className, ... ) {
      ...
      $transactionMap = new TransactionMap();
      if ( $transactionMap->onExceptionList( $classname ) {
        // -- Class is authorized in BT and not here
      } else {
        // -- See if class is authorized for user
        ... existing code ...
      }
      ... existing code ...
    }
  }

Business Object Class

Assumes that the SomeObject class has contractId and groupId fields:

  class SomeObjects extends SomeObjectBase {

    function isAuthorized( $transactionName, $userId, $obj, 
                           $contractId=null, $groupId=null ) {

      if ( ! isset( $contractId ) ) {
        $contractId = $obj->getValue( $contractId' );
      }
      if ( ! isset( $groupId ) ) {
        $groupId = $obj->getValue( $groupId' );
      }

      // -- Normal authorization

      $authorizer = Authorizer::get();
      if( $authorizer->isAuthorized(
          $transactionName, $userId, $contractId, $groupId ) ) {
        return true;
      }

      // -- Normal authorisation failed, apply special cases
      ... immplement special cases here, e.g. comments can be viewed by asubscriber

    }
    ...
  }

SomeObjectFMS and BTs

FSM

  class SomeObjectNewFSM extends FinstateState MachingBase { 
    ... no changes required ...
  }

Form BT

  class SomeObjectNewFormBT extends BusinessTransactionBase {
    function SomeObjectNewFormBT( $state, $depth ) {
        parent::BusinessTransactionBase( $state, $depth );
    }
  
  
    function run( $event, $state=null, $nextState=null ) {

      $transactionName = $session->getGlobal( 'transactionName' );
      $userId          = $session->getGlobal( 'userId' );
      $contractId      = $session->get( 'contractId' );
      $groupdId        = $session->getRequest( 'groupId' ); 

      if ( SomeObject::isAuthorized( $transactionName, null, 
                                     $userId, $contractId, $groupId ) ) 
      {
        ... display error/warning 'not authorized' 
        $form->setWarning( 'illegal group' ); 
        return $state; 
      }

      ... process event ...
    }

    function display() {

      if ( first time through ) {
        $userId = $session->getGlobal( 'userId' );

        if ( $updateMode ) { // 'Update' mode
          ...
          $soSet = new SomeObjectsSet();
          $someObject = $soSet->get( $someObjectId );
          $transactionName = 'SomeObjectUpd';
  
          if ( SomeObject::isAuthorized( $transactionName, 
                                         $userId, $someObject ) ) 
          {
            ... display error/warning 'not authorized' 
            return;
          }
        } else { // 'New' mode
  
          $contractId = $session->getGlobal( 'contractId' );
          $groupId = $session->getGlobal( 'groupId' );
          $transactionName = 'SomeObjectNew';
  
          if ( SomeObject::isAuthorized( $transactionName, $userId,  
                                         null, $contractId, $groupId ) ) 
          {
            ... display error/warning 'not authorized' 
            return;
          }
         
        }
        $session->set( 'transactionName', $transactionName );
        $session->set( 'contractId', $contractId );
        $session->set( 'userId', $userId );
        ... continue setup ...
      }
      ... display ...
    }
  }

New code for displaying Button Arrays

This should be in the display function of the BT:

      // -- Set buttons according to record's contractId and grouId

      $commentButtons = new CommentDetailButtons( $comment );
      ...

In ...Button.php:

     // -- constructor

    function CommentDetailbuttons( $comment=null ) {
      $this->init( $this->config, $comment );
      parent::ButtonArrayBase();
    } 

In ButtonArrayBase:

    function init( $config, $object=null ) {
       // Set transaction guard according to the object
       ... existing code ...
       // Set transaction guard back to global settings
    }

Conf BT

  class SomeObjectNewConfBT extends BusinessTransactionBase {
    ... Same updates as SomeObjectFormBT? The run function should 
    ... recheck the authorization if the user is able to change the 
    ... contractId or groupId.
  }

Testing Proposal

All changed BT should be tested:

Transaction

Operation

CommentDetailBT

Comment View

CommentNewFormBT

Comment New/Update

DrawingDelConfBT

Drawing Delete

DrawingDetailBT

Drawing View

DrawingNewFormBT

Drawing New/Update

RevisionDelConfBT

Revision Delete

RevisionNewFormBT

Revision New/Update

UploadPdfFormBT

Upload File

Test Users

Use linthal configuration, and create new test users to have the roles and only that roles in the table:

User

Roles in LC1

Roles in LC2

All Groups

Gem

Axpo

All Groups

Gem

Axpo

U_LC1_All

Vw

Com

Ap1

Vw

U_LC1_Gem

Vw

Com

Ap1

Vw

Test Drawings and Comments

Create drawings as admin user as follows:

Title

Contract

Group

Note

D_LC1_Gem

LC1

Gem

D_LC1_Axp

LC1

Axpo

D_LC1_No

LC1

-

D_LC2_Gem

LC2

Gem

D_LC2_Axp

LC2

Axpo

D_LC2_No

LC2

-

D_Mgt_Gem

Mgt

Gem

D_Mgt_No

Mgt

-

Create one revision to all drawings and upload a file to each of them.

Create comments similarly to drawings, start the titles with 'C'. Link all comments to the appropriate (same contract/group) revisions. Additionaly link all comments to a revision with different contract setting.

Testing Usual Operation

Test operations, where the user's current contract/group settings is the same as of the object working on.

Testing Contract/Group Mismatches

Test operations, where the user's current contract/group settings is NOT the same as of the object working on.

Test URL Hacks

Test operations, where the URL entered in the browser manually, to try to access not permitted data. These tests can be simulated by adding admin permissions to the user and remove it from another session, if the user is already on the detail screen. The user has all buttons enabled in this case, but all of these non-permitted operations should be denied.

Implementation for Performing Workflow Steps

  1. The normal revision update works as before (but requires the document/revision update role.
  2. Implement a new Ajax based workflow update transaction:
    • The complete date field is replace with a 'Workflow' button (assuming you have the appropriate roles for workflow step and revision in question.
    • Pressing the button initiates:
      • A popup or change to the HTML-DOM that lets you enter:
        • Complete date
        • Status
        • Initials
      • A 'doit' button that initiates an Ajax transaction that sends the fields (along with the revisionId) to a php script that validates the permissions, the input data and performs the update. E.g. the update happens in place, and the screen (or part of it) must be updated using Javascript/jQuery.

Other Items to Consider

  1. (Un-)Marking documents as obsolete
  2. (Un-)Marking tasks as obsolete
  3. (Un-)Marking task notes as obsolete
  4. Basket functions for setting workflow steps for revisons
  5. Download ZIP file

PermissionsRework (last edited 2010-04-15 13:41:20 by 183-56-240)

Copyright 2008-2014, SoftXS GmbH, Switzerland