The annual Acumatica Hackathon is the premiere event for many of the most avid developers in the Acumatica community. Loved for its challenging competition, networking opportunities, and intense learning experiences, developers and non-developers from all around the world descended on the Wynn Las Vegas to participate. As expected, the event offered all the challenge, fun, and learning opportunities in a tight 24-hour package.
Based on interest received from the community, Team Theta’s Gerhard van den Heever and I will share some of the inner workings of the Acumatica Centurion that contributed to being voted the winning project of the 2022 Acumatica Hackathon. In this post, I’ll share how the Centurion intercepts file uploads and some details about the Cloudmersive API’s used for AI analysis of the attachments. In a follow-up post, Gerhard will explain how the web services client app enables mass upload of files from a folder on the local PC.
Starting with the dawn of the personal computer, unsavory files have found their way onto dial-up bulletin boards, modern internet websites, and even into business systems. Whether containing virus payloads or outright objectional images, companies have fought for decades to establish policies and protocols to limit proliferation and liability of “bad files” across their systems. In the post-pandemic world where many traditional office workers have moved out of the office and now work from the corner of a bedroom or living room, fear of being caught diminishes as workers’ actions are hidden behind the walls of their homes. Even from the news headlines, an anti-encryption bill recently passed the senate judiciary committee threatening to hold corporations accountable for the files uploaded to their servers. Read more about the story here.
How can Acumatica customers protect themselves from all these risks? Team Theta answered the call by creating the Acumatica Centurion to help companies protect themselves.
The Acumatica Centurion is a project that intercepts file uploads and leverages AI from Cloudmersive via API calls to probe the file contents and perform certain administrative actions. Although Team Theta selected NSFW (Not Safe For Work) image scans, virus scans, and image resizing, the implemented actions are a small representation of the vast tools that can be applied. When an image upload fails the NSFW check, a notification is generated to prompt a real-world response to an AI detected condition.
Acumatica Centurion is available on GitHub in the 2021R2 branch of: AcumaticaHackathon/2022-Team-Theta (github.com)
While not all components will be explained here, the Acumatica Customization contains:
- Setup screen to hold the Cloudmersive API Key
- Data entry screen for placeholder records for bulk load attachments
- UploadFileMaint graph extension to intercept file uploads before persisting to the database
- Cloudmersive API call methods to facilitate file analysis
- GI, Business Event, and Notification Template to notify of monitored conditions occurring
- Various underlying DAC’s, Graphs, and Methods
- Cloudmersive Image Recognition and Virus Scan NuGet packages (A free Cloudmersive account is required to obtain a free, limited use key.)
The Guard Post
The Acumatica Centurion stands guard in a graph extension on the UploadFileMaintenace graph. By overriding Persist, we can analyze the attachment, alter the attachment if necessary, and update the attachment record with a comment reflecting the results of the analysis and/or alteration. While not written as such in the code, a failure of the virus scan or a returned classification of NSFW (Not Safe for Work) could be used to divert the attachment elsewhere or skip executing the base Persist. Since this runs in a LongOperation, you cannot simply throw a PXException to return a message to the screen. However, throwing an exception DOES cause the attachment to be lost and not saved to the database.
When looking at the code, a few focal points include:
- Line 16 – Create a Setup view to access the access the Cloudmersive API Key stored in the TT Preferences screen.
- Line 31 – Get the TTSetup record and extract the API Key.
- Line 35 – Cycle through all cached records in the Files view.
- Line 39 – Extract the attachment file into a binary array.
- Lines 46, 50, and 64 – Set the comment field based on the results of analysis.
- Lines 45 and 49 – Use the API to classify an image and return a description of the image.
- Line 55 – Use the API to change the size of the image.
- Line 61 – Use the API to scan the file for viruses.
- Line 75 – Invoke the original Persist method via the delegate.
Below is the code snippet described above:
Making the Call
Cloudmersive provides a large library of interesting API’s. Team Theta selected Cloudmersive for the NSFW component of the Image Recognition package that can be installed via NuGet. While this originally was an area that we expected Gerhard to lead development for the web service calls, he found in the documentation that .Net Framework code samples were provided which need only your own API key from your account to work. This moved development to me (Brian). With a painful awareness that our presentation time would be limited, we selected 4 API calls across 2 NuGet packages. While external API’s have played a large part in Hackathons for years, the API calls were not intended to be our focal point. Instead, they simply would add depth to the project by performing actions once we intercepted the attachments. This, of course, makes for a far more interesting demo to demonstrate a set of rules for AI analysis. The image classification to evaluate for NSFW gave us a plausible case for notifying an admin to take action, which again would add depth to the presentation.
While the code is pretty easy to read with the comments guiding you through the main points, we did made a couple of important changes to the code examples provided by Cloudmersive. First, we made these public static methods to simplify calling them. We also added parameters for the API Key and byte array that contains the data as the examples created streams from a local file. Instead, we needed to process the byte array and converted it into a System.IO.MemoryStream. Also, we returned either a string for comment modification, a bool to indicate success, or a byte array if we modified the attachment. Finally, we changed from outputting via Debug to using Acumatica’s standard PXTrace messages.
Here’s the code snippet:
Raising the Alarm
Since no centurion would be worth his salt without raising an alarm when under attack, Christina created a notification to the admin when the AI from Cloudmersive detected an image that was classified as NSFW. (For a little subtle fun, we changed that to Not Safe for Acumatica, or NSFA.) When an image returned as NSFA or “racy” by classification, we prefixed the UploadFile.Comment field value with [NSFA] or [Racy] respectively. Christina setup a Generic Inquiry (GI) to report all UploadFileRevision records with a comment beginning [NSFA]. UploadFileRevision was used because UploadFile.Comment was blank via the GI, and we found the data in UploadFileRevision consistently. After creating a Notification Template and a business event on creation of a record monitored by the GI, the admin could be notified and dispatched to investigate.
Dissecting a Centurion – Part 2
In Part 2, Gerhard gives us an under the hood look into the bulk uploader used in the demo to test web services file uploads by retrieving all files of a folder and pushing them into Acumatica. Your can read Part 2 here.
Until then, happy coding!