Category: Coding

Scratch – Touching Clones

While you can detect when a clone is touching a main sprite, there’s no way to detect if something is touching a clone. The workaround is to use broadcast messages to trigger events.

1. In the clone, create an “if touching” block to detect when it is being touched by the sprite

2. Within the if-touching block, send a broadcast message (under the “Events” blocks)

3. On the other sprite, create a block triggered by the “I receive” event for the code you want to run when this sprite touches the clone of another sprite.

4. Now a sprite executes the desired code when it is touching the clone of another sprite.

 

Git — Show Dates For Branches

We wanted to see all of the branches in a repository with the dates of the latest commit:

git for-each-ref --sort=committerdate refs/heads/ --format='%(committerdate:rfc-local) %(refname:short)'

This outputs the full date/time (you can use any of the git log date formats [relative|local|default|iso|rfc|short|raw] after committerdate; short for the date without time)

 

Decompiling Jython Class Files

Looks like Jython that is compiled into a class file can be decompiled just like a Java class (I use jd-cmd which is both simple and open source). But … you don’t get back Python. In a disaster recovery scenario, you get back something and could reconstruct your Python code from the Java-looking stuff you get back.

I don’t normally type the entire command — a quick function in your .bashrc gives you a command alias that can be used instead.

The Dog Ate My Homework

 
‘The police department acknowledged errors in the report that it said was the result of the reporting program creating a paper file.
 
“Inaccuracies in the report are unacceptable to us, and we are taking immediate steps to correct the report and to ensure the accuracy of incident reports going forward,” the statement said.’
 
Seriously? “Software problem” has become the new ‘dog ate my homework’. Do they actually expect someone to believe the software somehow turned their verbose and well-documented description of what actually happened to “PIU investigation”?! Leaving that element aside …
 
I’ve done software development for some 25 years. From a technical standpoint, incorrect data mapping happens — worked on a bank project once where they had bought a smaller company and mis-mapped previous and current address fields (+ instituted an annual fee — notice of which was sent to your previous address. Along with the first bill on a card you hadn’t used in three years, the next month’s bill with late fee and interest, the next next month’s bill … you get the idea. It was an ugly cleanup process full of angry people who generally didn’t even realize they *had* that card anymore, never mind wanting to pay 30$ a year to have said card).
 
But here’s the thing — you don’t randomly map “forced intrusion == true” to an unchecked box or find a non-null list of injuries and write “none” as a one-off. If they had one data inconsistency and wanted to claim a software bug? Suspect but technically possible — some specific data condition caused a problem generating a paper report. But *this* many errors in the reporting program impact *just* this one report? In the credit card company case, the *only* people who had their current address listed as their mailing address had never moved. 100% of the people with a previous address had that address recorded as their current address. I’ve encountered companies that had problems in specific conditions — everyone in Alaska had a bad address because someone had mapped “AR” to “Alaska” on the input form drop-down.
 
If their software had data mapped wrong in the paper file generation process, it would be generating bad paper files rather regularly. And, honestly, if the department wants to stick with this story … they need to put major time into validating all of the *other* paper files that the software has generated.
 

VSCode Keyboard Shortcuts

Scott accidentally hit the wrong key combination when attempting to comment out a block of code and brought up some clipboard viewer; and, in attempting to identify what he hit, I came across published keyboard shortcuts for Windows and Linux (I still haven’t found the key combo he hit, so I suspect he hit a desktop manager shortcut). There are a few time savers in the list:

  • Alt in combination with the up/down arrow keys moves the current line up or down
  • Shift+alt in combination with the up/down arrow keys will create a copy of the current line up or down
  • You do not need to select a line to copy/cut — using Ctrl+c or Ctrl+x with no selection will copy/cut the entire current line
  • Ctrl+K followed by the letter P copies the path to the active file
  • Ctrl+K followed by the letter R opens a new file explorer window to the location of the active file
  • Shift+Alt in combination with the letter i activates a ‘multiple cursor’ mode. Each line selected when you use the shortcut will get a cursor at the end of it

And whatever you type will be duplicated at each cursor. To leave multi-cursor mode, simply click elsewhere within the document — you’ll have one cursor where you clicked.

