5
\$\begingroup\$

Update:

This is an older version of the question/script. The new version can be found here: Part 2: Create or update record via HTTP request


I have an external system that sends an HTTP request to a Jython script (in IBM's Maximo Asset Management platform).

The Jython 2.7.0 script does this:

  1. Accepts an HTTP request: http://server:port/maximo/oslc/script/CREATEWO?_lid=wilson&_lpwd=wilson&f_wonum=LWO0382&f_description=LEGACY WO&f_classstructureid=1666&f_status=APPR&f_wopriority=1&f_assetnum=LA1234&f_worktype=CM
  2. Loops through parameters:
    • Searches for parameters that are prefixed with f_ ('f' is for field-value)
    • Puts the parameters in a list
    • Removes the prefix from the list values (so that the parameter names match the database field names).
  3. Updates or creates records via the parameters in the list:
    • If there is an existing record in the system with the same work order number, then the script updates the exiting record with the parameter values from the list.
    • If there isn't an existing record, then a new record is created (again, from the parameter values from the list).
  4. Finishes by returning a message to the external system (message: updated, created, or other (aka an error)).

Can the script be improved?


from psdi.mbo import SqlFormat
from psdi.server import MXServer
from psdi.mbo import MboSet
params = list( param for param in request.getQueryParams() if param.startswith('f_') )
paramdict={} 
resp='' 
for p in params:
 paramdict[p[2:]]=request.getQueryParam(p)
woset = MXServer.getMXServer().getMboSet("workorder",request.getUserInfo())
#Prevents SQL injection
sqf = SqlFormat("wonum=:1")
sqf.setObject(1,"WORKORDER","WONUM",request.getQueryParam("f_wonum"))
woset.setWhere(sqf.format())
woset.reset()
woMbo = woset.moveFirst()
if woMbo is not None:
 for k,v in paramdict.items():
 woMbo.setValue(k,v,2L)
 resp = 'Updated workorder ' + request.getQueryParam("f_wonum")
 woset.save()
 woset.clear()
 woset.close()
else:
 woMbo=woset.add()
 for k,v in paramdict.items():
 woMbo.setValue(k,v,2L)
 resp = 'Created workorder ' + request.getQueryParam("f_wonum")
 woset.save()
 woset.clear()
 woset.close()
responseBody = resp

Note 1: Previously, there was an SQL injection vulnerability in the code. This issue has been resolved via the SQLFormat java class. The code has been updated.

Note 2: Unfortunately, I'm not able to import Python 2.7.0 libraries into my Jython implementation. In fact, I don't even have access to all of the standard python libraries.

Note 3: The acronym 'MBO' stands for 'Master Business Object' (it's an IBM thing). For the purpose of this question, a Master Business Object can be thought of as a work order record. Additionally, the constant 2L tells the system to override any MBO rules/constraints.

asked Jun 30, 2020 at 18:17
\$\endgroup\$
3
  • 1
    \$\begingroup\$ You should read: Preventing SQL Injection Attacks With Python. If you cannot use the sql library from the article then I would Google: "Regex to Prevent SQL Injection". \$\endgroup\$ Commented Jul 2, 2020 at 11:30
  • \$\begingroup\$ Ensuring that f_wonum isNumeric() will prevent the SQL attacks for this script, assuming that f_wonum is a number. \$\endgroup\$ Commented Jul 2, 2020 at 11:32
  • \$\begingroup\$ @TinMan Thanks. Actually, f_wonum is not a number. What I've done is utilized the SQLFormat Java class for this. I've updated Note 1 in the question. Cheers! \$\endgroup\$ Commented Jul 2, 2020 at 15:14

1 Answer 1

5
+50
\$\begingroup\$

Can the script be improved?

Jython is somewhat of a horror show, so improvements are limited but still possible.

List comprehensions

params = list( param for param in request.getQueryParams() if param.startswith('f_') )

can be

params = [
 param for param in request.getQueryParams()
 if param.startswith('f_')
]

Dict comprehensions

This just squeaked into Python 2.7:

paramdict = {
 p[2:]: request.getQueryParam(p)
 for p in params
}

Factor out common code

Factor out common code from these blocks:

if woMbo is not None:
 for k,v in paramdict.items():
 woMbo.setValue(k,v,2L)
 resp = 'Updated workorder ' + request.getQueryParam("f_wonum")
 woset.save()
 woset.clear()
 woset.close()
else:
 woMbo=woset.add()
 for k,v in paramdict.items():
 woMbo.setValue(k,v,2L)
 resp = 'Created workorder ' + request.getQueryParam("f_wonum")
 woset.save()
 woset.clear()
 woset.close()

For example,

if woMbo is None:
 woMbo=woset.add()
 verb = 'Created'
else:
 verb = 'Updated'
for k,v in paramdict.items():
 woMbo.setValue(k,v,2L)
resp = verb + ' workorder ' + request.getQueryParam("f_wonum")
woset.save()
woset.clear()
woset.close()

Guaranteed closure

Wrap your code in a try/finally:

woset = MXServer.getMXServer().getMboSet("workorder",request.getUserInfo())
try:
 # ...
finally:
 woset.close()

Named constants

the constant 2L tells the system to override any MBO rules/constraints

Fine; so it should get first-class treatment:

IGNORE_RULES = 2L
answered Jul 2, 2020 at 16:27
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.