Poor Mans Security – fudging user authorisations in CRS692

Ok, been a wee while since my last post and lots of exciting things have been happening. Got the new firewall bedded down, finally added ‘automatic’ link redundancy to our WAN through the addition of OSPF, preparing a phone system upgrade, new more comprehensive DR plans, not to mention the plethora of other little bits of pieces – things are finally coming together.

This post was originally planned to be around BUS100 and importing budgets, sadly what I expected to be a 20 hours of coding turned in to a marathon effort involving my last 4 weekends without a satisfactory solution…so instead, we’ll talk about poor mans security…

The Problem:

Our Account Manager doesn’t like the idea of the people paying the bills being able to update and commit bank account details especially when EFTs are involved. We are pretty lucky in our organisation that there is a fairly high level of well deserved trust, however the AP staff being able to change the bank accounts in CRS692 isn’t good.

Now our Account Manager wants our AP staff to be able to update the bank details, but when they do so the status should change to 10 – so no payments can be made. Then another specifically designated person will need to change the status back to 20 before any more EFTs can be put through.

Standard M3 will not do this – Lawsons option is PFI. A quick chat to our Lawson Account Manager and we discover (unsurprisingly) that PFI was several zeros more expensive than we could justify for this particular problem and there wasn’t an immediate need for PFI in other areas.

So, another jscript was born. Before you cry out that this script can be circumvented fairly easily, yes, yes it can – I’m not aware of a way to stop people changing their customisations against a specific panel. However this is a little security through obscurity which has been deemed ‘adequate’ for the time being. I am certainly open to other suggestions on how we can address this issue properly 🙂

This script expands on a previous post “Validating Bank Account Details – Cancelling the Savehttps://potatoit.wordpress.com/2011/02/06/validating-bank-account-details-%E2%80%93-cancelling-the-save/ adding functionality which will disable the Status ComboBox in CRS692/E unless you are a specific user AND when Next is pressed it will change the Status to 10 if there was a change AND the user isn’t one of our authorised users. The script is smart enough to allow the user to Next through the screen when there are no changes without modifying the status.

Without further a-do the code

import System;
import System.Windows;
import System.Windows.Controls;
import MForms;

import Mango.Services;