If you want multiple cursors on discontiguous lines, hold the Alt key as you click — a cursor will appear each location you click (not at the end of the line where you have clicked, literally the location at which you clicked — including multiple cursors per line.) If you add a cursor in the wrong location, click it again to clear that cursor.

Scratch

A few of the books I got for Anya are introductions to programming — Python, C++, and this GUI block-based system called Scratch. She likes using Python because Scott and I use it, but she absolutely adores Scratch. She has A Beginner’s Guide To Coding (Marc Scott) that I picked up from Book Outlet (I get a bonus 10$ when someone uses my referral link) for a couple of bucks. We’ve got a studio of our games online, and she’s excited to share the games with family members.

Tricks we’ve leaned so far:

  • The UI will not match a book written a few years ago 🙂 This reminded me a bit of the “Internet Directory” book I had in 1994 … an obviously silly concept today, but a completely reasonable thing in 1994 when a decent bit of the content was still modem numbers. A book about a UI … it’s a good base — providing great first projects. But it took Anya a little while to accept that, while the book quite clearly told her to click an icon that looked like this … in the intervening 18-24 months, the UI had changed. How did I know this is what you click now? I mean, other than the fact it goes into the thing that has the same function as the one your book describes? A good guess!
  • You can create variables with the same name. I am certain they are assigned some underlying UID that you never see, but if you have two variables named ‘score’ and the score doesn’t seem to be incrementing … look at your variable list.
  • Variable scope of “this sprite” and “all sprites” is straight-forward until you create clones. “This sprite” means “this clone of a sprite”. We had a “all sprites” variable for speed and all of the clones will change speed each time a new clone pops in. This is cool if it’s what you want to do. I’ve also created variables scoped to “this sprite” to build clones that move at different speeds.
  • You cannot, unfortunately, change a variable’s scope after you create it. You need to make a new one.
  • The “glide” motion isn’t good for sensing collision. While the glide is in progress, that’s the block that is executing. Anya has a game where a crab collects crystals while avoiding divers which uses the fact you cannot check if Thing1 is touching Thing2. The grab can move through gliding divers with impunity. If you want to detect collisions, use a loop where the X and Y coordinates are changed in small increments instead of glide. Technically, there’s no collision detection while my X coordinate is changing, but that’s such a brief time interval that you cannot effectively avoid bumping into the other sprite while it moves.

  • You can avoid the sprite being moved off of the screen to avoid collision by adding a bounce when the sprite is on the edge.

  • You need to zero out your variables when the green flag is pressed, otherwise replaying the game by clicking the green flag again produces really strange behavior (you’ve already won or lost)
  • Fractions can be used in places where they have integer examples. Specifically, you can pause for fractional seconds.
  • When using clones, hide the “base” sprite that exists in the ‘when green flag clicked’ instantiation; use a ‘show’ in the ‘when I start as a clone’ block. Otherwise you have one sprite sitting at the edge of the screen

  • “My Blocks” is used to build functions. In Anya’s Simon Says… game, we use the pseudorandom number generator to select “Simon’s” instructions and call a block based on the generated number.

Filtering HTML Drop-down

I’ve got a few drop-downs that I’ve added filtering on the drop-down – start typing and you’ll see the options that match your string. But I needed to mirror an application functionality where you select a category and are then presented with a list of options that fit the category.

Here’s the drop-down selector for the categories

    echo "      <div class=\"row\">\n";
    echo "          <div class=\"col-md-12 col-sm-12 col-lg-12 col-xs-12\">\n";
    echo "              <div class=\"row\">\n";
    echo "                  <div class=\"row\">\n";
    echo "                      <div class=\"col-md-2 col-sm-2 col-lg-2 col-xs-2 text-left\">\n";
    echo "                          <span><strong>Animal Category:</strong></span>\n";
    echo "                      </div>\n";
    echo "                      <div class=\"col-md-10 col-sm-10 col-lg-10 col-xs-10 text-left form-group\">\n";
    echo "                          <select name=\"strAnimalType\" id=\"strAnimalType\" readonly/> \n";
    echo "                              <option class=\"NoSelection\" value=\"-----\">-----</option>\n";
    echo "                              <option class=\"Feline\" value=\"201\">Feline</option>\n";
    echo "                              <option class=\"Canine\" value=\"202\">Canine</option>\n";
    echo "                              <option class=\"Equine\" value=\"203\">Equine</option>\n";
    echo "                              <option class=\"Other\" value=\"204\">Other</option>\n";
    echo "                          </select>\n";
    echo "                      </div>\n";
    echo "                  </div>\n";

