Scott Roth · Feb 16, 2018 go to post

I have run into a case where the Last Name or First Name contains a Hyphen but no spaces between the Hyphen and the split name. I have tried "S", but that seemed not to help.

Anyone have suggestions on how to handle names with a hyphen besides doing an IF statement, and Piecing the Name apart to make it Proper Case?

Scott Roth · Feb 19, 2018 go to post

So I am trying to take a list of....

managedObjects                       : {CN=Access.Ensemble.Developer.User,OU=Access Groups,DC=OSUMC,DC=EDU, CN=[CPD Admin],OU=Distribution Lists,DC=OSUMC,DC=EDU, CN=[MUSE_Access],OU=Distribution Lists,DC=OSUMC,DC=EDU, CN=[IT eMaterials],OU=Distribution Lists,DC=OSUMC,DC=EDU...}

How can I put this into a list and properly filter out the values I need. For Example Access.Ensemble.Developer.User is my end target that corresponds to a role of that same name.

I am struggling with how to appropriately pull this information out so I can do the IF statements below.

Scott Roth · Feb 19, 2018 go to post

I believe it is either a String for a Fixed Sequence. I cut and pasted that from a document given to me to show me the LDAP/AD Attributes. I believe the structure is {CN=  ,OU=,  DC=, DC= , CN=,OSU=,DC=,DC=.....}  so every time we see a CN its a new group which is what I want to key off of.

Scott Roth · Feb 19, 2018 go to post

When I run...

 i=2:1:$LL(list2) write !,i,"",$p($li(list2,i),",")
 

I am getting the output of

2Access.Ensemble.Developer.User2

How can I remove the 2 from the beginning and ending of the string, then compare it to the %SYS Roles?

Thanks

Scott

