Marcos Vainer Loeff

Managing users security from the domain and the PI System

Blog Post created by Marcos Vainer Loeff Employee on Aug 20, 2015

Introduction

 

During the preparation for the Programming Hackathon, we had to learn how to manage not only the security of each domain user account programmatically, but also new security model from PI AF.

 

Our goal was to create 250 domain users and 50 domain groups as each group had 5 members/users. Each hackathon group received an AF database in order to be able to manipulate any AF object. Only its members could view/create/edit/delete its content. The other group members couldn't view the database content.

 

The same happened for the PI Data Archive. Each group had their own PI Points while the other group members couldn't realize their existence. All hackathon participants were able to create PI Points on a single central PI Data Archive.

 

Finally, each group was able to share its data with another specific group in case their members decide to do it.  Those were the restrictions we wanted to implement programmatically. How we were able to do that?

 

Managing user and group objects from the domain

 

There are a couple of approaches to solve this problem but we've decided to use Windows PowerShell. Our code snippet creates AD objects accessing the data from some spreasheets containing the AD users and groups.

 

 

The first spreadsheet is called NetHackGroups.csv. It has the following structure to create groups:

 

GroupName
Group01
Group02
Group03
Group04
Group05

 

The powershell code snippet below creates the requested AD groups:

 

cls;
Import-Module ActiveDirectory
New-ADGroup -Name "All Hackathon Users" -Path "CN=Users,DC=osiproghack,DC=net" -Description "All Hackathon Users"  -GroupCategory Security -GroupScope Universal
Import-Csv ".\NewHackGroups.csv" | ForEach-Object {
Write-Host $_.GroupName
$groupName = "Hackathon" + $_.GroupName
$groupDescription =  $_.GroupName + " from the Programming Hackathon"
Write-Host $groupName $groupDescription
New-ADGroup -Name $groupName  -Path "CN=Users,DC=osiproghack,DC=net" -Description $groupDescription  -GroupCategory Security -GroupScope Universal 
}

 

In order to use the AD command-lets, we need to import the Active directory module. Then a group called "All Hackathon Users" is created so that all hackathon users will become members. Later, we import the spreadsheet and process each line which will be responsable for creating a group with a description on a specific AD folder (CN = Users).

 

The second spreadsheet is called NetHackUsers.csv. It has the following structure to create users and it assigns each user to its group:

 

Name,samAccountName,ParentOU,Password,Group
Hackathon User 001,hackuser001,"CN=Users,DC=osiproghack,DC=net",8M5s6Kd1,HackathonGroup01
Hackathon User 002,hackuser002,"CN=Users,DC=osiproghack,DC=net",ZmXM0NJ2,HackathonGroup01
Hackathon User 003,hackuser003,"CN=Users,DC=osiproghack,DC=net",zCyruR02,HackathonGroup01
Hackathon User 004,hackuser004,"CN=Users,DC=osiproghack,DC=net",PcQLnUg2,HackathonGroup01
Hackathon User 005,hackuser005,"CN=Users,DC=osiproghack,DC=net",q2rphYM2,HackathonGroup01
Hackathon User 006,hackuser006,"CN=Users,DC=osiproghack,DC=net",GSJJbb32,HackathonGroup02
Hackathon User 007,hackuser007,"CN=Users,DC=osiproghack,DC=net",hsknUfj2,HackathonGroup02
Hackathon User 008,hackuser008,"CN=Users,DC=osiproghack,DC=net",7IBHOiQ2,HackathonGroup02
Hackathon User 009,hackuser009,"CN=Users,DC=osiproghack,DC=net",XidlIm62,HackathonGroup02
Hackathon User 010,hackuser010,"CN=Users,DC=osiproghack,DC=net",y84FBpn2,HackathonGroup02
Hackathon User 011,hackuser011,"CN=Users,DC=osiproghack,DC=net",OYWj5tT2,HackathonGroup03
Hackathon User 012,hackuser012,"CN=Users,DC=osiproghack,DC=net",oyxEzw92,HackathonGroup03
Hackathon User 013,hackuser013,"CN=Users,DC=osiproghack,DC=net",FOPis0q2,HackathonGroup03
Hackathon User 014,hackuser014,"CN=Users,DC=osiproghack,DC=net",foqCm3W2,HackathonGroup03
Hackathon User 015,hackuser015,"CN=Users,DC=osiproghack,DC=net",5EIgg7D2,HackathonGroup03
Hackathon User 016,hackuser016,"CN=Users,DC=osiproghack,DC=net",WejAZAt2,HackathonGroup04
Hackathon User 017,hackuser017,"CN=Users,DC=osiproghack,DC=net",w3BeTEa2,HackathonGroup04
Hackathon User 018,hackuser018,"CN=Users,DC=osiproghack,DC=net",NTc8NHG2,HackathonGroup04
Hackathon User 019,hackuser019,"CN=Users,DC=osiproghack,DC=net",nt4cGLx2,HackathonGroup04
Hackathon User 020,hackuser020,"CN=Users,DC=osiproghack,DC=net",DJV6AOd2,HackathonGroup04
Hackathon User 021,hackuser021,"CN=Users,DC=osiproghack,DC=net",ejwb4SJ2,HackathonGroup05
Hackathon User 022,hackuser022,"CN=Users,DC=osiproghack,DC=net",49O5xV02,HackathonGroup05
Hackathon User 023,hackuser023,"CN=Users,DC=osiproghack,DC=net",UZpZrZg2,HackathonGroup05
Hackathon User 024,hackuser024,"CN=Users,DC=osiproghack,DC=net",vzH3lcN2,HackathonGroup05

 

 

