We recently identified a gap in our user authorization model, and we are developing a solution. First, a little background info...
We have four separate staging environments:
In DEV and ITE, we ensure that users can execute only objects that belong to their particular project, and we control this using a naming convention. All objects begin with a three-character project prefix. Users belong to one or more user group associated with these project prefixes. Each user group grants rights to work with objects of that project prefix.
In PROD, normal users are not allowed to execute objects. Instead, objects must be scheduled using a JSCH object, and these schedule objects are executed automatically as an administrative user when they are deployed to production. This administrative user has broad authorizations in the production system, because it must be able to execute objects belonging to any project prefix. In production, almost all tasks run as this administrative user. Up until now, we have not checked the scripting tabs of objects being deployed to PROD to ensure that users are not doing things the should not. We recently discovered that some users have been using such statements to execute objects outside of their project. These statements — e.g., ACTIVATE_UC_OBJECT, would fail in DEV or ITE because they would be run as ordinary users that lack broad access rights; but in PROD, because the calling tasks are started automatically as an administrative user, these statements essentially run unchecked.
We would ideally prefer to solve this problem by executing the schedules are project-specific users — technical (non-personal) users that have limited execution authority for a specific project prefix, but no broader access rights. Unfortunately, until the Automation Engine provides a mechanism for user impersonation, this approach will not be feasible.
So until this User impersonation enhancement is delivered, we need to make sure users are not inserting ACTIVATE_UC_OBJECT statements into their objects that run objects to which they would normally not have access. This means during a deployment to production, we must check, line-by-line, the content of the scripting tabs of all relevant object types, look for ACTIVATE_UC_OBJECT statements, and validate that the name of the executable objects referenced in such statements begin with the same project prefix as the calling object.
I already had a script that extracted the scripting lines of z/OS jobs, parse_zOS_jobs_in_transport_case.sh, so I took the code from this script and merged it into the more general transport case parsing script, parse_transport_case.sh. Version 0.7 of this script, posted earlier today, is now capable of printing all scripting tab content of relevant objects — objects with one or more scripting tab. These are: CALL, EVNT, JOBF, JOBG, JOBI, JOBP, JOBS, JSCH, and SCRI.
I wrote a separate script, validate_obj_script.sh, that parses the scripting tab content and performs the required validation of ACTIVATE_UC_OBJECT statements. I will post this script once it is complete. I realized earlier today that I still have some work to do before it’s ready, because the scope of potentially problematic scripting statements is much broader than just ACTIVATE_UC_OBJECT statements.
AE scripting statements that could cause harm include:
There is no way to allow users to use most of these statements without also granting them the access necessary to accidentally or intentionally interfere with tasks belonging to other projects.
I categorized these scripting statements based on what they change and how they are specified. I also listed how frequently they are used in our production environment.
I added the new validate_obj_script.sh script. Based on how the various script elements work, and how frequently they are used, I decided to handle them as follows:
I think I figured it out. This SQL query will list the details of all workflow tasks that have a pre/post-condition that execute an object.
with task_conditions as(select * from OHinner join JPP on JPP_OH_Idnr = OH_Idnrinner join JPOV on JPOV_OH_Idnr = OH_Idnrinner join JPPO on JPPO_OH_Idnr = OH_Idnr)select OH_Name,OH_Title,JPP_Alias,JPP_Object,JPP_LNR,JPP_Row,JPP_Col,case JPOV_Location when 1 then 'Pre-conditions'when 2 then 'Post-conditions' end as Location,cast(JPOV_Value as VARCHAR2(200)) as Obj_to_Execfrom task_conditionswhere 1=1and JPPO_JPP_Lnr = JPP_Lnrand JPOV_JPP_Lnr = JPP_Lnrand JPOV_JPPO_Lnr = JPPO_Lnrand JPPO_Location = JPOV_Locationand JPPO_CarName = 'EXECUTE OBJECT'and JPPO_Type = 'A'and JPOV_Type = 'V'and JPOV_Value is not nulland JPOV_Value not like '##%'and JPOV_VName = 'XC_P01'
Extracting this information from a transport case unload file would be a bit involved because of the multiple levels of abstraction between the workflow, task, condition, and action.
Update: After a bit more testing, I got a better understanding of the relationships and cleaned up the query. It works reliably now.
Here is a bit of XSLT that will generate an HTML table listing all EXECUTE OBJECT actions defined in task pre- or post-conditions of workflows in an XML export file.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"> <!-- Idendtity template --> <xsl:template match="/"> <html> <body> <h2>Workflows with <i>EXECUTE OBJECT</i> actions defined in pre- or post-conditions.</h2> <table border="1"> <tr bgcolor="#66ccff"> <th>Workflow</th> <th>Running #</th> <th>Row</th> <th>Column</th> <th>Object</th> <th>Pre/Post</th> <th>Object to execute</th> </tr> <xsl:for-each select="/uc-export/JOBP/JOBP/JobpStruct/task//conditions//action[@id='EXECUTE OBJECT']/params/param[@name='XC_P01']"> <tr> <td> <xsl:value-of select="ancestor::JOBP/../@name"/> </td> <td> <xsl:value-of select="ancestor::task/@Lnr"/> </td> <td> <xsl:value-of select="ancestor::task/@Row"/> </td> <td> <xsl:value-of select="ancestor::task/@Col"/> </td> <td> <xsl:value-of select="ancestor::task/@Object"/> </td> <td> <xsl:value-of select="name(ancestor::conditions/..)"/> </td> <td> <xsl:value-of select="@value"/> </td> </tr> </xsl:for-each> </table> </body> </html> </xsl:template></xsl:stylesheet>
Here is the output, when run against the attached XML export of a JOBP object.