Is there a simple way to resolve the Active Directory path of a Domain name in Active Directory?
E.g. your user account might be SITE\Username or DEPARTMENT\Username but the actual path of the domain SITE might be site.company.com or DEPARTMENT might be dep.company.com etc
I'm trying to find a way of turning DEPARTMENT into DC=dep,DC=company,DC=com etc for searching correctly in Active Directory...
What you probably have is a company with one forrest (company.com) and a couple of child domains (SITE and DEPARTMENT in your case). The SITE- and DEPARTMENT-bits are the NetBIOS representations of the domains. Is not very common that they differ from the corresponding DNS names but it is possible. Just make sure we're not talking about OU:s and "physical" Active Directory sites.
Assuming the above, here are a couple of options:
The following assumes that your application is running under an account with "forrest-wide read access" (the ability to access your different domains):
using System.DirectoryServices.ActiveDirectory;
// ...
DirectoryContext directoryContext
= new DirectoryContext(DirectoryContextType.Domain, "DEPARTMENT");
Domain domain = Domain.GetDomain(directoryContext);
String domainName = domain.Name;
String domainComponent = "DC=" + domainName.Replace(".", ",DC=");
Console.WriteLine(domainComponent);
(I haven't found a "System.DirectoryServices-built-in" way to transform domain.company.com to DC=domain,DC=company,DC=com but the simple string manipulation above should do the trick.)
If you're using a non-forrest-wide account (or if DEPARTMENT and SITE are not domains in the same forrest but in themselves separate forrests), then you'll have to maintain a list of usernames and passwords to use depending on the "DEPARTMENT" or "SITE"-strings:
// if(domainNetBios == "DEPARMENT")...
DirectoryContext directoryContext
= new DirectoryContext(DirectoryContextType.Domain,
"DEPARTMENT",
"UserInDEPARTMENT",
"PassForUserInDEPARTMENT");
If you're not willing to bind to the different forrests/domains to get the domain name/component you could try something along the lines of:
IPAddress[] addresses = Dns.GetHostAddresses("DEPARTMENT");
IPHostEntry host = Dns.GetHostEntry(addresses[0]);
Int32 dotIndex = host.HostName.IndexOf('.');
String domain =
host.HostName.Substring(dotIndex + 1, host.HostName.Length - dotIndex - 1);
Console.WriteLine(domain);
But the above assumes that the NETBios-name is the same as the first part of the DNS-name and that DNS resolution is working correctly. What we're doing above is to query for a list of domain controllers then removing the hostnames from the DNS-names. Not a particularly clean option...
Related
I'm trying to store email addresses in Salesforce.com from another service that allows invalid email addresses to be specified. If one of those bad invalid email addresses is sent to Salesforce.com via their Web Services API, Salesforce.com will prevent the record from saving with an INVALID_EMAIL_ADDRESS error code.
I can't find any documentation on how to disable validation on Email fields, so it looks like I'll need to validate them in my integration and pull out those that fail. Does anyone know the validation process Salesforce.com uses to determine if an email address is valid? All I have right now is a Regex, but I'd like it to match Salesforce.com's process.
EDIT: For reference, here is my Regex (I'm using C#/.NET):
^(\w|[!#$%'*+-/=?^_`\{\}~.&])+#\w+([-.]\w+)*\.\w+([-.]\w+)*([,;]\s*\w+([-+.]\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*)*$
Summary: we're using the following .NET RegEx:
const string SFEmailRegExPattern = #"^[A-Z0-9._%-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$";
If you can believe SF's own documentation then:
For the local part of the email address we accept the following characters. The local part is anything before the # sign.
abcdefg.hijklmnopqrstuvwxyz!#$%&'*/=?^_+-`{|}~0123456789
Note: The character dot . is supported; provided that it is not the first or last character in the local-part
For the domain part of the email address we accept. The domain part is anything after the # in an email address:
0-9 and A-Z and a-z and dash -
A couple of people have coded this up as a Java regex as:
String pat = '[a-zA-Z0-9\\.\\!\\#\\$\\%\\&\\*\\/\\=\\?\\^\\_\\+\\-\\`\\{\\|\\}\\~\'._%+-]+#[a-zA-Z0-9\\-.-]+\\.[a-zA-Z]+';
although to me this looks like it fails to reject an email that starts with a "." so isn't perfect.
I don't know how salesforce.com is validating email addresses, but since you are using .NET I'd suggest you to consider an email validation component like our EmailVerify.NET, which is 100% compliant with the current IETF standards (RFC 1123, RFC 2821, RFC 2822, RFC 3490, RFC 3696, RFC 4291, RFC 5321, RFC 5322 and RFC 5336) and does not suffer from ReDoS: if needed, it even checks the DNS records of the email domain under test, its SMTP availability, validates the related mailbox and can even tell if the target mail exchanger is a catch-all or if it is a disposable/free email address provider.
I don't know what salesforce.com uses (and I don't think there's any way for you to find out), but \b[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}\b from here is a commmon one and should work for most of the cases.
I've looked previously and not been able to find a definitive answer on exactly which rules SFDC applies to the native "Email" field type. The quickest path to success that I would suggest would be this:
in your initial data integration from the external application, map the email field that you describe into a new (non-email, just text 255) custom field in SFDC.
if this is a one-time dataload, run a separate process that, for every row in SFDC with this custom field populated, attempts to copy the contents of this custom field to the native email field. If any row fails with the email validation error, you just skip it. Then you can decide what to do with the non-compliant addresses.
if this is an ongoing integration, it may be better to do something like attempt to insert new rows one-at-a-time via WS API, and if the email validation exception is thrown, you catch it and either insert the record without an email address, store the bad email in a different field (like a custom field called "non-compliant email address"), or skip the row altogether (if bad emails == bad record).
Hope that helps.
Apex has native Pattern and Matcher classes, based on java.
You can validate your email addresses in Apex code, using your RegEx expression as a string
String emailPattern = {your regex expression);
Boolean validEmail = pattern.match(emailPattern, emailAddress);
You can't definitely create common regex for salesforce email, due to inconsistency of their own requirements.
The one rule is about to give possibilities to put IP address after the local part. Example -> email#123.123.123.123.
The second is about do not allow digits in top-level domain.
For example: test#test.com1
So, they are mutually excluded.
But as I understood the email address with IP after the local part is more important and commonly used comparing with numbers in top-level domain.
Here is some examples of valid/invalid emails for salesforce.
Valid:
a#ua.fm
email#domain.com
firstname.lastname#domain.com
email#subdomain.domain.com
firstname+lastname#domain.com
email#123.123.123.123
1234567890#domain.com
email#domain-one.com
_______#domain.com
email#domain.name
email#buyacar.co.uk
ail#github.dennis.co.uk
email#news.i.ua
firstname-lastname#domain.com
Alexka1!+1123klsn&*^%$%$#^^^#a3432.4s.c4p.uk
frw...??//||/wt'f`fe#wfwfg-----wfwef.mm
a..#test.jp
abcdefg.hijklmnopqrstuvwxyz!#$%&'*/=?^_+-`{|}~0123456789#acme-inc.com
Invalid:
aasd#sdfжжж.rf
plainaddress
##%^%#$##$##.com
#domain.com
email.domain.com
email#domain#domain.com
.email#domain.com
あいうえお#domain.com
email#domain.com (Joe Smith)
email#domain
email#domain..com
email#domain.com.e
email#domain.com.33
As result of above, the final regex is:
/^(?!\.)(([^<>()\[\]\\a-zA-Z0-9.,;:\s#"]*(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))[a-zA-Z0-9.!#$%&'‘*+\/=?^_{|}~-]+#[\w.-?]+.[A-Za-z]*(?
Here is a regular expression based on this help page + a lot of experimenting in Salesforce:
^(?=(?:\([^)]*\))*[^()]+[^#]*#)(?!(?:\([^)]*\))*\.)(?:(?:[\w!#$%&'*+-/=?^`{|}~]|\([\w!#$%&'*+-/=?^`{|}~]*\))+|"(?:[\w!#$%&'*+-/=?^`{|}~]|\([\w!#$%&'*+-/=?^`{|}~]*\))*")#(?:\([A-Za-z0-9-]*\))*(?:(?:[A-Za-z0-9]+|[A-Za-z0-9]+(?:\([A-Za-z0-9-]*\))*-(?:\([A-Za-z0-9-]*\))*[A-Za-z0-9]+)(?:\([A-Za-z0-9-]*\))*)(?:\.(?:\([A-Za-z0-9-]*\))*(?:(?:[A-Za-z0-9]+|[A-Za-z0-9]+(?:\([A-Za-z0-9-]*\))*-(?:\([A-Za-z0-9-]*\))*[A-Za-z0-9]+)(?:\([A-Za-z0-9-]*\))*))+$
See this Demo. It gives the same validation result as Salesforce for all the values I could think of testing - copied below - any counter examples are welcome...
************* VALID *************
a#a.a
-#a.a
a#1.a
a#a-a.a
a#a.a-a
!#$%&'*+-/=?^_`{|}~#test.jp
a..a#test.jp
a..#test.jp
"a"#test.jp
""#test.jp
(comment)(comment)a(comment)(comment)(comment)#(comment)a.a
(comment)(comment)a.(comment)(comment)(comment)#(comment)a.a
(comment)(comment)a(comment).(comment)(comment)#(comment)a.a
a#(comment)a(comment)-(comment)a(comment).a
john.doe#(-comment)example.com
john.doe#example.com(comment-)
()a#test.jp
(a)a#test.jp
a(a)#test.jp
a#(a)test.jp
a#test.jp(a)
simple#example.com
very.common#example.com
disposable.style.email.with+symbol#example.com
other.email-with-hyphen#example.com
fully-qualified-domain#example.com
user.name+tag+sorting#example.com
x#example.com
example-indeed#strange-example.com
test/test#test.com
example#s.example
"john..doe"#example.org
mailhost!username#example.org
user%example.com#example.org
user-#example.org
1#1234567890123456789012345678901234567890123456789012345678901234.1.2.3.4.5.6.7
************* INVALID *************
a#a
a#a.
a#-a.a
a#a-.a
a#a.a-
a#a.-a
a;a#test.jp
.a#test.jp
";"#test.jp
"#"#test.jp
"a#test.jp
a"#test.jp
a""#test.jp
""a#test.jp
()#test.jp
)(a#test.jp
(a)#test.jp
(a#test.jp
(())a#test.jp
(comment)(comment).(comment)a(comment)(comment)#(comment)a.a
john.doe#(comment).com
a#(comment)a(comment)-(comment)(comment).a
Αθήνα#email.com
admin#mailserver1
" "#example.org
"very.(),:;<>[]\".VERY.\"very#\\ \"very\".unusual"#strange.example.com
postmaster#[123.123.123.123]
postmaster#[IPv6:2001:0db8:85a3:0000:0000:8a2e:0370:7334]
A caveat to begin with - I don't actually know if what I want to do is possible, particularly because I'm not well versed with LDAP/Active Directory or JIRA.
I'm trying to integrate my shiny new installation of JIRA with my existing active directory. What I want to do is set up some specific JIRA groups (e.g. in London\Security Groups\JIRA*) and then have JIRA only import the users who have membership of those groups. However, in the directory set up page in JIRA, I don't understand how to do this. It seems to indicate that I can import users and groups, but not users from groups.
What am I missing? (apart from expert level knowledge of AD!)
Update
Under my domain, I have an organisational structure like this:
London\Users
London\Security Groups\JIRA
Under the latter organisational unit, I have a security group called "jira-users". The former contains all users.
So far I've tried the following queries and none of them have worked :
(all prefixed with &(objectCategory=Person)(sAMAccountName=*)")
memberof=CN=jira-users,ou=London,ou=Security Groups,ou=JIRA,dc=mycompany,dc=local
memberof=CN=JIRA,ou=London,ou=Security Groups,dc=mycompany,dc=local
(prefixed with just &(objectCategory=Person)")
memberof=CN=jira-users,ou=London,ou=Security Groups,ou=JIRA,dc=mycompany,dc=local
Completed
The query that works is this :
memberof=CN=jira-users,OU=JIRA,OU=Security Groups,OU=London,DC=mycompany,DC=local
I hadn't realised that for a folder structure that is logically, left to right, London\Security Groups\JIRA, the organisational units need to be listed in reverse order.
Further Update
This only works when using the DirectorySearcher class for some reason, e.g.
DirectoryEntry rootEntry = new DirectoryEntry("LDAP://dc=mycompany,dc=local");
DirectorySearcher srch = new DirectorySearcher(rootEntry);
srch.SearchScope = SearchScope.Subtree;
srch.Filter = "(&(objectCategory=Person)(sAMAccountName=*)(memberof=CN=jira-users,ou=London,ou=Security Groups,ou=JIRA,dc=mycompany,dc=local))";
SearchResultCollection results = srch.FindAll();
This doesn't work in the LDAP explorer tool and subsequently, not in JIRA itself.
Last Update
So...for JIRA, you need to reverse the order AND remove the wildcard. Working query in the end is :
(&(objectCategory=Person)(memberof=CN=jira-users,OU=JIRA,OU=Security Groups,OU=London,DC=mycomapny,DC=local))
When you are setting up the user directory look under the User Schema settings. You should see a "User Object Filter" field. In there you should be able to add something like this:
(memberOf=cn=jira-users,ou=London,dc=mydomain,dc=com)
This will allow you to filter based on a specific LDAP group. Of course you will need to edit the values above to reflect your own environment.
I'm trying to find out the unboundid AttributeSyntax type for a specific attribute name and it's simply not working.
Here's the example test code that I'm using to achieve this:
#Test
public void testLDAPSchema() {
try {
LDAPConnection connection = new LDAPConnection();
connection.connect("hessmain", 389);
connection.bind("CN=Administrator,CN=Users,DC=FISHBOWL,DC=NET", "password");
Schema s = connection.getSchema();
System.out.println(s.toString());
AttributeTypeDefinition atd = s.getAttributeType("directReports");
Set<AttributeTypeDefinition> oat = s.getOperationalAttributeTypes();
Set<AttributeSyntaxDefinition> l = s.getAttributeSyntaxes();
AttributeSyntaxDefinition asd1 = s.getAttributeSyntax(atd.getOID());
AttributeSyntaxDefinition asd2 = s.getAttributeSyntax(atd.getSyntaxOID());
AttributeSyntaxDefinition asd3 = s.getAttributeSyntax(atd.getBaseSyntaxOID());
connection.close();
} catch (Exception e) {
Assert.fail(e.getMessage());
}
}
From the above code, all the sets are empty. This also means that no matter which OID I pass to the schema getAttributeSyntax method that I will simply get a null return.
Is there any reason why I can't get the attribute syntaxes from an Active Directory server schema?
Thanks
I don't think that this is specific to the UnboundID LDAP SDK for Java. I'm not sure that Active Directory exposes this information over LDAP. When I perform a general LDAP search to retrieve schema information, I can see the attributeTypes and objectClasses attributes, but ldapSyntaxes isn't returned (and in fact ldapSyntaxes doesn't appear in the list of attribute types).
Similarly, none of the attribute type definitions includes a USAGE element, which is what is used to indicate that the attribute type is operational (e.g., "USAGE directoryOperation").
It may well be that Active Directory simply doesn't report this information at all. It could be that it provides some other non-standard way to get this information (e.g., a control or extended operation, or some other entry that can be retrieved), but if there is then I don't know about it.
I'm trying to convert the distinguishedName of a Domain into the address for the domain and am wondering if the order is strict. They all seem to be (after using the Active Directory explorer http://technet.microsoft.com/en-us/sysinternals/bb963907.aspx to explore ActiveDirectory...). I just haven't found anywhere which states the order is strict...
so, I'm hoping that:
DC=safety,DC=company,DC=com
is always equal to
safety.company.com
or could safety.company.com be stored in ActiveDirectory with any of the following etc:
DC=com,DC=company,DC=safety
DC=company,DC=com,DC=safety
Ultimately when passed the DC=...string, I want to be able to do the following to store the address:
Dim ADPath As String = "DC=safety,DC=company,DC=com"
Dim DomainAddress As String = ADPath.Replace(",DC=",".").Replace("DC=","")
Not the easiest text to read (for me anyway) but this MSDN page does describe that the path from left to right is a hierarchical path that uniquely describes the object.
"A DN is a sequence of relative distinguished names (RDN) connected by commas."
source: http://msdn.microsoft.com/en-us/library/aa366101%28VS.85%29.aspx
I've got an asp application running but i want to search the Active Directory.
i am using vb (visual web developer 2008)
how do i search the active directory for a given user?
ie: user enters login name in text box, clicks submit. active directory is searched on-click for this user. when found user information is displayed .
Thanks
What version of the .NET framework can you use? Searching and looking up stuff in AD has become extremely easy in .NET 3.5 - see this great MSDN article by Ethan Wilanski and Joe Kaplan on using the security principals API for that.
If you're not on .NET 3.5 yet, you'll have to use the DirectorySearcher class and set up the search filters as you need. Getting the LDAP filter right is probably the biggest obstacle.
Robbie Allen also has two great intro article on System.DirectoryServices programming:
- Part 1
- Part 2
There are some really good resources at http://www.directoryprogramming.net (Joe Kaplan's site - he's a Microsoft Active Directory MVP), and Richard Mueller has some great reference excel sheets on what properties are available for each of the ADSI providers, and what they mean, and how their LDAP name is - see http://www.rlmueller.net.
Marc
EDIT: Ok- here's the .NET 2.0 / 3.0 approach:
// set the search root - the AD container to search from
DirectoryEntry searchRoot = new DirectoryEntry("LDAP://dc=yourdomain,dc=com");
// create directory searcher
DirectorySearcher ds = new DirectorySearcher(searchRoot);
ds.SearchScope = SearchScope.Subtree;
// set the properties to load in the search results
// the fewer you load, the better your performance
ds.PropertiesToLoad.Add("cn");
ds.PropertiesToLoad.Add("sn");
ds.PropertiesToLoad.Add("givenName");
ds.PropertiesToLoad.Add("mail");
// set the filter - here I'm using objectCategory since this attribute is
// single-valued and indexed --> much better than objectClass in performance
// the "anr" is the "ambiguous name resolution" property which basically
// searches for all normally interesting name properties
ds.Filter = "(&(objectCategory=person)(anr=user-name-here))";
// get the result collection
SearchResultCollection src = ds.FindAll();
// iterate over the results
foreach (SearchResult sr in src)
{
// do whatever you need to do with the search result
// I'm extracting the properties I specified in the PropertiesToLoad
// mind you - a property might not be set in AD and thus would
// be NULL here (e.g. not included in the Properties collection)
// also, all result properties are really multi-valued, so you need
// to do this trickery to get the first of the values returned
string surname = string.Empty;
if (sr.Properties.Contains("sn"))
{
surname = sr.Properties["sn"][0].ToString();
}
string givenName = string.Empty;
if (sr.Properties.Contains("givenName"))
{
givenName = sr.Properties["givenName"][0].ToString();
}
string email = string.Empty;
if (sr.Properties.Contains("mail"))
{
email = sr.Properties["mail"][0].ToString();
}
Console.WriteLine("Name: {0} {1} / Mail: {2}", givenName, surname, email);
}
Hope this helps!
Marc