Scott Roth · Feb 26, 2018 go to post
ZAUTHENTICATE(ServiceName,Namespace,Username,Password,Credentials,Properties) PUBLIC {#include %occErrors#include %sySecurity#include %syLDAPs LDAPServer="xxxxx.xxxxxx.xxxx"s LD=##Class(%SYS.LDAP).Init(LDAPServer)i LD=0 {s Status=##Class(%SYS.LDAP).GetLastError()//g Error}sLDAPUser = "ensemble.Services"s LDAPPass = "xxxxxxxxxxxxxxxxxxxxxxxxxxx"s Status=##Class(%SYS.LDAP).Binds(LD,"",$lb(LDAPUser,"",LDAPPass),$$$LDAPAUTHNEGOTIATE)i Status'=$$$LDAPSUCCESS q $SYSTEM.Status.Error($$$InvalidUsernameOrPassword)s BaseDN="dc=OSUMC,dc=EDU"s Filter="sAMAccountname="_Usernames Attributes=$lb("sAMAccountname","displayName","mail")s Status=##Class(%SYS.LDAP).SearchExts(LD,BaseDN,$$$LDAPSCOPESUBTREE,Filter,Attributes,0,"","",10,1,.SearchResult)i Status'=$$$LDAPSUCCESS {w !,"SearchExts error: "_Status_" - "_##Class(%SYS.LDAP).Err2String(Status)g Done}s CurrentEntry=##Class(%SYS.LDAP).FirstEntry(LD,SearchResult)i CurrentEntry=0 {s Status=##Class(%SYS.LDAP).GetError(LD)w !,"FirstEntry error: "_Status_" - "_##Class(%SYS.LDAP).Err2String(Status) g Done}s DN=##Class(%SYS.LDAP).GetDN(LD,CurrentEntry) s Attribute=##Class(%SYS.LDAP).FirstAttribute(LD,CurrentEntry,.Ptr)while (Attribute'="") {s Values=##Class(%SYS.LDAP).GetValuesLen(LD,CurrentEntry,Attribute)//w Valuess Properties("Attributes",Attribute)=Values//w Attributes(Attribute)s Attribute=##Class(%SYS.LDAP).NextAttribute(LD,CurrentEntry,.Ptr)}s Properties("Username")=$li(Properties("Attributes","sAMAccountName"))k Properties("Attributes","sAMAccountName")s Properties("FullName")=$li(Properties("Attributes","displayName"))k Properties("Attributes","displayName")s GroupFilter="(&(objectClass=group)(member:1.2.840.113556.1.4.1941:="_DN_"))"s GroupAttributes=""s Status=##Class(%SYS.LDAP).SearchExts(LD,BaseDN,$$$LDAPSCOPESUBTREE,GroupFilter,GroupAttributes,0,"","",10,0,.GroupSearchResult)i Status'=$$$LDAPSUCCESS {w !,"SearchExts error: "_Status_" - "_##Class(%SYS.LDAP).Err2String(Status)g Done}s GroupNumEntries=##Class(%SYS.LDAP).CountEntries(LD,GroupSearchResult)i GroupNumEntries=-1 {s Status=##Class(%SYS.LDAP).GetError(LD)//w !,"CountEntries Group error: "_Status_" - "_##Class(%SYS.LDAP).Err2String(Status)g Done}w !i GroupNumEntries=0 {w !,"No nested groups for "_Username_" found"g Done}i GroupNumEntries>0 {//w !,"Found "_GroupNumEntries_" nested groups for user "_Username}#;Get the dn of the first entry returned.s GroupCurrentEntry=##Class(%SYS.LDAP).FirstEntry(LD,GroupSearchResult)i GroupCurrentEntry=0 {s Status=##Class(%SYS.LDAP).GetError(LD)w !,"FirstEntry error: "_Status_" - "_##Class(%SYS.LDAP).Err2String(Status)g Done}s Groups=""While (GroupCurrentEntry'=0) {s GroupDN=##Class(%SYS.LDAP).GetDN(LD,GroupCurrentEntry)i GroupDN="" {s Status=##Class(%SYS.LDAP).GetError(LD)w !,"GetDN Group error: "_Status_" - "_##Class(%SYS.LDAP).Err2String(Status)q}s CN=$p(GroupDN,",",1)s AD=$p(CN,"=",2)s AD=$zcvt(AD,"L") set exists=''$d(^|"%SYS"|SYS("Security","RolesD",AD))if exists{s Properties("Roles") = ADw Properties("Roles")}#;Save for later display//w !,ADs GroupCurrentEntry=##Class(%SYS.LDAP).NextEntry(LD,GroupCurrentEntry)}Donei $d(SearchResult) d ##Class(%SYS.LDAP).MsgFree(SearchResult)i $d(GroupSearchResult) d ##Class(%SYS.LDAP).MsgFree(GroupSearchResult)#;Close the connection and free the LDAP in memory structures.i $d(LD) d ##Class(%SYS.LDAP).UnBinds(LD)q $SYSTEM.Status.OK()Error s $zt=""w !,"Cache error: "_$zeg Done}
Scott Roth · Feb 26, 2018 go to post
  • To verify against LDAP we needed a separate user other than the user trying to log into the system.
  • Also to make it easier on all parties involved instead of using new Attributes in LDAP, I had a group created that I could match up against a role of the same name in Ensemble.

So if I have an additional user I need to use to verify as the LDAP search user, how would I store the user name and password in this scenario?

Scott Roth · Feb 26, 2018 go to post

Sorry I am learning AD/LDAP as I go. I talked with the "powers that be" and they did confirm that I do not see a separate username and password to BIND to LDAP, and that I can use the UserName. So I will make that switch in the code above.

Thanks everyone.

Scott Roth · Mar 1, 2018 go to post

Instead of storing it at the Credentials level, I have created a Global to store the LDAP server information. However when I am calling $Get(^OSUMCLDAP(Server)) nothing is being returned. Am I not calling it appropriately? Do I need to add an include statement somewhere to make sure it includes the globals?

#define LDAPServer $Get(^OSUMCLDAP(Server))

 $$$WindowsLDAPServer{
AdminDN=$Get(^OSUMCLDAP(User))
AdminPW=$Get(^OSUMCLDAP(Pass))
 }

Thanks

Scott

Scott Roth · Mar 9, 2018 go to post

Can I still add code in Business Studio and it maintain integrity to be able to still open in the BPL editor?

I added the following in my while loop, but then the BPL hung.

<assign property='..%Process.%SessionId' value='""'/>
Scott Roth · Mar 12, 2018 go to post

What does your... ##class(SamsReq).%New() look like? Here is what my Request and code look like as a whole. I have used this many times across many services in different fashions. I am using JDBC vs ODBC which maybe a difference, not sure.


Class osuwmc.CPD.DataStructures.StartJobRequest Extends (%Library.Persistent, %XML.Adaptor) [ Not ProcedureBlock, SqlRowIdPrivate ]
{
Property StartJobStatus As %Integer;
Storage Default
{
<Data name="StartJobRequestDefaultData">
<Value name="1">
<Value>%%CLASSNAME</Value>
</Value>
<Value name="2">
<Value>StartJobStatus</Value>
</Value>
</Data>
<DataLocation>^osuwmc.CPD59D.StartJobReqE986D</DataLocation>
<DefaultData>StartJobRequestDefaultData</DefaultData>
<IdLocation>^osuwmc.CPD59D.StartJobReqE986D</IdLocation>
<IndexLocation>^osuwmc.CPD59D.StartJobReqE986I</IndexLocation>
<StreamLocation>^osuwmc.CPD59D.StartJobReqE986S</StreamLocation>
<Type>%Library.CacheStorage</Type>
}
}


Class osuwmc.CPD.UpdateClarityAddressesFromCPDService Extends Ens.BusinessService [ ClassType = "", ProcedureBlock ]
{
Parameter ADAPTER = "EnsLib.SQL.InboundAdapter";
Parameter REQUESTCLASSES As %String = "EnsLib.SQL.Snapshot";
Property InitDSN As %String;
Method OnInit() As %Status
{
Set ..InitDSN = ..Adapter.DSN
//Set ..Adapter.ConnectAttrs = "QueryTimeout:45" ; try this too just in case...
Quit $$$OK
}
Method OnProcessInput(pInput As EnsLib.SQL.Snapshot, pOutput As %RegisteredObject) As %Status
{
set req=##class(osuwmc.CPD.DataStructures.StartJobRequest).%New()
set req.StartJobStatus = pInput.Get("1")
set sc = ..SendRequestSync("CPDClarityAddressUpdateBPL",req,.pOutput)
    Quit sc
}
}
 

Scott Roth · Mar 14, 2018 go to post

Thanks not the exact answer that I was looking for but I think I can make it work somehow.

Scott Roth · Apr 6, 2018 go to post

Using the Ensemble Scheduler we ran into issues when we had multiple schedules running at the same time, and notice it pausing the whole environment just to refresh. That's why we went down this route.

It looks like this particular Inbound.SQL.Adapter service that caused the issue had a data lock on the database which caused more issues. So I am wondering if the data lock caused EnsConfigItem() to get in a hung status.

Scott Roth · Apr 10, 2018 go to post

Just adding a bump to see if anyone has a way to have the system users bypass the delegated sign on without having to hard code the system.

THanks

Scott

Scott Roth · May 29, 2018 go to post

When I look at the message in Message viewer it is showing 06CL as FT1:16.4.2

Scott Roth · Jun 7, 2018 go to post

Haven't tried that. Wasn't sure if it was going to make me do the Authentication before I connect to the external database, or while connecting to the external database.

Maybe I am making a mountain out of a mole hill.

Scott Roth · Jul 2, 2018 go to post

Thanks.

I am now to the point where I am getting Error message: LDAP error: 0 - Success. How can a Success be a Failure?

ZAUTHENTICATE(ServiceName,Namespace,Username,Password,Credentials,Properties) PUBLIC {
#include %occErrors
#include %sySecurity
#include %sySite
#include %syLDAP
#define LDAPServer $Get(^OSUMCLDAP("Server"))
#define WindowsLDAPServer 1

#define WindowsCacheClient 0
#define UseSecureConnection 1
#define UnixCertificateFile $Get(^OSUMCLDAP("LDAPKey"))_"certnew.pem"
#define WindowsBaseDN "dc="_$Get(^OSUMCLDAP("Domain"))_",dc=edu"
#define WindowsFilter "sAMAccountname"
#define WindowsAttributeList $lb("displayName","department","mail")
$zt="Error"
 
 Status = 0
 Password="" {
Status= $SYSTEM.Status.Error($$$InvalidUsernameOrPassword)
Error
 }
 $$$WindowsLDAPServer{
AdminDN=$Get(^OSUMCLDAP("User"))
AdminPW=$Get(^OSUMCLDAP("Pass"))
 }
 #;The following line sets up the internal LDAP structures.
 $$$ISWINDOWS,$$$UseSecureConnection {
LD=##Class(%SYS.LDAP).Init($$$LDAPServer,636)
 else {
LD=##Class(%SYS.LDAP).Init($$$LDAPServer)
 }
 LD=0 {
Status=##Class(%SYS.LDAP).GetLastError()
Status="Init error: "_Status_" - "_##Class(%SYS.LDAP).Err2String(Status)
Error
 }
 Status=##Class(%SYS.LDAP).SetOption(LD,$$$LDAPOPTXTLSCACERTFILE,$$$UnixCertificateFile)
 Status'=$$$LDAPSUCCESS{
Status ="SetOption error: "_Status_" - "_##Class(%SYS.LDAP).Err2String(Status)
Error
 
 Status=##class(%SYS.LDAP).StartTLSs(LD)
 Status'=$$$LDAPSUCCESS{
Status=Status_",ldap_setoption(Certificate) - "_##class(%SYS.LDAP).Err2String(Status)
Error
  }
 
 Status=##Class(%SYS.LDAP).SimpleBinds(LD,AdminDN,AdminPW)
 Status'=$$$LDAPSUCCESS 
  {
Status = Status_", ldap_Simple_Bind(AdminDN) - "_##Class(%SYS.LDAP).Err2String(Status)
#;w !,Status
Error
  }
 $$$WindowsLDAPServer {
Filter=$$$WindowsFilter_"="_Username
 }
 $$$WindowsLDAPServer {
AttributeList=$$$WindowsAttributeList
 
 $$$WindowsLDAPServer {
BaseDN=$$$WindowsBaseDN
 
 
 SearchScope=$$$LDAPSCOPESUBTREE
 Timeout=30
 
 SizeLimit=1
 
 Status=##Class(%SYS.LDAP).SearchExts(LD,BaseDN,SearchScope,Filter,AttributeList,0,"","",Timeout,"",.SearchResult)
 Status'=$$$LDAPSUCCESS {
Status=$$$XLDAPFILTERERROR {
Status="1,User "_Username_" does not exist"
!,Status
else {
Status=Status_",ldap_Search_Ext - "_##Class(%SYS.LDAP).Err2String(Status)
}
Error
 }
 NumEntries=##Class(%SYS.LDAP).CountEntries(LD,SearchResult)
 NumEntries=-1 {
 Status=##Class(%SYS.LDAP).GetError(LD)
 Status=Status_",ldap_Count_Entries - "_##Class(%SYS.LDAP).Err2String(Status)
 Error
 }
NumEntries=0 {
Status="1,User "_Username_" does not exist"
  Error
 }
 
 NumEntries>1 {
Status="1,LDAP Filter is not unique"
  Error
 }
 
 CurrentEntry=##Class(%SYS.LDAP).FirstEntry(LD,SearchResult)
 CurrentEntry=0 {
Status=##Class(%SYS.LDAP).GetError(LD)
Status=Status_",ldap_FirstEntry - "_##Class(%SYS.LDAP).Err2String(Status)
Error
 }
 
 DN=##Class(%SYS.LDAP).GetDN(LD,CurrentEntry
 Password="" {
Status="1,ldap_Simple_Bind("_DN_") - password cannot be null"
Error
 }
 
 Status=##Class(%SYS.LDAP).SimpleBinds(LD,DN,Password)
 Status'=$$$LDAPSUCCESS {
Status=Status_",ldap_Simple_Bind("_DN_") - "_##Class(%SYS.LDAP).Err2String(Status)
Error
 }
 Attribute=##Class(%SYS.LDAP).FirstAttribute(LD,CurrentEntry,.Ptr)
 while (Attribute'="") {
   Values=##Class(%SYS.LDAP).GetValuesLen(LD,CurrentEntry,Attribute)
   #;Values:"_Values
   Properties("Attributes",Attribute)=Values
  Attribute=##Class(%SYS.LDAP).NextAttribute(LD,CurrentEntry,.Ptr)
 }
 Properties("Username")=Username
 Properties("FullName")=$li(Properties("Attributes","displayName"))
 Properties("Attributes","displayName")
 Properties("Comment")=$li(Properties("Attributes","department"))
 Properties("Attributes","department")
 Properties("EmailAddress")=$li(Properties("Attributes","mail"))
 Properties("Attributes","mail")
 
 $d(SearchResult) ##Class(%SYS.LDAP).MsgFree(SearchResult)
 
 GroupFilter="(&(objectClass=group)(member:1.2.840.113556.1.4.1941:="_DN_"))"
 GroupAttributes=""
 Status=##Class(%SYS.LDAP).SearchExts(LD,BaseDN,$$$LDAPSCOPESUBTREE,GroupFilter,GroupAttributes,0,"","",10,0,.GroupSearchResult)
 #;GroupSearch Status: "_Status
 Status'=$$$LDAPSUCCESS {
!,"SearchExts error: "_Status_" - "_##Class(%SYS.LDAP).Err2String(Status)
Error
 }
 GroupNumEntries=##Class(%SYS.LDAP).CountEntries(LD,GroupSearchResult)
 GroupNumEntries=-1 {
Status=##Class(%SYS.LDAP).GetError(LD)
Status=##Class(%SYS.LDAP).Err2String(Status)
Error
 }
!
 GroupNumEntries=0 {
!,"No nested groups for "_Username_" found"
Done
 }
 GroupNumEntries>0 {
 }
 
 GroupCurrentEntry=##Class(%SYS.LDAP).FirstEntry(LD,GroupSearchResult)
 GroupCurrentEntry=0 {
Status=##Class(%SYS.LDAP).GetError(LD)
!,"FirstEntry error: "_Status_" - "_##Class(%SYS.LDAP).Err2String(Status)
Error
 }
 Groups=""
 While (GroupCurrentEntry'=0) {
GroupDN=##Class(%SYS.LDAP).GetDN(LD,GroupCurrentEntry)
GroupDN="" {
Status=##Class(%SYS.LDAP).GetError(LD)
!,"GetDN Group error: "_Status_" - "_##Class(%SYS.LDAP).Err2String(Status)
Error
}
CN=$p(GroupDN,",",1)
AD=$p(CN,"=",2)
AD=$zcvt(AD,"L")
exists=''$d(^|"%SYS"|SYS("Security","RolesD",AD))
exists{
Properties("Roles") = AD
}
  GroupCurrentEntry=##Class(%SYS.LDAP).NextEntry(LD,GroupCurrentEntry)
 }
Done
+$d(LD) ##Class(%SYS.LDAP).UnBinds(LD)
 #;w !,"SystemOK "_$SYSTEM.Status.OK()
 $SYSTEM.Status.OK()
Error $zt=""
 $d(SearchResult) ##Class(%SYS.LDAP).MsgFree(SearchResult)
 $d(GroupSearchResult) ##Class(%SYS.LDAP).MsgFree(GroupSearchResult)
 +$d(LD) Status=##class(%SYS.LDAP).UnBinds(LD)
 $ze'=""{
 $SYSTEM.Status.Error($$$CacheError,$ze)
 else{  
  $SYSTEM.Status.Error($$$GeneralError,"LDAP error: "_Status_" - "_##Class(%SYS.LDAP).Err2String(Status))
 }
}
 

Scott Roth · Jul 2, 2018 go to post

That's the part I am unsure of, because it is not logging to the Audit Database. How can I run this and put breakpoints in to verify where it might be getting stuck?

Scott Roth · Jul 3, 2018 go to post

I was able to track down the error to

Status=##class(%SYS.LDAP).StartTLSs(LD)
 Status'=$$$LDAPSUCCESS{
Status=Status_",ldap_setoption(Certificate) - "_##class(%SYS.LDAP).Err2String(Status)
Status,!
Error
  }

-1,ldap_setoption(Certificate) - Can't contact LDAP server

Scott Roth · Jul 3, 2018 go to post

I was able to track down the error to

Status=##class(%SYS.LDAP).StartTLSs(LD)
 Status'=$$$LDAPSUCCESS{
Status=Status_",ldap_setoption(Certificate) - "_##class(%SYS.LDAP).Err2String(Status)
Status,!
Error
  }

