Recently we had a request from an Oracle Cloud administrator who wanted to change user passwords programmatically. We came up with a solution to create a program that mimics the web requests that a normal user would make when changing their own password.
Initially, we searched for a tool that could do this, but it looked like the vendor didn’t provide a tool out of the box. In cases like this, software vendors usually provide easy-to-use Application Programming Interfaces (APIs) that can be used by third-party software developers to access internal functions.
There is an API call that would do something similar to what we wanted, but it wouldn’t work for our needs. The API call would set a one-time password that the user would need to change on first use. We needed the password to be valid and usable after the change. This ruled out the API route. So, we decided to create our own custom automation tool.
It’s important to understand the pros and cons of using a tool that mimics users’ actions.
- You get the job done.
- It’s always possible to fall back on manually doing the task if the tool no longer functions. More on this below.
- You enable automation in an area where it previously wasn’t available.
- The tool is developed against a certain software version and could need tweaking if the software is updated or patched.
As stated above, software patches can change the way the software works and temporarily make the tool unusable. In an on-premise deployment, the roll-out of patches and software updates is managed and scheduled. This means that these tools can be tested against new software versions and updated accordingly.
Using these tools against a cloud solution is riskier since the vendor can change their software at any time as they see fit. In our case, we understood the risks and we can always fall back to manually changing passwords until we have updated the tool to work against the latest cloud software version. Of course, it’s also important to build tests and assertions into the tool in order to detect anomalies and stop sending requests if something is out of the ordinary.
Now that we’ve covered the warnings, let’s look at some of the development processes. We took the following steps:
- Decide which programming language to choose
- Implement a bare-bones-first working proof of concept
- Add exception-handling and assertions
- Add command line argument parsing to make it into a dynamic fully working application.
The user mostly interacts with the software using a browser. Our approach is to monitor the network requests made by the user during normal operation and then program a web client that sends the same type of requests.
The first step is to record the web requests. There are a lot of tools that can monitor network traffic, but I like to keep things simple by using Google Chrome's built-in development tools.
Looking through the network traffic, we could see all the steps we took in the web page. Clicking on the requests showed the exact information sent by the browser to the server as well as the responses we got back from the server. Using the built-in Google Chrome “Copy as cURL” function we could get the exact web requests in an easy-to-use format that we converted into Python code HTTPS requests.
The cURL commands may not be pretty to look at, but they contain all the information we need and provide an easy way to test tweaks and changes as we can simply test them in a command terminal.
curl 'https://cloud.oracle.com/en_US/sign-in' -H 'authority: cloud.oracle.com' -H 'pragma: no-cache' -H 'cache-control: no-cache' -H 'upgrade-insecure-requests: 1' -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36' -H 'accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8' -H 'referer: https://cloud.oracle.com/home' -H 'accept-encoding: gzip, deflate, br' -H 'accept-language: en-GB,en;q=0.9,en-US;q=0.8,sv;q=0.7' -H 'cookie: s_fid=674CCBA0AC1CC120-3190671AF37A1BFB; s_cc=true; xdVisitorId=13A2mgaY3XVPIXZGDKXvbgzJyEz4_razRw8EXx-SeY5G_NE4591; atgRecVisitorId=13A2mgaY3XVPIXZGDKXvbgzJyEz4_razRw8EXx-SeY5G_NE4591; notice_preferences=2:cb8350a2759273dccf1e483791e6f8fd; notice_gdpr_prefs=0,1,2:cb8350a2759273dccf1e483791e6f8fd; cmapi_gtm_bl=; cmapi_cookie_privacy=permit 1,2,3; JSESSIONID=MOaVnW2SDo5CFMzc3vmjTzoW87DpjZABCDPpCp1wgv8yqi7My9Fu!-441120772!760515802; ORA_OCHAT=true; atgRecSessionId=1SSVtkxH-cjZI_H2be5qJEteONd4kyuNnBLG3bt9hPi_qdP5zvRN!-218549620!567767027; gpw_e24=https%3A%2F%2Fcloud.oracle.com%2Fhome; s_nr=1540110100424-Repeat; s_sq=%5B%5BB%5D%5D' --compressed
Here’s an example in Python:
The development took about a day from start to finish, and the result can be seen here:
inlumi:~ peter$ python3 oracle_cloud_password.py --datacenter em2 --identity_domain a000000 --username 'Frank' --password 'demo' --new_pass 'rockyou'
Changing password for Frank
Password for Frank successfully changed. Valid until 17-Feb-2019 6:46 PM CET.
Now we can enjoy automation where previously there was none!