Legacy to containers to serverless microservices - part 3 (Updating legacy code)
Part 1 - Things to consider when containerizing an app
Part 2 - Running MySQL in a container
Part 3 - Updating legacy code
Part 4 - Further legacy site work
Previously…
We briefly explored a typical decision making process when deciding how/what to do. We ended up using a PaaS MySQL service above all others.
We’ve also discussed a WiFi review site - that can be found at https://netfinder.gen7tech.com/
(Note: I’ve disabled user sign-up/sign-in, due to the risk of the site getting hosed or bots etc. Once OIDC/OAUTH2 is implemented, that will be re-opened)
This week, we will be implementing some new features into this site. This post will not go into much detail as it’s not really the point of this series.
What features are we adding?
- Support for ‘state’ query, so we aren’t limited to QLD
- Groundwork of OIDC/OAuth2 integration (Azure B2C) -> client-side session
State query
Very straight forward:
- Add a “state” column to the hotspots table
- Add in 2x hotspots in Victoria to test
- Modify the DB query and loop logic to render and sort states:
New query:
define("ALL_SUBURBS",
"SELECT suburb, ANY_VALUE(state)
FROM hotspots
GROUP BY suburb
ORDER BY ANY_VALUE(state)");
And a php loop to set the disabled entry:
$suburb = "";
foreach($suburbs as $key => $value){
if ($suburb != $value[1]){
echo "<option selected disabled>".$value[1]."</option>";
$suburb = $value[1];
}
echo "<option value='$value[0]'>".$value[0]."</option>";
};
What it now looks like:
B2C groundwork - client-side session
Fair bit more work. What we will firstly do is migrate away from server-side in-memory sessions to a client-side cookie session.
Yes we could store session information in a DB, or in something like redis, however:
- For the DB, we’re using the cheapest, slowest option available
- For something like redis, utilizing a PaaS service we would have to pay for
So we wills store the session information in a encrypted cookie.
What we have to do
- Create helper functions to handle CRUD operations for the cookie, including encrypt and decrypt functionality
- Migrate current
$_SESSION
functions to our new helper functions
Cookie stuff
Long story short we use the following functions:
- encryptCookie
- decryptCookie
- setCookie
- updateSessionKey
- readSessionKey
- initSession
We use openssl with AES-128-CBC ciphersuite. We do something like the below:
Init:
- Session exist? If yes, use/update/whatever
- If not:
- Create a new session
- JSON encode it
- encrypt it
- Set-cookie header with updated, encrypted value
Update:
- Decrypt cookie value
- Decode into an array
- Update value
- Encode, encrypt, set-cookie etc.
Destroy:
- Set-cookie for the past
The most difficult part was the encrypt/decrypt, so they’re below and you can fill in your own blanks:
$cipher = "AES-128-CBC";
$ivlen = openssl_cipher_iv_length($cipher);
$password = "SuperSecretPassword";
$key = hash('sha256', $password);
$secret_iv = substr(hash('sha256', $password, true), 0, 32);
$iv = substr(hash('sha256', $secret_iv), 0, 16);
function encryptCookie($value){
global $cipher,$iv,$key;
if(!$value){return false;}
$ciphertext = openssl_encrypt(json_encode($value), $cipher, $key, $options=0, $iv);
return base64_encode($ciphertext);
}
function decryptCookie($value){
global $cipher,$iv,$key;
if(!$value){return false;}
$plaintext = openssl_decrypt(base64_decode($value), $cipher, $key, $options=0, $iv);
return json_decode($plaintext,true);
}
Seems to work fine in basic testing. I think there could perhaps be either a race condition, or a condition where setting the session cookie happens 1 request late. We will see.
Assuming it’s all OK, I’m going to wrap it as an actual usable module and put it on github.
Migrate existing session stuff over to our new logic
The $_SESSION
functionality is used for lots of functionality throughout the site… after a quick vscode search, we use it for:
- User firstname
- User lastname
- User email
- User ID
- User latitude
- User longitude
- Logged-in status
- Some other state-type things, e.g. if user profile has been updated
Need to firstly test the above race condition, but there’s no reason why we cannot cut across one things at a time. For example, store users first and last time in our new session provider, while the rest remains unchanged.
Improvements
We can store a backup of the session in the DB. On session write, write it async to the DB as well as locally. When someone goes to login, if they’ve already got a session we can deliver it out of the DB oppose to starting from scratch. Given we already are using a DB and we don’t require it for session read, I think that would work fine.