package MForms.JScript
{
	class ValidateBankDetails_012
	{
		var giicInstanceController : IInstanceController = null;    // this is where we will store the IInstanceController to make it available to the rest of the class
		var ggrdContentGrid : Grid = null;                          // this is the Grid that we get passed by the Init()

        var gstrOriginalBank : String = null;       // store the original 'Bank' component of the bank account
        var gstrOriginalBranch : String = null;     // store the original 'Branch' component of the bank account
        var gstrOriginalAccount : String = null;    // store the original 'Account' component of the bank account
        var gstrOriginalSuffix : String = null;     // store the original 'Suffix' component of the bank account

		var tbBank : TextBox = null;                // here we cache the Bank TextBox
		var tbBranch : TextBox = null;              // here we cache the Branch TextBox
		var tbAccount : TextBox = null;             // here we cache the Account TextBox
		var tbSuffix : TextBox = null;              // here we cache the Suffix TextBox

        var cmbStatus : ComboBox = null;            // here we cache the Status ComboBox

		public function Init(element: Object, args: Object, controller : Object, debug : Object)
		{
			giicInstanceController = controller;                // save the controller to a more accessible variable
			ggrdContentGrid = controller.RenderEngine.Content;  // save the Content Grid to a more accessbile variable
			
            initSetup(giicInstanceController);                  // go out and do some of our setup

			giicInstanceController.add_Requesting(OnRequesting);    // add our event so we can actually respond to user interaction
		}
		
        // this little function will go out and retrieve the TextBoxes
        // and ComboBox that we are interested in
        private function retrieveTextBoxes()
        {
            try
            {
                ggrdContentGrid = giicInstanceController.RenderEngine.Content;  // we have some issues during testing navigating backwards and fowards through panels, so we retrieve our content object here again
			    tbBank = ScriptUtil.FindChild(ggrdContentGrid, "W1BF02");       // Bank
			    tbBranch = ScriptUtil.FindChild(ggrdContentGrid, "W2BF04");     // Branch
			    tbAccount = ScriptUtil.FindChild(ggrdContentGrid, "W3BF07");    // Account
			    tbSuffix = ScriptUtil.FindChild(ggrdContentGrid, "W4BF03");     // Suffix
                cmbStatus = ScriptUtil.FindChild(ggrdContentGrid, "WWSTAT");    // Status
            }
            catch(ex)
            {
                MessageBox.Show("retrieveTextBoxes Exception: " + ex.message);
            }
        }

        private function initSetup(aiicInstanceController : IInstanceController)
        {
            try
            {
                giicInstanceController = aiicInstanceController;

                // retrieve the TextBoxes and Combobox that we will be working with
                retrieveTextBoxes();

                if(null != tbBank)
                {
                    gstrOriginalBank = tbBank.Text;         // get the 'original' value of the Bank
                }
                if(null != tbBranch)
                {
                    gstrOriginalBranch = tbBranch.Text;     // get the 'original' value of the Branch
                }
                if(null != tbAccount)
                {
                    gstrOriginalAccount = tbAccount.Text;   // get the 'original' value of the Account
                }
                if(null != tbSuffix)
                {
                    gstrOriginalSuffix = tbSuffix.Text;     // get the 'original' value of the Suffix
                }

                if(null != cmbStatus)
                {
                    // if the user isn't jbloggs or jdoe then disable the status ComboBox
                    if((0 != String.Compare(ApplicationServices.UserContext.UserName, "jbloggs", true)) && (0 != String.Compare(ApplicationServices.UserContext.UserName, "jdoe", true)))
                    {
                        cmbStatus.IsEnabled = false;
                    }
                }
            }
            catch(ex)
            {
                MessageBox.Show("initSetup() Exception " + ex.message);
            }
        }
		
        // clean up in this case is pretty basic - remove our event
        private function cleanpUp()
        {
            try
            {
                giicInstanceController.remove_Requesting(OnRequesting);
            }
            catch(exClose)
            {
                MessageBox.Show("cleanpUp() exception when removing requesting Event " + exClose.message);
            }
        }

		public function OnRequesting(sender: Object, e: CancelRequestEventArgs)
		{
			try
			{
				if(e.CommandType == MNEProtocol.CommandTypeKey)     // we're looking for a key event
				{
					if(e.CommandValue == MNEProtocol.KeyEnter)      // specifically we're looking the enter key event
					{
                        try
                        {
						    if(true == giicInstanceController.RenderEngine.PanelHeader.EndsWith("CRS692/E"))   // are we on panel E?
						    {
                                retrieveTextBoxes();
                                if(true == haveWeChangedAnyDetails())       // have the bank account details been changed?
                                {
							        if(false == validBankAccount())         // is the bank account valid?
							        {
								        e.Cancel = true;                    // it wasn't valid so cancel the request
							        }
                                    else
                                    {
                                        if(null != cmbStatus)
                                        {
                                            // we changed our account, if the user isn't jbloggs or jdoe then we need to change the status to 10 (which is index 0 in the ComboBox)  We can do this even when the ComboBox is disabled
                                            if((0 != String.Compare(ApplicationServices.UserContext.UserName, "jbloggs", true)) && (0 != String.Compare(ApplicationServices.UserContext.UserName, "jdoe", true)))
                                            {
                                                cmbStatus.SelectedIndex = 0;
                                            }
                                        }
                                    }
                                }
                                if(false == e.Cancel)
                                {
                                    cleanpUp();
                                }
						    }
                        }
                        catch(exKeyEnter)
                        {
                            MessageBox.Show("KeyEnter Exception: " + exKeyEnter.message);
                        }

					}
					else if( (e.CommandValue == MNEProtocol.KeyF3) || (e.CommandValue == MNEProtocol.KeyF03))
					{
                        try
                        {
                            giicInstanceController.remove_Requesting(OnRequesting);
                        }
                        catch(exClose)
                        {
                            MessageBox.Show("F3 pressed, exception when removing requesting Event " + exClose.message);
                        }
						
					}
				}
			}
			catch(ex)
			{
			    MessageBox.Show(ex.message);
			}
		}
		
        // check to see if two strings are equal (ignoring case)
        // this includes if they are both Null or Empty
        // return true if they are the same
        private function areStringsEqual(astrString1 : String, astrString2 : String)
        {
            var bResult : boolean = false;

            if( (false == String.IsNullOrEmpty(astrString1)) && (false == String.IsNullOrEmpty(astrString2)) )
            {
                if(0 == String.Compare(astrString1, astrString2, true))
                {
                    bResult = true;
                }
            }
            else
            {
                if( (true == String.IsNullOrEmpty(astrString1)) && (true == String.IsNullOrEmpty(astrString2)) )
                {
                    bResult = true;
                }
            }

            return(bResult);
        }

        // check to see if we have changed any details 
        private function haveWeChangedAnyDetails()
        {
            var bResult : boolean = false;

            // compare our existing TextBoxes against the original values
            if(false == areStringsEqual(gstrOriginalBank, tbBank.Text))
            {
                bResult = true;
            }
            if(false == areStringsEqual(gstrOriginalBranch, tbBranch.Text))
            {
                bResult = true;
            }
            if(false == areStringsEqual(gstrOriginalAccount, tbAccount.Text))
            {
                bResult = true;
            }
            if(false == areStringsEqual(gstrOriginalSuffix, tbSuffix.Text))
            {
                bResult = true;
            }

            return(bResult);
        }

        // this is the heart of the class
        // we will turn the background textbox to green
        // if we have a semi-valid account
        // If we know that we don't have the correct number
        // of characters then we set the background to orange
        // and we cancel the saving of the information
		public function validBankAccount()
		{
			var bResult : boolean = false;
			try
			{
				var bError : boolean = false;       // this is where we keep track of if there is an error
				
				var bBankOK : boolean = false;
				var bBranchOK : boolean = false;
				var bAccountOK : boolean = false;
				var bSuffixOK : boolean = false;
				
				var strError : String = null;
				
                // in our world, our bank is two characters
				if(false == String.IsNullOrEmpty(tbBank.Text))
				{
					if(tbBank.Text.Length != 2)
					{
						strError = "Bank is incorrect";
						tbBank.Background = System.Windows.Media.Brushes.Orange;
						bError = true;
					}
					else
					{
						bBankOK = true;
					}
				}
				else bBankOK = true;
				
				if(true == bBankOK)
				{
					tbBank.Background = System.Windows.Media.Brushes.LightGreen;
				}
				
                // 4 characters for the branch
				if(false == String.IsNullOrEmpty(tbBranch.Text))
				{
					if(tbBranch.Text.Length != 4)
					{
						tbBranch.Background = System.Windows.Media.Brushes.Orange;
						bError = true;
					}
					else
					{
						bBranchOK = true;
					}
				}
				else bBranchOK = true;
				
				if(true == bBranchOK)
				{
					tbBranch.Background = System.Windows.Media.Brushes.LightGreen;
				}
				
                // 7 characters for the account
				if(false == String.IsNullOrEmpty(tbAccount.Text))
				{
					if(tbAccount.Text.Length != 7)
					{
						tbAccount.Background = System.Windows.Media.Brushes.Orange;
						bError = true;
					}
					else bAccountOK = true;
				}
				else bAccountOK = true;
				
				if(true == bAccountOK)
				{
					tbAccount.Background = System.Windows.Media.Brushes.LightGreen;
				}
				
                // three characters for the suffix
				if(false == String.IsNullOrEmpty(tbSuffix.Text))
				{
					if(tbSuffix.Text.Length != 3)
					{
						tbSuffix.Background = System.Windows.Media.Brushes.Orange;
						bError = true;
					}
					else bSuffixOK = true;
				}
				else bSuffixOK = true;
				
				if(true == bSuffixOK)
				{
					tbSuffix.Background = System.Windows.Media.Brushes.LightGreen;
				}
				
				if( (false == bSuffixOK) && (false == bAccountOK) && (false == bBranchOK) && (false == bBankOK) )
				{
				}
				else bResult = true;

			}
			catch(ex)
			{
				MessageBox.Show("Error validating the bank account");
			}
			if(true == bError)
			{
                // cancel the save
				bResult = false;
				MessageBox.Show("Sorry, but you haven't entered the account number correctly");
			}
			return(bResult);
		}
	}
}

 

Be aware that this is the first cut being presented for testing…so it’s entirely possible that you may find a scenario that it breaks in…

Have fun!

This entry was posted in Development, M3 / MoveX and tagged , , , , , . Bookmark the permalink.

3 Responses to Poor Mans Security – fudging user authorisations in CRS692

  1. Al says:

    Hi Scott, Personalisations can be easily turned by the (from memory) ‘disable all personalisations’ option in the menu. Another option for something like this is Smart Notifications from LBI or the equivalent product from the BI toolset you’re using. You’ll do a query on the table storing the bank account details and fire an email to the CFO whenever they change. Again not security, rather an audit trail that addresses the risk. Al.

  2. potatoit says:

    Hi Al, thanks for the suggestion – the disable personalisations only disables the ability to change the personalisations for a panel rather than the whole application?

    Smart Notifications – I had forgotten about them; no LBI here but it parallels a conversation I had on Friday after the acceptance testing; we discussed running a scheduled report to check for changes in bank account details…

    Fun, fun, fun 🙂

  3. Al says:

    Hi Scott. Disable personalisations disables jscript, formatting etc. across the LSO session. Al.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s