I've looked at the UserHttpClient, ProfileHttpClient and GraphHttpClient.
I cannot figure out how I can retrieve the email address from any of those when I have an IdentityRef from a work item (the "AssignedTo" field).
Earlier I assumed that the uniquename field always where the email address, but that seems not to be the case for premise installations?
I finally figured it out.
The identityRef contains a field called Descriptor which corresponds to the "user descriptor" in the ProfileHttpClient (Rest api).
Thus, to get the email one have to do the following:
public static Task<string> GetEmailAddress(this VssConnection connection, SubjectDescriptor descriptor)
{
var client = connection.GetClient<GraphHttpClient>();
var user = await client.GetUserAsync(descriptor.ToString());
return user?.MailAddress;
}
// .. and in your code (where assignedTo is an IdentityRef).
var email = await connection.GetEmailAddress(assignedTo.Descriptor);
Update
This doesn't work on Azure DevOps Server as the Graph is not available on it. So the question remains.
(Leaves this as an answer for the cloud version)
Related
First I will talk of what both of the projects to get an idea of what I'm doing
gRPC Server
Have Context DB and some services ( the User AddUser(User user) ) and the (IEnumerable getAllUsers())
The DB calls Users_1
Proto (Server Part) have some methods like a InsertNewUser, SelectAllUsers and here also have a SelectByID and Update method but not implement in the services yet
UserService (using the proto file) that have InsertNewUser and SelectAllUsers done, need to do the SelectByID and the Update method
To Select/Update a user I have some messages like this
service UserAuth {
rpc SelectAll (Empty) returns (stream ShowUser); //this is ok and it is in the UserService
rpc SelectByID (UserFilter) returns (ShowUser);
rpc InsertNew (RUser) returns (UserFilter); //this is ok and it is in the UserService
rpc Update (ShowUser) returns (Empty);
}
message Empty {
}
message RUser {
string username = 1;
string password = 2;
string email = 3;
int32 age = 4;
}
message ShowUser {
string username = 1;
string email = 2;
int32 age = 3;
bool test = 4;
}
message UserFilter
{
string userID = 1;
}
In the WebApi I have this
methods [httpPost] InsertNewUser(User user) and [httpGet] SelectAll() and this is using the gRPC Server that I talk previously (the InsertNewUser will be my register). Both works fine
This is use protos too (but in the Client part)
method [httpPost] Login(User user) that creates a token and also check if the user is in
the gRPC DB or not
Need to add the [httpGet] SelectByID(Guid UserFilter) -> return an user, and the [httpPut] Update(User user) BUT and here is where my problem comes in
The methods I want to do can only be given to the user who has been logged in, that is, he can see his information and also edit his own information (and not the information of another user)
That is, I will have to save the user who logged in in something similar to "localStorage()", now will it be possible to do this in two different projects? Is it possible to use it in a Web Api-only method (which does not use gRPC) to put this user in a Select/Update method that will be used in the WebApi but it calls methods coming from gRPC?
If you don't understand what I want I can then send the signature of the methods but the ideia of "what is my problem is?" is was well explained
Any answer is welcome
I have .NET Web API Project for the fulfillment API as our webhook in my Dialogflow agent. In our Post method of the controller, after getting the request from Dialogflow, I implement the explicit authentication as shown in the Google Cloud documentation for C#.
//jsonFileName is the name of the serviceAccountKey json generated from the Google Cloud Platform that's encrypted internally
public bool AuthExplicit(string projectId, string jsonFileName)
{
try
{
string JsonCredential = DecryptHelper.Decrypt(jsonFileName);
var credential = GoogleCredential.FromJson(JsonCredential).CreateScoped(LanguageServiceClient.DefaultScopes);
var channel = new Grpc.Core.Channel(
LanguageServiceClient.DefaultEndpoint.ToString(),
credential.ToChannelCredentials());
var client = LanguageServiceClient.Create(channel);
AnalyzeSentiment(client);
if (client != null)
{
return true;
}
else
{
return false;
}
}
internal void AnalyzeSentiment(LanguageServiceClient client)
{
var response = client.AnalyzeSentiment(new Document()
{
Content = "Authenticated.",
Type = Document.Types.Type.PlainText
});
var sentiment = response.DocumentSentiment;
string score = $"Score: {sentiment.Score}";
string magnitude = $"Magnitude: {sentiment.Magnitude}";
}
The difference with the code is that after getting the client, when we call the AnalyzeSentiment() method, it doesn't do anything, and the projectId parameter is never used to authenticate. GCP docs are quite confusing, since when there is an AuthExplicit() that uses projectId, it uses it as a parameter for the buckets and only prints this on the console.
It works fine, until we test the service account key with a different agent. Expected output is that authentication would fail, but somehow it still passes.
Once the Post method goes through the AuthExplicit() method, it would only return a boolean. Is this the right way to authenticate? Or is there something else needed to invoke?
The difference with the code is that after getting the client, when we call the AnalyzeSentiment() method, it doesn't do anything,
Does client.AnalyzeSentiment() return an empty response? Does the call hang forever?
It works fine, until we test the service account key with a different agent.
What is a different agent? A different User-Agent header?
Once the Post method goes through the AuthExplicit() method, it would only return a boolean. Is this the right way to authenticate? Or is there something else needed to invoke?
What does 'the Post method' refer to? What is the 'it' that would only return a boolean?
I know how to send targeted messages from the various tutorials with code like that:
// Get the current user SID and create a tag for the current user.
var claimsPrincipal = this.User as ClaimsPrincipal;
string sid = claimsPrincipal.FindFirst(ClaimTypes.NameIdentifier).Value;
string userTag = "_UserId:" + sid;
// Build a dictionary for the template with the item message text.
var notification = new Dictionary { { "message", item.Text } };
// Send a template notification to the user ID.
await hub.SendTemplateNotificationAsync(notification, userTag);
But I struggle to figure out how to send to all my registered UserIds.
I hope there is some method like:
// Send a template notification to all UserIds
await hub.SendTemplateNotificationAsync(notification);
Yes, there's an overload of SendTemplateNotificationAsync that looks exactly as you described:
public Task<NotificationOutcome> SendTemplateNotificationAsync(
IDictionary<string, string> properties)
Does it not work for you?
An alternative would be to register your users with an additional tag (e.g. "registered user" for all users or "paid user"/"trial user" if you need to slice them somehow) and then use the method you're currently using to send messages, but to this broader tag.
I am trying to get the email address of a particular user in TFS 2012 using the API. I have set the users Preferred Email address in the Profile section. I have done plenty of searching online and have the following code.
var userId = "myUserId";
var collection = new TfsTeamProjectCollection(tfsUri, tfsCerd);
var managementService = collection.GetService<IIdentityManagementService>();
var member =
managementService
.ReadIdentity(
IdentitySearchFactor.AccountName,
userId,
MembershipQuery.Direct,
ReadIdentityOptions.ExtendedProperties);
var emailAddress = member.GetAttribute("Mail", null)
This code is both a success and a failure. It is a success in that it successfully retrieves the specified user; however, the problem is that the Email attribute is blank. When I analyzed the member variable, I noticed the "Mail" attribute was listed there and it was empty. I then noticed there were two other attributes called "ConfirmedNotificationAddress" and "CustomNotificationAddress" that had my preferred email address correctly in there.
I am wondering why I can't seem to get the "Mail" variable to load properly with the preferred email address as I will need this code to work on a lot of peoples servers.
Try using Mail instead of Email for the attribute name - that works for me.
Also, if that doesn't work, check the results of member.GetProperties() - maybe that will give you the right name to use.
For me, GetProperty("Mail") also worked.
I bumped into the same problem, I found a work around by getting my users email address from AD using the following code.
public string GetUserEmail(string username)
{
using (var pctx = new PrincipalContext(ContextType.Domain))
{
using (UserPrincipal up = UserPrincipal.FindByIdentity(pctx, username))
{
return up != null && !string.IsNullOrEmpty(up.EmailAddress) ? up.EmailAddress : string.Empty;
}
}
}
But then I found that it would throw an exception when my user was not in my domain. So this code helped me have an a second source. If I didn't find in AD i would go and use the IdentityManagementService.
public TeamFoundationIdentity GetUserByAccountName(string account)
{
var ims = _tfServer.GetService<IIdentityManagementService>();
return ims.ReadIdentity(IdentitySearchFactor.DisplayName, account, MembershipQuery.Expanded, ReadIdentityOptions.ExtendedProperties);
}
Then I would simply use this execution.
var ownerMail = GetUserEmail(checkinEvent.Resource.CheckedInBy.DisplayName);
if (string.IsNullOrEmpty(ownerMail))
{
ownerMail = GetUserByAccountName(checkinEvent.Resource.CheckedInBy.DisplayName).GetProperty("Mail").ToString();
}
Using the Exchange Web Services API, is it possible to determine whether a mailbox/e-mail address such as someone#mydomain.com exists within an organization?
If so, which is the simplest way to do this and is it possible without the use of impersonation?
Case: A Windows Service regularly sends e-mails to people within the organization. It does not have any explicit knowledge about their e-mail adresses. It only knows their username and assumes that their e-mail address is username#mydomain.com. This is true for all users except for a few that do not have mailboxes. In these cases, it should not attempt to send the e-mail in the first place.
Solution:
As suggested by mathieu: look for user and e-mail address in Active Directory instead. This function gets the job done:
using System.DirectoryServices.AccountManagement;
// ...
public static bool TryGetUserEmailAddress(string userName, out string email)
{
using (PrincipalContext domainContext =
new PrincipalContext(ContextType.Domain, Environment.UserDomainName))
using (UserPrincipal user =
UserPrincipal.FindByIdentity(domainContext, userName))
{
if (user != null && !string.IsNullOrWhiteSpace(user.EmailAddress))
{
email = user.EmailAddress;
return true;
}
}
email = null;
return false; // user not found or no e-mail address specified
}
Determining if an user has a mailbox with EWS only could be more complicated than expected, especially without impersonation.
If you're in an Active Directory domain, you should rely on the DirectoryEntry information to determine the mailbox of an user, and send email accordingly. If you got your user login, it's really easy to get the associated DirectoryEntry.
there is an easy way to do it by checking the user availability like the following code.
I tried this and it is working for me.
I am not sure about other cases when availability result returns error but for sure when the email is not right it does
to define your exchange service refer to this: https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/get-started-with-ews-managed-api-client-applications
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2007_SP1);//You version
service.Credentials = new WebCredentials("user1#contoso.com", "password");
service.AutodiscoverUrl("user1#contoso.com", RedirectionUrlValidationCallback);
string email = "TEST#YOUR.COM";
// Get User Availability after 6 months
AttendeeInfo attendee = new AttendeeInfo(email);
var attnds = new List<AttendeeInfo>();
attnds.Add(attendee);
var freeTime = service.GetUserAvailability(attnds, new
TimeWindow(DateTime.Now.AddMonths(6), DateTime.Now.AddMonths(6).AddDays(1)), AvailabilityData.FreeBusyAndSuggestions);
//if you receive result with error then there is a big possibility that the email is not right
if(freetimes.AttendeesAvailability.OverallResult == ServiceResult.Error)
{
return false;
}
return true;