4.1.8.3 Server Behavior of the IDL_DRSGetMemberships Method

Informative summary of behavior: The IDL_DRSGetMemberships method constructs a directed graph G(V,A). The vertex set of the graph includes all the objects in the scope of the forest if the server is a GC, or in the scope of the default domain NC otherwise. The arc set of the graph includes all the tuples [initial: u,final: v] if u is a member of v and both u and v are in the scope. This graph represents the membership relation in the given scope.

For a GroupMembersTransitive request, a reversed graph of G is used because member relation is queried rather than membership. The reversed graph has the same vertex set as G, but the arcs in the arc set are in the opposite direction as those in A.

For other types of requests, a subgraph of G is used. The vertex set of this subgraph consists of only the DSName values of interest for that particular request type, and the arc set is reduced to the arcs that link two vertices in the vertex set of the subgraph.

Starting from the graph, this method computes a set of objects for each DSName in the input parameters. The set could be either transitive closure of the object or the immediate neighbors of the object in the graph, depending on the type of request. The union of these sets is returned as the result.

 ULONG
 IDL_DRSGetMemberships(
     [in, ref] DRS_HANDLE hDrs,
     [in] DWORD dwInVersion,
     [in, ref, switch_is(dwInVersion)] DRS_MSG_REVMEMB_REQ *pmsgIn,
     [out, ref] DWORD *pdwOutVersion,
     [out, ref, switch_is(*pdwOutVersion)]
         DRS_MSG_REVMEMB_REPLY *pmsgOut)
  
 msgIn: DRS_MSG_REVMEMB_REQ_V1
 vSet, wSet, uSet: set of DSName
 aSet, aSetR: ArcSet
 u,v,w: DSName
 op, i: integer
 transitive: boolean
 t: SID
  
 ValidateDRSInput(hDrs, 9)
  
 pdwOutVersion^ := 1
 pmsgOut^.V1.errCode := 0
 pmsgOut^.V1.cDsNames := 0
 pmsgOut^.V1.cSidHistory := 0
 pmsgOut^.V1.ppDsNames := null
 pmsgOut^.V1.pAttributes := null
 pmsgOut^.V1.ppSidHistory := null
  
 msgIn := pmsgIn^.V1
  
 if dwInVersion ≠ 1 then
   return ERROR_DS_DRA_INVALID_PARAMETER
 endif
 if not AccessCheckCAR(DefaultNC(), DS-Replication-Get-Changes) then
   return ERROR_DS_DRA_ACCESS_DENIED
 endif
  
 op := msgIn.OperationType
 if (op = RevMembGetUniversalGroups) and not IsGC() then
   return ERROR_DS_GC_REQUIRED
 endif
  
 /* Construct a membership graph. */
 /* Vertices */
 if IsGC() then
   vSet := select all v from all where true
 else
   vSet := select all v from subtree DefaultNC() where true
 Endif
  
 /* Edges */
 aSet := {}
 aSetR := {}
 foreach v in vSet
   foreach u in vSet
     if (u in v!memberOf) 
         or (u = GetDSNameFromPrimaryGroupId(v!primaryGroupId)) then
       aSet  := aSet  + {[initial: v, final: u]}
       aSetR := aSetR + {[initial: u, final: v]}
     endif
   endfor
 endfor
  
 /* Calculate GroupMembersTransitive. */
 if op = GroupMembersTransitive then
   wSet := {}
   for i := 0 to msgIn.ppDsNames.cDsNames - 1
     u := msgIn.ppDsNames[i]
     if u in vSet then
       wSet := wSet + (Closure(uSet, aSetR, u) - {u})
     endif
   endfor
  
   foreach w in wSet
     pmsgOut^.V1.ppDsNames[pmsgOut^.V1.cDsNames] := w
     pmsgOut^.V1.cDsNames:= pmsgOut^.V1.cDsNames + 1
   endfor
  
   return 0
 endif
  
 /* Calculate all other cases (where op ≠ GroupMembersInTransitive).*/
 transitive := op in {RevMembGetAccountGroups, 
                      RevMembGetResourceGroups, 
                      RevMembGetUniversalGroups}
  
 /* Get the initial result set from the graph. */
 wSet := {}
 for i := 0 to msgIn.ppDsNames.cDsNames - 1
   u := msgIn.ppDsNames[i]
   if u in vSet then
     /* Get the subgraph by applying the predicate IsMatchedGroup
      * on each element in the vertex set, plus u itself. */
     uSet := {u} + select all v from vSet where 
          IsMatchedGroup(v, op, msgIn.pLimitingDomain^)
     if transitive then
       wSet := wSet + (Closure(uSet, aSet, u) - {u})
     else
       wSet := wSet + (Neighbors(uSet, aSet, u) - {u})
     endif
     if((u!userAccountControl & ADS_UF_WORKSTATION_TRUST_ACCOUNT =
            ADS_UF_WORKSTATION_TRUST_ACCOUNT) and
         (u!userAccountControl & ADS_UF_PARTIAL_SECRETS_ACCOUNT =
            ADS_UF_PARTIAL_SECRETS_ACCOUNT))
  
         wSet := wSet + GetDSNameOfEnterpriseRODCsGroup()
     endif
   endif
 endfor
  
 /* Construct the result message. */
 pmsgOut^.V1.cSidHistory := 0
 pmsgOut^.V1.cDsNames := 0
  
 foreach w in wSet
   foreach t in w!sIDHistory
     if not (t in pmsgOut^.V1.ppSidHistory) then
       pmsgOut^.V1.ppSidHistory[pmsgOut^.V1.cSidHistory] := t
       pmsgOut^.V1.cSidHistory := pmsgOut^.V1.cSidHistory + 1
     endif
   endfor
   
   pmsgOut^.V1.ppDsNames[pmsgOut^.V1.cDsNames] := w
  
   if (DRS_REVMEMB_FLAG_GET_ATTRIBUTES in msgIn.dwFlags) then
     pmsgOut^.V1.pAttributes[pmsgOut^.V1.cDsNames] := 
         {SE_GROUP_MANDATORY,SE_GROUP_ENABLED_BY_DEFAULT,
          SE_GROUP_ENABLED}
   else 
     pmsgOut^.V1.pAttributes[pmsgOut^.V1.cDsNames] := 0
   endif
   pmsgOut^.V1.cDsNames := pmsgOut^.V1.cDsNames + 1
 endfor
  
 return 0