Let's describe each column:

 

  • Name: the display name of the user.
  • SamAccountName: the account logon name or the user object is stored - in fact the legacy NetBIOS form as used in the naming notation.
  • ParentOU: specifies where the object will be created on the AD folders hierarchy.
  • Password: the user password.
  • Group: the name of the group which the created user will be member of.

 

 

Finally, the PowerShell code snippet below creates the users using the data from the spreadsheet and it assigns each user not only to a domain group but also to the group "All Hackathon Users" .

 

Import-Csv ".\NewHackUsers.csv" | ForEach-Object {
Write-Host ""
Write-Host $_.Name
Write-Host $_."samAccountName";
Write-Host $_."Password";
Write-Host $_."Group";


 $userPrincinpal = $_."samAccountName" + "@osiproghack.net"
New-ADUser -Name $_.Name `
 -Path $_."ParentOU" `
 -SamAccountName  $_."samAccountName" `
 -UserPrincipalName  $userPrincinpal `
 -AccountPassword (ConvertTo-SecureString $_."Password" -AsPlainText -Force) `
 -ChangePasswordAtLogon $false  `
 -Enabled $true


 Add-ADGroupMember $_."Group" $_."samAccountName";
Add-ADGroupMember "All Hackathon Users"  $_."samAccountName";
}

 

Managing the PI System security programmatically

 

The new security model from PI AF 2.7 introduces a similar model compared to PI Data Archive using mapping and identities as described on PI AF 2015 Release Notes:

 

"An update to the security model to make it similar to the model exposed by the PI Data Archive.  ACL’s on AF Objects are no longer being tied directly to Windows Principals, but are instead defined using AF defined identities."

 

 

Creating identities and mappings on the PI Data Archive

 

