I have read on SO about validating emails with MailAddress and have produced the folowing code:
try
{
var address = new MailAddress(value.Email);
}
catch (FormatException)
{
emailError = "Invalid Email";
}
To my great surprise the string ndskfndlfk#sdflsdf validates as good email.
Any idea why is it so?
If you really need to filter those email addresses, you could use the little utility found here, which uses Regex to validate email addresses. Note that this is the .Net 4.0 version - it's a little different for 4.5.
using System;
using System.Globalization;
using System.Text.RegularExpressions;
public class RegexUtilities
{
bool invalid = false;
public bool IsValidEmail(string strIn)
{
invalid = false;
if (String.IsNullOrEmpty(strIn))
return false;
// Use IdnMapping class to convert Unicode domain names.
strIn = Regex.Replace(strIn, #"(#)(.+)$", this.DomainMapper);
if (invalid)
return false;
// Return true if strIn is in valid e-mail format.
return Regex.IsMatch(strIn,
#"^(?("")(""[^""]+?""#)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])#))" +
#"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-\w]*[0-9a-z]*\.)+[a-z0-9]{2,17}))$",
RegexOptions.IgnoreCase);
}
private string DomainMapper(Match match)
{
// IdnMapping class with default property values.
IdnMapping idn = new IdnMapping();
string domainName = match.Groups[2].Value;
try {
domainName = idn.GetAscii(domainName);
}
catch (ArgumentException) {
invalid = true;
}
return match.Groups[1].Value + domainName;
}
}
Otherwise, if it's for user registration, I usually just depend on email verification - i.e. the system sends the user an email with a link, and clicking the link therefore validates that the email address is real.
It is possible for a valid email address not to be a real one. In other words, it may even pass a perfect validation system, but that's no guarantee that it exists.
That might not be valid WWW domain name but on a private network I think it is a valid domain name. If you want to get more selective then Regex.
I try and parse legit email (eliminate phony used by spam) using Regex and it is messy.
If you have access to the actual email header then it does not really get easier but it get more accurate.
Related
Hi I am working on a bulk email sending feature. Below is my loop that validates email and sends them to each recipient:
foreach (var userID in recipientUserIds)
{
var userInfo = //getting from database using userID.
try
{
to = new MailAddress(userInfo.Email1, userInfo.FirstName + " " + userInfo.LastName);
}
catch (System.FormatException fe)
{
continue;
}
using (MailMessage message = new MailMessage(from, to))
{
//populate message and send email.
}
}
Since the recipientUserIds is always more than 2000, using try-catch seems to be very expensive for each user in this case just to validate the email address format. I was wondering to use regex, but not sure if that will help with performance.
So my question is if there is any better or performance optimized way to do the same validation.
Validating email addresses is a complicated task, and writing code to do all the validation up front would be pretty tricky. If you check the Remarks section of the MailAddress class documentation, you'll see that there are lots of strings that are considered a valid email address (including comments, bracketed domain names, and embedded quotes).
And since the source code is available, check out the ParseAddress method here, and you'll get an idea of the code you'd have to write to validate an email address yourself. It's a shame there's no public TryParse method we could use to avoid the exception being thrown.
So it's probably best to just do some simple validation first - ensure that it contains the minimum requirements for an email address (which literally appears to be user#domain, where domain does not have to contain a '.' character), and then let the exception handling take care of the rest:
foreach (var userID in recipientUserIds)
{
var userInfo = GetUserInfo(userID);
// Basic validation on length
var email = userInfo?.Email1?.Trim();
if (string.IsNullOrEmpty(email) || email.Length < 3) continue;
// Basic validation on '#' character position
var atIndex = email.IndexOf('#');
if (atIndex < 1 || atIndex == email.Length - 1) continue;
// Let try/catch handle the rest, because email addresses are complicated
MailAddress to;
try
{
to = new MailAddress(email, $"{userInfo.FirstName} {userInfo.LastName}");
}
catch (FormatException)
{
continue;
}
using (MailMessage message = new MailMessage(from, to))
{
// populate message and send email here
}
}
My objective is simple: pre-validate a new password client-side (Javascript) as an initial check the password matches the domain/ou password policy, to avoid wasting server resources rejecting bad passwords and give faster responses to users.
The question: How can I get user password policy from Active Directory?
I especially need to know the password "format", password length, capital and special characters requirements, etc. The final validation will, of course, be Active Directory itself. But first I want to use Javascript as a performance optimization, and I'm pretty sure I can manage the Javascript if I can just retrieve the password format requirements for a specific user/OU on the C#/ASP.Net end.
Currently, I'm stuck trying to find WHAT the current password policy is for the user. Yes, user Alice might use the password domain policy, but Bob could have a different password policy in his OU.
This website will be installed in an institution with thousands of users; we want to minimize the back and forth validation against Active Directory. Additionally, having this in Javascript can eventually help in compliance with NIST Special Publication 800-63, which among other things asks for prompt feedback to users on relative password strength. For now, I must be able to make the code work on Windows 2008, 2008 R2 and 2012.
I'm currently able to change the password in C#, and I can get the error, but it's it's all or nothing, and not helpful for client-side validation.
public static PasswordChangeResultsDTO ChangeUserPassword(PasswordChangeRequestDTO request)
{
try
{
bool isPasswordChanged = false;
SearchResult result = LdapHelper.GetUser(request.Username, request.OldPassword);
if (result != null)
{
using (DirectoryEntry userEntry = result.GetDirectoryEntry())
{
userEntry.Invoke("ChangePassword", new object[] {request.OldPassword, request.NewPassword});
userEntry.CommitChanges();
isPasswordChanged = true;
}
}
return new PasswordChangeResultsDTO {PasswordChanged = isPasswordChanged};
}
catch (COMException comException)
{
LoggingHelper.Instance.WriteException(comException);
string message = comException.ErrorCode == -2147022651
? "The password does not meet the password policy requirements"
: comException.Message;
return new PasswordChangeResultsDTO {PasswordChanged = false, Message = message};
}
catch (TargetInvocationException targetInvocationException)
{
LoggingHelper.Instance.WriteException(targetInvocationException);
string message;
if (targetInvocationException.InnerException != null)
{
var comException = targetInvocationException.InnerException as COMException;
if (comException != null)
{
message = comException.ErrorCode == -2147022651
? "The password does not meet the password policy requirements"
: comException.Message;
}
else
{
message = targetInvocationException.InnerException.Message;
}
}
else
{
message = targetInvocationException.Message;
}
return new PasswordChangeResultsDTO {PasswordChanged = false, Message = message};
}
catch (Exception ex)
{
string msgError = (null != ex.InnerException) ? ex.InnerException.Message : ex.Message;
string msgSource = (null != ex.InnerException) ? ex.InnerException.Source : ex.Source;
string msgStackTrace = (null != ex.InnerException) ? ex.InnerException.StackTrace : ex.StackTrace;
string msgOutput = String.Format(CultureInfo.InvariantCulture,
"Exception in {3} MSG[{0}] SOURCE[{1}] STACK[{2}]",
msgError, msgSource, msgStackTrace, MethodBase.GetCurrentMethod().Name);
LoggingHelper.Instance.Fatal(msgOutput);
throw;
}
}
Finding out this information at the domain level is easy. Figuring out if any Group Policies have overridden the default is hard.
At the domain level, there are attributes at the domain itself that govern the default password policy for the domain. You can bind to the domain itself (i.e. LDAP://domain.com) and read these attributes:
minPwdLength: The minimum character length
pwdHistoryLength: The number of old passwords that can't be reused.
pwdProperties: This is a bit flag that could mean various things, which you can read about under the "PasswordProperties" section here. It's likely to be set to 1 (DOMAIN_PASSWORD_COMPLEX), which means a password must include at least two of either uppercase, lowercase and numbers.
If you want to go through the effort to read group policies that would apply to the user's OU, there doesn't seem to be any .NET libraries to do that. You have to resort to using unmanaged code. There is an example here that uses the IGPMDomain interface from C#, but you will have to adapt it to find the GPO for the right OU.
Is there any way to make a textbox input validation for email addresses in wpf C#? Regex or validation expression or anything that can help, best with code sample and some instructions
On the text_changed event you could pass the value of the textbox to a helper class.
public static class ValidatorExtensions
{
public static bool IsValidEmailAddress(this string s)
{
Regex regex = new Regex(#"^[\w-\.]+#([\w-]+\.)+[\w-]{2,4}$");
return regex.IsMatch(s);
}
}
Now on the text changed event you can test the input
private void myTextBox_TextChanged(object sender, EventArgs e)
{
bool result = ValidatorExtensions.IsValidEmailAddress( myTextBox.Text );
}
There are several ways to check if the email address is valid
About System.Net.Mail.MailAddress
About Regex Expression
static void Main(string[] args)
{
var validMail = "validMail#gmail.com";
var invalidMail = "123";
Console.WriteLine("IsValidMailAddress1 Test");
Console.WriteLine(string.Format("Mail Address : {0} . is valid : {1}", validMail, IsValidMailAddress1(validMail)));
Console.WriteLine(string.Format("Mail Address : {0} . is valid : {1}", invalidMail, IsValidMailAddress1(invalidMail)));
Console.WriteLine("IsValidMailAddress2 Test");
Console.WriteLine(string.Format("Mail Address : {0} . is valid : {1}", validMail, IsValidMailAddress2(validMail)));
Console.WriteLine(string.Format("Mail Address : {0} . is valid : {1}", invalidMail, IsValidMailAddress2(invalidMail)));
}
static bool IsValidMailAddress1(string mail)
{
try
{
System.Net.Mail.MailAddress mailAddress = new System.Net.Mail.MailAddress(mail);
return true;
}
catch
{
return false;
}
}
static bool IsValidMailAddress2(string mailAddress)
{
return Regex.IsMatch(mailAddress, #"^([\w\.\-]+)#([\w\-]+)((\.(\w){2,3})+)$");
}
This is the best one I found:
Regex.IsMatch(emailAddress, #"\A(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)\Z", RegexOptions.IgnoreCase)
Actually, a great number of things have to be considered when validating email addresses:
Nearly all Regex expressions found on the Internet are not 100%
correct
Even if the Regex is doing a 100% perfect job, the email client like
Outlook or email server like Exchange might not accept the exotic,
but valid email address.
Just one example of a strange, but valid email address:
"very.(),:;<>[]\".VERY.\"very#\ \"very\".unusual"#strange.example.com
which is of the type:
John.Doe#example.com
But actually, RFC 5322: Internet Message Format: Address Specification specifies that en email address looks like this:
John Doe<John.Doe#example.com>
The first "John Doe" is the display name.
If you add display name validation, it becomes rather complicated. But even without display name, most regex fail to recognise the following email address as valid:
"John#Doe"#example.com
Therefore the recommendation is just to warn the user if an email address looks strange, but to let the user decide if he made a typo or if he actually wants to enter that email address.
Additionally to validation, it might be helpful when the user can key in only legal characters.
Read my article CodeProject: Email Address Validation Explained, it comes with the code of a fully implemented and tested WPF Email Textbox.
This is the code I am trying to use but I am not really getting anywhere with it:
public static string GetUserIdFromEmail(string emailAddress)
{
string displayName = string.Empty;
if (emailAddress.Contains("#sub.domain.edu") || emailAddress.Contains("#domain.edu"))
{
displayName = emailAddress.Split(new char[] { '#' })[0];
}
else
{
//no active directory account.
return string.Empty;
}
// set up domain context
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain))
{
// find user by display name
try
{
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, displayName);
if (user != null)
{
return user.SamAccountName; // or UserPrincipleName
}
else
{
return string.Empty;
}
}
catch (Exception)
{
return "Error";
}
}
}
These are the things I know regarding our current systems:
Emails are going to be unique and follow a certain naming convention
The naming convention is first letter of first name and whole last name (dstanley).
Some older accounts on Exchange are first.last#email.com
I have no idea what i am doing, but I know what I want.
I want to be able to take a users domain email address and find their record in active directory. Is the code above on the right track or is there something simpler that I can do?
In order to gain access to a users email using the standard active directory library you must first have the samaccount. This means that the solution to this problem requires you to iterate through every samaccount and compare the email address until you find a match.
PsuedoCode
foreach(user in activedirectory)
{
if (user.email = targeted_email)
{
return matched samaccount
}
}
I do not believe there is a better way to approach this and therefore it is a resource intensive solution.
I am trying to get a user's email address in AD without success.
String account = userAccount.Replace(#"Domain\", "");
DirectoryEntry entry = new DirectoryEntry();
try {
DirectorySearcher search = new DirectorySearcher(entry);
search.PropertiesToLoad.Add("mail"); // e-mail addressead
SearchResult result = search.FindOne();
if (result != null) {
return result.Properties["mail"][0].ToString();
} else {
return "Unknown User";
}
} catch (Exception ex) {
return ex.Message;
}
Can anyone see the issue or point in the right direction?
Disclaimer: This code doesn't search for a single exact match, so for domain\j_doe it may return domain\j_doe_from_external_department's email address if such similarly named account also exists. If such behaviour is undesirable, then either use a samAccountName filter intead of an anr one used below or filter the results additionally.
I have used this code successfully (where "account" is the user logon name without the domain (domain\account):
// get a DirectorySearcher object
DirectorySearcher search = new DirectorySearcher(entry);
// specify the search filter
search.Filter = "(&(objectClass=user)(anr=" + account + "))";
// specify which property values to return in the search
search.PropertiesToLoad.Add("givenName"); // first name
search.PropertiesToLoad.Add("sn"); // last name
search.PropertiesToLoad.Add("mail"); // smtp mail address
// perform the search
SearchResult result = search.FindOne();
You guys are working too hard:
// Look up the current user's email address
string eMail = UserPrincipal.Current.EmailAddress;
You can try the below GetUserEmail method. If You are looking out to find the email address for logged-in user in MVC then call the GetUserEmail() function with User.Identity.Name
using System.DirectoryServices;
using System.Linq;
public string GetUserEmail(string UserId)
{
var searcher = new DirectorySearcher("LDAP://" + UserId.Split('\\').First().ToLower())
{
Filter = "(&(ObjectClass=person)(sAMAccountName=" + UserId.Split('\\').Last().ToLower() + "))"
};
var result = searcher.FindOne();
if (result == null)
return string.Empty;
return result.Properties["mail"][0].ToString();
}
GetUserEmail(User.Identity.Name) //Get Logged in user email address
You forgot a filter.
Try adding this before calling FindOne:
search.Filter = String.Format("(sAMAccountName={0})", account);
What about this
public string GetEmailFromSamAccountName(string samAccountName, string domain="YOURCOMPANY")
{
using (var principalContext = new PrincipalContext(ContextType.Domain, domain))
{
var userPrincipal = UserPrincipal.FindByIdentity(principalContext, samAccountName);
return userPrincipal.EmailAddress;
}
}
Also, where do you pull the username from (stored, user input, current identity)? A username can change (be renamed) easily - the SID/Windows Logon Identity on the other hand does not change - so you would be better off doing filters/searches by SID rather than samaccountname - if possible and/or needed design-wise...
You need to add references for System.DirectoryServices.AccountManagement and include this same references in your using statement. Now you will have access to the current users login details as listed below include the email address.
string loginname = Environment.UserName;
string firstname = UserPrincipal.Current.GivenName;
string lastname = UserPrincipal.Current.Surname;
string name = UserPrincipal.Current.Name;
string eMail = UserPrincipal.Current.EmailAddress;
update: fredrick nailed it....
Jakob is right. You need to filter your search. You can do all sorts of ands and ors there too if you need to, but I think sAMAccountName is enough. You might want to fire up the ADSI tool (it's in the resource kit I think), which lets you walk AD like the registry. it's great for looking at properties. Then find a user, work out what prop you want (mail in this case) and what it's primary key is - sAMAccountName is a good one, but you may also want to filter on the node type.
I'm on a mac, so I can't check it for you, but each node in AD has a type, and you can add that to your filter. I think it looks like this:
((sAMAccountName=bob) & (type=User))
Again, check that - I know it's not type=user, but something LIKE that.