-1,ldap_setoption(Certificate) - Can't contact LDAP server

Scott Roth · Jul 23, 2018 go to post

I am still struggling to get this to work. If I go through %SYS and manually try to start a TLS connection in AIX to my LDAP server I am getting...

s Status=##Class(%SYS.LDAP).SetOption(LD,$$$LDAPOPTXTLSCACERTFILE,"/ensemble/TestClin/mgr/LDAPKeyStore/OSUWMC_CA.pem") - Error <SYNTAX>

Does the certificate need to be in a certain directory for this to work as the examples suggested " /usr/share/ssl/certs/...."?

Scott Roth · Jul 24, 2018 go to post

Yes I was. That would explain the syntax issue. I have verified that the SimpleBinds without StartTLSs works fine. I have added some additional print statements to see where the issue might lie. The code gets past the Init, and SetOption, but then dies on the StartTLSs.

LD=1
SetOption=Success
-11,ldap_StartTLSs(Certificate) - Connect error

I went ahead and opened a ticket up with WRC to see if they could help. Thanks everyone.

Scott Roth · Aug 15, 2018 go to post

Maybe I didn't describe this right. I am trying to go from No Authentication to a Windows Authentication account. Has anyone done this? if so can you share your part of your URL to see what I am possibly missing.

Thanks