And here’s the drop-down selector I want to filter based on category — there are a lot of options. The class for each option includes the category selectors that will include the option in the drop-down.

    echo "      <div class=\"row\">\n";
    echo "          <div class=\"col-md-12 col-sm-12 col-lg-12 col-xs-12\">\n";
    echo "              <div class=\"row\">\n";
    echo "                  <div class=\"row\">\n";
    echo "                      <div class=\"col-md-2 col-sm-2 col-lg-2 col-xs-2 text-left\">\n";
    echo "                          <span><strong>Pet Breed:</strong></span>\n";
    echo "                      </div>\n";
    echo "                      <div class=\"col-md-10 col-sm-10 col-lg-10 col-xs-10 text-left form-group\">\n";
    echo "                          <select name=\"strPetBreed\" id=\"strPetBreed\" readonly/> \n";
    echo " <option value=\"-----\" class=\"selectors All\">-----</option>\n";
    echo " <option value=\"101\" class=\"selectors Feline\">Domestic Shorthair</option>\n";
    echo " <option value=\"1275\" class=\"selectors Feline\">Flame Point Siamese</option>\n";
    echo " <option value=\"1069\" class=\"selectors Equine\">Arabian</option>\n";
    echo " <option value=\"1071\" class=\"selectors Equine\">Tennessee Walking Horse</option>\n";
    echo " <option value=\"1072\" class=\"selectors Other\">Chicken</option>\n";
    echo " <option value=\"1073\" class=\"selectors Other\">Snake</option>\n";
    echo " <option value=\"1074\" class=\"selectors Canine\">Australian Shepherd</option>\n";
    echo " <option value=\"1075\" class=\"selectors Feline\">Burmese</option>\n";
    echo " <option value=\"1076\" class=\"selectors Canine\">Siberian Husky</option>\n";
    echo " <option value=\"1077\" class=\"selectors Feline\">Sphinx</option>\n";
    echo " <option value=\"1078\" class=\"selectors Other\">Rabbit</option>\n";
    echo "                          </select>\n";
    echo "                      </div>\n";
    echo "                  </div>\n";
    echo "              </div>\n";
    echo "          </div>\n";
    echo "      </div>\n";

In the JavaScript, I’ve got a quick function that repopulates the rather long drop-down menu based on the selected category

// Filter strPetBreed options based on strAnimalCategory value
$(document).ready(function () {    
    var allOptions = $('#strPetBreedoption')
    $('#strAnimalCategory').change(function () {
        $('#strPetBreed option').remove()

        var classN = $('#strAnimalCategory option:selected').prop('class');
        var optsCat = allOptions.filter('.' + classN);
        $.each(optsCat, function (i, j) {
            $(j).appendTo('#strPetBreed');
        });

        var optsAll = allOptions.filter('.All');
        $.each(optsAll, function (i, j) {
            $(j).prependTo('#strPetBreed');
        });

    });
});

Since it’s possible there are options you’d want to always appear (in my case, it’s just the “—–” to indicate no selection has been made … but there could be real items that fall into each category too), I’ve got an “All” classification that will get popped onto the top of the list.

NodeJS Unit File

For future reference, this is an example unit file for running a NodeJS server with systemd. The NodeJS code we use reads from a local MariaDB, so I’ve added a service dependency for the database server.

Create /etc/systemd/system/nodeserver.service

[Unit]
Description=SiteName Node.js Server
Requires=After=mariadb.service

[Service]
ExecStart=/path/to/binary/for/node /path/to/nodeJS/html/server.js
WorkingDirectory=/path/to/nodeJS/html
Restart=always
RestartSec=30
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=nodejs-sitename
User=web-user
Group=www-group

[Install]
WantedBy=multi-user.target

