= 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.) 1. 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): 1. 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''' ||<:-9> '''Roles in LC1''' ||<:-9> '''Roles in LC2''' || || ||<:-3> ''All Groups'' ||<:-3> ''Gem'' ||<:-3> ''Axpo'' ||<:-3> ''All Groups'' ||<:-3> ''Gem'' ||<:-3> ''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. * Login as U_LC1_All in contract LC1 and group Gem * Create a new drawing/revision * Create a new comment and associate with revision the just created * Try to update all test drawings/revisions in contract LC1 * Ensure that only the appropriate test drawings can be seen and updated * Ensure that only the appropriate buttons are enabled * Try to delete uploaded files in contract LC1 * Ensure that files only from the appropriate test revisions can be deleted * Try to upload files in contract LC1 * Ensure that files only to the appropriate test revisions can be uploaded * Try to update all test comments in contract LC1 * Ensure that only the appropriate test comments can be seen and updated * Ensure that only the appropriate buttons are enabled * Login as U_LC1_All in contract LC2 and group Gem * Do the same tests as above but with drawings and comments in LC2 * Login as U_LC1_Gem in contract LC1 and group Gem * Do the same tests as above * Login as U_LC1_Gem in contract LC2 and group Gem * Do the same tests as above but with drawings and comments in LC2 === Testing Contract/Group Mismatches === Test operations, where the user's current contract/group settings is NOT the same as of the object working on. * Login as U_LC1_All in contract LC1 and group Gem * Try to update all test drawings/revisions in contract LC2 * Ensure that only the appropriate test drawings can be seen and updated * Ensure that only the appropriate buttons are enabled * Try to delete uploaded files in contract LC2 * Ensure that files only from the appropriate test revisions can be deleted * Try to upload files in contract LC2 * Ensure that files only to the appropriate test revisions can be uploaded * Try to update all test comments in contract LC2 * Ensure that only the appropriate test comments can be seen and updated * Ensure that only the appropriate buttons are enabled * Try to search for drawings in contract Mgt * Ensure that drawings in contract Mgt can not be found * Login as U_LC1_All in contract LC2 and group Gem * Do the same tests as above but with drawings and comments in LC1 * Login as U_LC1_Gem in contract LC1 and group Gem * Do the same tests as above * Login as U_LC1_Gem in contract LC2 and group Gem * Do the same tests as above but with drawings and comments in LC1 * Add user U_LC1_All to the subscriber list of comments C_Mgt_Gem and C_Mgt_No * Ensure that all of these contracts can be found in search contract * Ensure that on comment detail only the appropriate buttons are enabled * Ensure that all of the associated drawings can be found in search drawings * Ensure that on drawing detail only the appropriate buttons are enabled * Add user U_LC1_Gem to the subscriber list of comments C_LC1_Axp, C_Mgt_Gem and C_Mgt_No * Ensure that all of these contracts can be found in search contract * Ensure that on comment detail only the appropriate buttons are enabled * Ensure that all of the associated drawings can be found in search drawings * Ensure that on drawing detail only the appropriate buttons are enabled === 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. * Login as U_LC1_All in contract LC1 and group Gem * Display comments C_Mgt_Gem and C_Mgt_No * Try to update the comments by entering the update URL manually * Display drawings associated with comments C_Mgt_Gem and C_Mgt_No * Try to update the drawing by entering the update URL manually * Try to update the revision by entering the update URL manually * Try to delete uploaded file by entering the update URL manually * Try to upload file by entering the update URL manually * Try to delete revision by entering the update URL manually * Try to delete drawing by entering the update URL manually * Login as U_LC1_Gem in contract LC1 and group Gem * Do the same tests as above with comments C_LC1_Axp, C_Mgt_Gem and C_Mgt_No == Implementation for Performing Workflow Steps == 1. The normal revision update works as before (but requires the document/revision update role. 1. 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 1. (Un-)Marking tasks as obsolete 1. (Un-)Marking task notes as obsolete 1. Basket functions for setting workflow steps for revisons 1. Download ZIP file