Point by point security is a long standing feature of the PI System.  Many PI systems use the default permission model that provides a read only experience for PI world and full access to PI administrators. This approach is adequate in many cases and seems a natural fit for publishing control system data to users in your corporate directory.

 

Other times a more granular model is appropriate. Reducing the number of connections with full administrative access is recommended. We specifically recommend lowering the permissions configured for PI Interface roles.

 

Doing so means the PI System manager will need to configure permissions on a point by point basis.

 

There are times a custom tool is worthwhile in maintaining point by point security.  For instance, a custom tool can automate cloning permissions from a reference tag to all tags in the same pointsource and location1.  The tool can take care to only modify points that don't match the reference tag (or maybe you just want to log points with permissions that don't conform to the reference tag).

 

Another custom scenario is to merge the reference tag permissions to other tags in the same pointsource and location1.

 

It turns out the AF SDK offers a fairly straight forward way to build this kind of tool (if it wasn't compiled I'd probably call this a script)!

 

Many of you might already have tools that do this kind of thing.  Please consider sharing your PI security tools and scripts, here's my Tag Security Checker:

 

Imports System
Imports System.Collections.Generic
Imports System.IO
Imports System.Text
Imports OSIsoft.AF.PI
Imports OSIsoft.AF
Module TagSecurityChecker
    Public Sub Main(ByVal args As String())
        'Arg(0) is reference tag name
        'Arg(1) valid options: Clobber, Confirm (report only if missing)
        Static Dim Bar As String = "|"
        Static Dim Carat As String = "^"
        Static Dim Space As String = " "
        Dim myPIServers As New PIServers
        Dim myConnectionInfo As PIConnectionInfo = myPIServers.DefaultPIServer.ConnectionInfo
        Dim myPI As PIServer = myPIServers.DefaultPIServer
        Dim Clobber As Boolean = False
        Dim myMerge As Boolean = False
        Dim Check As Boolean = True
        Dim nargs As Integer
        nargs = args.GetUpperBound(0)
        If nargs > 0 Then
            If args(1).Length > 0 Then
                If args(1).ToUpper = "MERGE" Then myMerge = True
                If args(1).ToUpper = "CLOBBER" Then Clobber = True
            End If
            If myMerge Or Clobber Then Check = False
        End If
        'Find the reference point
        Dim rTag As String = "Sinusoid"
        If nargs > -1 Then rTag = args(0)
        Try
            Dim myPt As PIPoint = PIPoint.FindPIPoint(myPI, rTag)
            myPt.LoadAttributes(PICommonPointAttributes.PointSource, PICommonPointAttributes.Location1, PICommonPointAttributes.PointSecurity, PICommonPointAttributes.DataSecurity)
            Dim myACL, PtACL, DataACL As String
            PtACL = myPt.GetAttribute("PtSecurity").ToString
            DataACL = myPt.GetAttribute("DataSecurity").ToString
            myACL = PtACL + Carat + DataACL
            Dim myACLs As New Collection 'collection of distinct point/data level ACLs(i)
            Dim myACLTags As New Collection 'collection of collection objects with tag names referencing the ACLs(i)
            myACLs.Add(myACL, myACL)
            Dim rTags As New Collection
            myACLTags.Add(rTags)
            If Check Then Console.WriteLine("Tag {0}, Pt^Data ACL: {1}", myPt.Name, myACL)
            'Find rest of points in this Pointsource AND Location1
            Dim ptSRC, ptLOC1, TagSearchStr As String
            ptSRC = myPt.GetAttribute("Pointsource").ToString
            ptLOC1 = myPt.GetAttribute("Location1").ToString
            TagSearchStr = "PointSource:=" + ptSRC + " AND Location1:=" + ptLOC1
            Dim myPts As IEnumerable(Of PIPoint)
            myPts = PIPoint.FindPIPoints(myPI, TagSearchStr, False)
            'organize collections by distinct ACLs
            Dim myTAGSperACL As Collection
            Dim i As Integer
            Dim ACLfound As Boolean
            For Each myPt In myPts
                'Debug.WriteLine("Pt = {0}", myPt.ID)
                myPt.LoadAttributes(PICommonPointAttributes.PointSecurity, PICommonPointAttributes.DataSecurity)
                'combine ptsecurity and datasecurity
                PtACL = myPt.GetAttribute("PtSecurity").ToString
                DataACL = myPt.GetAttribute("DataSecurity").ToString
                myACL = PtACL + Carat + DataACL
                'search through already found ACLs
                ACLfound = False
                For i = 1 To myACLs.Count
                    If myACLs(i) = myACL Then
                        myTAGSperACL = myACLTags(i)
                        myTAGSperACL.Add(myPt.Name)
                        ACLfound = True
                        Exit For
                    End If
                Next
                'new distinct ACL, add new tag collection object
                If ACLfound = False Then
                    myACLs.Add(myACL, myACL)
                    Dim tc As New Collection
                    tc.Add(myPt.Name)
                    myACLTags.Add(tc)
                    If Check Then Console.WriteLine("Tag {0}, Pt^Data ACL: {1}", myPt.Name, myACL)
                End If
            Next
            ' report of distinct ACLs and count of associated tag names
            For i = 1 To myACLs.Count
                myTAGSperACL = myACLTags(i)
                Console.WriteLine("Count = {0} ^ {1}", myTAGSperACL.Count, myACLs(i))
            Next
            ' check list of distinct ACLs and associated tag names
            If Check Then
                For i = 1 To myACLs.Count
                    myTAGSperACL = myACLTags(i)
                    For Each tagname In myTAGSperACL
                        Console.WriteLine("Check List = {0}^{1}", tagname, myACLs(i))
                    Next
                Next
            End If
            If Clobber Or myMerge Then
                ' synchronize security attributes with refrence tag (first ACL)
                Dim FullACL As Object
                FullACL = Split(myACLs(1), Carat)
                PtACL = FullACL(0).ToString
                DataACL = FullACL(1).ToString
                'if merge initialize reference security attributes (first ACL) with distinct ACLs
                Dim PtACEs, DataACEs As Object
                PtACEs = Split(PtACL, Bar)
                DataACEs = Split(DataACL, Bar)
                Dim ACLchanged As Boolean = False
                For i = 2 To myACLs.Count 'start at 2nd ACL, first is the reference tag
                    If myMerge Then
                        'separate PtACL from DataACL
                        Dim CheckACL As Object
                        CheckACL = Split(myACLs(i), Carat)
                        PtACL = CheckACL(0).ToString
                        DataACL = CheckACL(1).ToString
                        'append missing ACE - rely on PIServer to merge/reconcile verbs within each ACE [eg PIWorld:A(R) with PIWorld:A(R,W)]
                        For Each ACE In PtACEs
                            If PtACL.Contains(Trim(ACE)) = False Then
                                PtACL = PtACL + Space + Bar + Space + Trim(ACE)
                                ACLchanged = True
                            End If
                        Next
                        'append missing ACE - rely on PIServer to merge/reconcile verbs within each ACE [eg PIWorld:A(R) with PIWorld:A(R,W)]
                        For Each ACE In DataACEs
                            If DataACL.Contains(Trim(ACE)) = False Then
                                DataACL = DataACL + Space + Bar + Space + Trim(ACE)
                                ACLchanged = True
                            End If
                        Next
                    End If
                    ' set and save attributes to PI Server for collection of tags with missing reference ACEs
                    If ACLchanged Or Clobber Then
                        myTAGSperACL = myACLTags(i)
                        For Each tagname In myTAGSperACL
                            myPt = PIPoint.FindPIPoint(myPI, tagname)
                            myPt.LoadAttributes(PICommonPointAttributes.PointSecurity, PICommonPointAttributes.DataSecurity)
                            myPt.SetAttribute("PtSecurity", PtACL)
                            myPt.SetAttribute("DataSecurity", DataACL)
                            Dim errors As AFErrors(Of String) = myPt.SaveAttributes(PICommonPointAttributes.PointSecurity, PICommonPointAttributes.DataSecurity)
                        Next
                    End If
                Next
            End If 'clobber or mymerge
        Catch
            Console.WriteLine("FindPIPoint {0} Error {1}", rTag, Err.Description)
            Console.WriteLine("Default Server = {0}", myPIServers.DefaultPIServer.Name)
            Console.WriteLine("Connection Client = {0}", myConnectionInfo.Host)
        End Try
    End Sub
End Module