Use systemctl daemon-reload to register the new unit file, then “systemctl start nodeserver.service” to start the service. Assuming everything works properly, use “systemctl enable nodeserver.service” to have the service start on boot.

LDAP Authentication: Python Flask

This is a quick python script showing how the flask-ldap3-login module can be used to authenticate and gather user attribute values

from flask_ldap3_login import LDAP3LoginManager
from ldap3 import Tls
import ssl

config = dict()

config['LDAP_HOST'] = 'ad.example.com'

# Use SSL unless you are debugging a problem. Clear text port is 389 and tls_ctx needs to be removed from add_server call
config['LDAP_USE_SSL'] = True
config['LDAP_PORT'] = 636

# Base DN
config['LDAP_BASE_DN'] = 'dc=example,dc=com'

# User Base DN, prepended to Base DN
config['LDAP_USER_DN'] = 'ou=UserDN'

# Groups Base DN, prepended to Base DN
config['LDAP_GROUP_DN'] = 'ou=SecurityGroupDN'

# Server will be manually added to establish SSL
config['LDAP_ADD_SERVER'] = False

# Domain component of userprincipal name
config['LDAP_BIND_DIRECT_SUFFIX'] = '@example.com'

# Search scope needs to be subtree
config['LDAP_USER_SEARCH_SCOPE'] = "SUBTREE"

# Attributes to return
config['LDAP_GET_USER_ATTRIBUTES'] = ("mail", "givenName", "sn")

# Setup a LDAP3 Login Manager.
ldap_manager = LDAP3LoginManager()

# Init the mamager with the config since we aren't using an app
ldap_manager.init_config(config)

# TLS settings to establish trust without validating CA issuance chain. 
# Can use CERT_REQUIRED and ca_certs_file with path to cacerts that includes issuing chain
tls_ctx = Tls(
    validate=ssl.CERT_NONE,
    version=ssl.PROTOCOL_TLSv1,
    valid_names=[
        'ad.example.com',
    ]
)

ldap_manager.add_server(
    config.get('LDAP_HOST'),
    config.get('LDAP_PORT'),
    config.get('LDAP_USE_SSL'),
    tls_ctx=tls_ctx
)

# Validate credentials
response = ldap_manager.authenticate_direct_credentials('e0012345', 'P@s5w0rdG03sH3re')
print(response.status)
print(response.user_info)

LDAP Authentication and Authorization: PHP

Blah