As we will have to create identities and mappings programmatically on the PI Data Archive and the PI AF Server, let's start with the first option for you to remember how this is done. Please refer to the code snippet below:

 

  private static void CreatePIServerSecurityObjs()
        {
            PIServer piServer = new PIServers()["MERCURY001"];
            piServer.Connect();

            for (int i = 1; i < 51; i++)
            {
                int k = 100 + i;
                string name = "HackathonGroup" + k.ToString().Substring(1, 2);
                if (piServer.Identities[name] == null)
                {
                    PIIdentity newIdentity = piServer.Identities.Add(name, PIIdentityType.PIIdentity);
                    newIdentity.AllowExplicitLogin = false;
                    newIdentity.AllowTrusts = false;
                    newIdentity.Description = "Identity for group " + name;
                    newIdentity.IsEnabled = true;
                    newIdentity.CheckIn();
                }

                if (piServer.IdentityMappings[name] == null)
                {
                    PIIdentityMapping newMapping = piServer.IdentityMappings.Add(name, @"OSIPROGHACK\" + name, name);
                    newMapping.Description = "Mapping for group " + name;
                    newMapping.CheckIn();
                }
            }
        }

 

First we connect to the PI Data Archive and foreach Hackathon group the program will:

 

  • Generate the name for the identity and mapping which in this case would be the same. The name for the first group would be HackathonGroup01. It was needed to add 100 to integer i before getting the last two digits in order to generate the string name  HackathonGroup01 and not HackathonGroup1.
  • With this string, the program checks if there is an identity with the generated name. If it does not exist, it will create one with proper description. The method CheckIn() needs to be called in order to save the changes.
  • A similar process happens with the mapping (PIIdentityMapping class).

 

Running the method above, it creates one identity and one mapping per group. The upcoming PI Points created by a group needs to have their security settings edited by its members (programmaticaly or using PI SMT) so that the other 49 groups won't have access to them.

 

Creating the AF databases

 

Now that the security of the PI Data Archive was handled, let's start creating our 50 databases on PI AF Server. Each database needs to be managed only by a single group. As you already know, it is pretty easy to create 50 databases using PI AF SDK:

 

        private static void CreateDbs()
        {
            PISystem piSystem = new PISystems()["MERCURY001"];
            piSystem.Connect();


            for (int i = 1; i < 51; i++)
            {
                int k = 100 + i;
                string name = "DatabaseHackGroup" + k.ToString().Substring(1, 2);
                AFDatabase db = piSystem.Databases[name];
                if (db == null)
                {
                    piSystem.Databases.Add(name);
                }
            }
            piSystem.CheckIn();


        }

 

 

 

Creating identities and mappings on the new PI AF Server 2.7

 

The code snippet for creating identities and mapping on PI AF Server is very similar compared to the PI Data Archive version:

 


        private static void CreatePISystemSecurityObjs()
        {
            PISystem piSystem = new PISystems()["MERCURY001"];
            piSystem.Connect();


            for (int i = 1; i < 51; i++)
            {
                int k = 100 + i;
                string name = "HackathonGroup" + k.ToString().Substring(1, 2);
                AFSecurityIdentity newIdentity = piSystem.SecurityIdentities[name];
                if (newIdentity == null)
                {
                    newIdentity = piSystem.SecurityIdentities.Add(name);
                    newIdentity.Description = "Identity for group " + name;
                    newIdentity.IsEnabled = true;
                    newIdentity.CheckIn();
                }


                AFSecurityMapping newMapping = piSystem.SecurityMappings[name];
                if (newMapping == null)
                {
                    System.Security.Principal.NTAccount account = new System.Security.Principal.NTAccount("OSIPROGHACK", name);
                    newMapping = piSystem.SecurityMappings.Add(name, account, newIdentity);
                    newMapping.Description = "Mapping for group " + name;
                    newMapping.CheckIn();
                }          
            }


            AFSecurityIdentity newIdentityForAll = piSystem.SecurityIdentities["AllHackUsers"];
            if (newIdentityForAll == null)
            {
                newIdentityForAll = piSystem.SecurityIdentities.Add("AllHackUsers");
                newIdentityForAll.Description = "Identity for all hackathon users";
                newIdentityForAll.IsEnabled = true;
                newIdentityForAll.CheckIn();
            }


            AFSecurityMapping newMappingForAll = piSystem.SecurityMappings["AllHackUsers"];
            if (newMappingForAll == null)
            {
                System.Security.Principal.NTAccount account = new System.Security.Principal.NTAccount("OSIPROGHACK", "All Hackathon Users");
                newMappingForAll = piSystem.SecurityMappings.Add("AllHackUsers", account, newIdentityForAll);
                newMappingForAll.Description = "Mapping for all hackathon users";
                newMappingForAll.CheckIn();
            }
        }

 

Here are the main differences between them:

 

  • When calling the method SecurityMapping.Add(), one of the inputs is an System.Security.Principal.NTAccount object whereas on the PI Data Archive version, this account was referred as a string.
  • Besides the creation of the 50 identities and 50 mappings, we have created a mapping and an identity for all hackathon users group.

 

Setting up proper security on the PI AF Server

 

Finally, the method SetPISystemSecurityObjs() will set up the PI System and AF databases security property in order to apply the desired restrictions. Please refer to the code snippet below:

 

 

        private static void SetPISystemSecurityObjs()
        {
            PISystem piSystem = new PISystems()["MERCURY001"];
            piSystem.Connect();
            AFSecurity security1 = piSystem.GetSecurity(AFSecurityItem.Default);
            security1.SetSecurityString(security1.GetSecurityString() + "|AllHackUsers:A(r,rd)", true);


            AFSecurity security2 = piSystem.GetSecurity(AFSecurityItem.SecurityIdentity);
            security2.SetSecurityString(security2.GetSecurityString() + "|AllHackUsers:A(r,rd)", true);


            AFSecurity security3 = piSystem.GetSecurity(AFSecurityItem.SecurityMapping);
            security3.SetSecurityString(security3.GetSecurityString() + "|AllHackUsers:A(r,rd)", true);




            for (int i = 1; i < 51; i++)
            {
                Console.WriteLine("Processing item " + i.ToString());
                AFDatabase groupDb = piSystem.Databases[GetDatabaseName(i)];
                IList<AFSecurity> groupSecurityObjs = new List<AFSecurity>();
                foreach (AFSecurityItem securityItem in Enum.GetValues(typeof(AFSecurityItem)))
                {
                    AFSecurity groupSecurityObj = groupDb.GetSecurity(securityItem);
                    if (groupSecurityObj != null)
                    {
                        groupSecurityObjs.Add(groupSecurityObj);
                    }
                }
                AFSecurity.AddIdentity(piSystem, piSystem.SecurityIdentities[GetIdentityName(i)], groupSecurityObjs, AFSecurityRights.All, AFSecurityRights.None, AFSecurityOperation.Merge, true);
            }
        }


        private static string GetDatabaseName(int i)
        {
            int k = 100 + i;
            string name = "DatabaseHackGroup" + k.ToString().Substring(1, 2);
            return name;
        }


        private static string GetIdentityName(int i)
        {
            int k = 100 + i;
            string name = "HackathonGroup" + k.ToString().Substring(1, 2);
            return name;
        }

 

The PI System and the 50 AF databases need to have their security settings changed.

 

Concerning the PI System, calling piSystem.GetSecurity(AFSecurityItem) will return an AFSecurity object. For each type of AFSecurityItem, you will edit the security of a different aspect of this object security. If you want to change the security mappings from the selected PI System, you shall call piSystem.GetSecurity(AFSecurityItem.SecurityMapping). The returned AFSecurity has a method called SetSecurityString() which lets you change the security string. You can call GetSecurityString() in order to view the current security string of the object before changing it.

 

Concerning each one of the AF Databases, we will get the group database and store it on groupDb variable. For each type of AFSecurityItem we will call groupDb.GetSecurity(AFSecuirtyItem) whose returned AFSecurity will be added to an IList<AFSecurity> groupSecurityObjs. Finally, AFSecurity has a static method called AddIdentity which is used to set up its security settings from each database with the groupSecurityObjs and its respective identity.

 

 

 

Conclusions

 

There are some scenarios where you need to manage a large number of users on your PI System. As the recommended security of the PI System is Windows Integrated Security which uses objects from the Active Directory, creating objects on the domain programmatically is a very interesting skill to have. After doing this procedure, PI AF SDK provides enough methods to manage the mappings and identities from the PI Data Archive and from PI AF Server in order to secure your environment properly.

 

Handling the security manually using PI System Explorer may lead to security breach caused by humans' mistakes. A safer option is to manage the security programmatically as for well tested code, security breaches are generally harder to find.

 

Hope you have found this blog post interesting and I would like to thank you for reading it!

Outcomes