Scott

Scott Roth · Aug 16, 2018 go to post

I got jTDS working through using Squirrel SQL Client, I am just now trying to transpose that over to Ensemble to see if I can get it to work.

Scott Roth · Aug 30, 2018 go to post

Was this ever pushed forward as true development? I know you have been working on it sometime, and wanted to see if I could take a look at it for my organization.

Thanks

Scott

Scott Roth · Sep 13, 2018 go to post

Are you connecting from a Windows Client to a Windows LDAP server? Or is it an Unix Client to a Windows LDAP server? Its a matter of using Binds vs SimpleBinds. I am wondering if that is causing your error. I use Delegated Authentication through LDAP using a TLS certificate so my setup maybe more complicated than yours.

Scott Roth · Sep 13, 2018 go to post

This is not in a translation. We want to write a function to loop through a repeating segment and compare it to a string value within a Routing Rule. I've done this before as a lookup against a table but not compare strings before.

Scott Roth · Sep 13, 2018 go to post

I am not familiar with that function.

This is how I am looking up a repeatable segment against a lookup table.

ClassMethod GroupIDExists(pHL7Msg As EnsLib.HL7.Message, pSegment As %String, pField As %String, pLookupTable As %String) As %Boolean
{
            #dim tSeg as EnsLib.HL7.Segment
            
            set tSegCount = pHL7Msg.SegCountGet()
            set = 1
            Set tFound = 0
            //get new values
            set tval=""
            while ((<= tSegCount) && (tval="")) {
                         
                        set tSeg = pHL7Msg.GetSegmentAt(i)
                        if (tSeg.Name = pSegment) {
                                    set tID = tSeg.GetValueAt(pField)
                                    set tval= ..Lookup(pLookupTable, tID)
                                                            
                        }
                        set = + 1
            }
            if (tval '= "")
            {
                        1
            }
            quit 0
}

Not sure how you would do that with XECUTE

Scott Roth · Sep 18, 2018 go to post

We've have done something similar but wrote a function to do the looping... This example loops through a given segment and field and looks that value up against a lookup table. This could be modified for your use. This is the only way I know to do the looping within the BusinessRule.

ClassMethod GroupIDExists(pHL7Msg As EnsLib.HL7.Message, pSegment As %String, pField As %String, pLookupTable As %String) As %Boolean
{
            #dim tSeg as EnsLib.HL7.Segment
            
            set tSegCount = pHL7Msg.SegCountGet()
            set = 1
            Set tFound = 0
            //get new values
            set tval=""
            while ((<= tSegCount) && (tval="")) {
                         
                        set tSeg = pHL7Msg.GetSegmentAt(i)
                        if (tSeg.Name = pSegment) {
                                    set tID = tSeg.GetValueAt(pField)
                                    set tval= ..Lookup(pLookupTable, tID)
                                                            
                        }
                        set = + 1
            }
            if (tval '= "")
            {
                        1
            }
            quit 0
}