<?php
    error_reporting(0);
    #=== FUNCTION ==================================================================
    #      NAME: ldapAuthenticationAndAuthorizationWithAttributes
    #      PARAMETERS:
    #                    $strLDAPHost                   String  LDAP Server URI
    #                    $strUIDAttr                    String  Schema attribute for user ID search
    #                    $strSystemUser                 String  System credential username
    #                    $strSystemPassword             String  System credential password
    #                    $strUserBaseDN                 String  User search LDAP base DN
    #                    $strLogonUserID                String  Input user ID
    #                    $strLogonUserPassword          String  Input user password
    #					 $arrayAttrsToReturn			String	Attributes to be returned
    #                    $strGroupBaseDN                String  (optional) Group search LDAP base DN
    #                    $strGroupNamingAttribute       String  (optional) Schema attribute for group search
    #                    $strMembershipAttr             String  (optional) Schema attribute for group membership
    #                    $strAuthGroup                  String  (optional) Group name
    #     DESCRIPTION: Verify authentication and authorization against AD server.a
    #
    #     RETURNS: array(BindReturnCode, Authorized, array(returnValues))
    #                        BindReturnCode:    -1 indicates LDAP connection failure, -2 indicates system account auth failed, -3 indicates user auth not attempted, >=0 is IANA-registered resultCode values (https://www.iana.org/assignments/ldap-parameters/ldap-parameters.xml#ldap-parameters-6)
    #							NOTE: 0 is successful authentication in IANA-registered resultCode
    #                        Authorized:        0 authorization not attempted, -1 is not a member of the located group, 1 is member of the located group
    #						arrayUserAttributeValues	Array with values of $arrayAttrsToReturn
    #
    #     USAGE: $arrayUserAuthorized = ldapAuthenticationAndAuthorizationWithAttributes("ldaps://ad.example.com","sAMAccountName","ldapquery@example.com", "Sy5t3mP@ssw0rdG03sH3re", "ou=example,dc=example,dc=com", $strInputUserName, $strInputUserPassword, array('givenName', 'sn'), "ou=securitygroups,dc=example,dc=com","cn", "member", "LJRTestGroup")
    #===============================================================================
    function ldapAuthenticationAndAuthorizationWithAttributes($strLDAPHost,$strUIDAttr, $strSystemUser, $strSystemPassword, $strUserBaseDN, $strLogonUserID, $strLogonUserPassword, $arrayAttrsToReturn, $strGroupBaseDN=null, $strGroupNamingAttribute=null, $strMembershipAttr=null, $strAuthGroup=null){
        $arrayAuthResults = array();
        $arrayUserAttributeValues = array();
        // Validate password is not null, otherwise directory servers implementing unauthenticated bind (https://tools.ietf.org/html/rfc4513#section-5.1.2) will return 0 on auth attempts with null password
        if( strlen($strLogonUserPassword) < 1){
            $arrayAuthResults['BindReturnCode'] = -3;
            $arrayAuthResults['Authorized'] = -1;
        }
        else{
            // Connect to the LDAP directory for system ID queries
            $systemDS = ldap_connect($strLDAPHost);
            ldap_set_option($systemDS, LDAP_OPT_PROTOCOL_VERSION, 3);

            if ($systemDS) {
                // Bind with the system ID and find $strLogonUserID FQDN
                $systemBind = ldap_bind($systemDS, $strSystemUser, $strSystemPassword);

                if(ldap_errno($systemDS) == 0){
                    $strLDAPFilter="(&($strUIDAttr=$strLogonUserID))";
                    $result=ldap_search($systemDS,$strUserBaseDN,$strLDAPFilter, $arrayAttrsToReturn);

                    $entry = ldap_first_entry($systemDS, $result);

                    $strFoundUserFQDN= ldap_get_dn($systemDS, $entry);

                    if($strFoundUserFQDN){
                        $userDS = ldap_connect($strLDAPHost);
                        ldap_set_option($userDS, LDAP_OPT_PROTOCOL_VERSION, 3);

                        $userBind = ldap_bind($userDS, $strFoundUserFQDN, $strLogonUserPassword);
                        $arrayAuthResults['BindReturnCode'] = ldap_errno($userDS);

                        ldap_close($userDS);

                        if($arrayAuthResults['BindReturnCode'] == 0){
                        	$objFoundUser = ldap_get_entries($systemDS, $result);
							for($arrayAttrsToReturn as $strAttributeName){
								$arrayUserAttributeValues[$strAttributeName] = $objFoundUser[0][$strAttributeName];

							}
							$arrayAuthResults['AttributeValues'] = $arrayUserAttributeValues;
                            //////////////////////////////////////////////////////////////////////////////////////
                            // If an auth group has been supplied, verify authorization
                            //////////////////////////////////////////////////////////////////////////////////////
                            if($strAuthGroup){
								// Escapes in DN need to be double-escaped or bad search filter error is encountered
                                $strGroupQuery = "(&($strGroupNamingAttribute=$strAuthGroup)($strMembershipAttr=" . str_replace("\\","\\\\", $strFoundUserFQDN) . "))";

                                $groupResult = ldap_search($systemDS,$strGroupBaseDN, $strGroupQuery);
                                $authorisedState = ldap_count_entries($systemDS ,$groupResult);

                                // If a group matching the filter is found, the user is authorised
                                if($authorisedState == 1){
                                    $arrayAuthResults['Authorized'] = 1;
                                }
                                // Otherwise the user is not a member of the group and is not authorised
                                else{
                                    $arrayAuthResults['Authorized'] = -1;
                                }
                            }
                            else{
                                $arrayAuthResults['Authorized'] = 0;
                            }
                            //////////////////////////////////////////////////////////////////////////////////////
                            ldap_close($systemDS);
                        }
                        // If the bind failed, the user has not logged in successfully so they cannot be authorized
                        else{
                            $arrayAuthResults['Authorized'] = -1;

                            ldap_close($systemDS);
                            ldap_close($userDS);
                        }
                    }
                    // User not found in directory
                    else{
                        $arrayAuthResults['BindReturnCode'] = 32;
                        $arrayAuthResults['Authorized'] = -1;
                    }
                }
                // system bind failed
                else{
                    $arrayAuthResults['BindReturnCode'] = -2;
                    $arrayAuthResults['Authorized'] = -1;
                    ldap_close($systemDS);
                }
            }
            // ldap connection failed
            else{
                $arrayAuthResults['BindReturnCode'] = -1;
                $arrayAuthResults['Authorized'] = -1;
            }
        }
        return $arrayAuthResults;
    }

    print "User password not supplied:\n";
    $arrayNullPassword = array();
    $arrayNullPassword = ldapAuthenticationAndAuthorizationWithAttributes("ldaps://ad.example.com","sAMAccountName","ldapquery@example.com", "Sy5t3mP@ssw0rdG03sH3re", "ou=example,dc=example,dc=com", "e0012345", '');
    var_dump($arrayNullPassword);

    print "Bad password:\n";
    $arrayBadPassword = array();
    $arrayBadPassword = ldapAuthenticationAndAuthorizationWithAttributes("ldaps://ad.example.com","sAMAccountName","ldapquery@example.com", "Sy5t3mP@ssw0rdG03sH3re", "ou=example,dc=example,dc=com", "e0012345", 'N0tTh3P@s5w0rd',"ou=SecurityGroups,dc=example,dc=com","cn", "member");
    var_dump($arrayBadPassword);

    print "\nInvalid user:\n";
    $arrayUserNotInDirectory = array();
    $arrayUserNotInDirectory = ldapAuthenticationAndAuthorizationWithAttributes("ldaps://ad.example.com","sAMAccountName","ldapquery@example.com", "Sy5t3mP@ssw0rdG03sH3re", "ou=example,dc=example,dc=com", "xe0012345", 'xDoesN0tM@tt3r');
    var_dump($arrayUserNotInDirectory);

    print "\nGood password without authorization:\n";
    $arrayUserAuthenticated = array();
    $arrayUserAuthenticated = ldapAuthenticationAndAuthorizationWithAttributes("ldaps://ad.example.com","sAMAccountName","ldapquery@example.com", "Sy5t3mP@ssw0rdG03sH3re", "ou=example,dc=example,dc=com", "e0012345", 'Us3rP@s5w0rdG035H3re|Us3rP@s5w0rdG035H3re');
    var_dump($arrayUserAuthenticated);

    print "\nGood password with authorized user:\n";
    $arrayUserAuthorized = array();
    $arrayUserAuthorized = ldapAuthenticationAndAuthorizationWithAttributes("ldaps://ad.example.com","sAMAccountName","ldapquery@example.com", "Sy5t3mP@ssw0rdG03sH3re", "ou=example,dc=example,dc=com", "e0012345", 'Us3rP@s5w0rdG035H3re|Us3rP@s5w0rdG035H3re',"ou=SecurityGroups,dc=example,dc=com","cn", "member", "cfyP_Unix_UnixUsers");
    var_dump($arrayUserAuthorized);

    print "\nGood password with unauthorized user:\n";
    $arrayUserNotAuthorized = array();
    $arrayUserNotAuthorized = ldapAuthenticationAndAuthorizationWithAttributes("ldaps://ad.example.com","sAMAccountName","ldapquery@example.com", "Sy5t3mP@ssw0rdG03sH3re", "ou=example,dc=example,dc=com", "e0012345", 'Us3rP@s5w0rdG035H3re|Us3rP@s5w0rdG035H3re',"ou=SecurityGroups,dc=example,dc=com","cn", "member", "WIN AM Team West");
    var_dump($arrayUserNotAuthorized);

    print "\nBad system account:\n";
    $arrayBadSystemCred = array();
    $arrayBadSystemCred = ldapAuthenticationAndAuthorizationWithAttributes("ldaps://ad.example.com","sAMAccountName","ldapquery@example.com", "xSy5t3mP@ssw0rdG03sH3re", "ou=example,dc=example,dc=com", "e0012345", 'Us3rP@s5w0rdG035H3re|Us3rP@s5w0rdG035H3re');
    var_dump($arrayBadSystemCred);

?>