How to send user defined details to activity in Bot Framework App Or How to access BotData in my dialog class - bots

I have two questions
1) How to send custom values to Activity in Bot Framework?
I have below code in post method
UserDetails usr = new UserDetails();
usr.LoginID = l.LoginID
userData.SetProperty<string>("LoginID", usr.LoginID);
sc.BotState.SetUserData(activity.ChannelId, activity.From.Id, userData);
Now I want to access this property in my Dialog/FormBuilder class, How to achieve this?
Below is my formbuilder class
[Serializable]
public class FlightBooking
{
public static IForm<FlightBooking> BuildForm()
{
return new FormBuilder<FlightBooking>().Message("Tell me flight details!")
.Field(nameof(title))
....
....
}
}
2) How to access BotData user defined properties in FormBuilder/Dialog class?
As in above code, you can see that I have set EmailId property, how to access that property value in formbuilder class?

1) I don't understand what you're doing with BotState. The question you asked is "How to send custom values to Activity...", but you are actually saving values into UserData. Also, the code you've provided does not seem complete.
You can save UserData like this:
var stateClient = activity.GetStateClient();
BotData userData = stateClient.BotState.GetUserData(activity.ChannelId, activity.From.Id);
userData.SetProperty<string>("LoginID", l.LoginID);
stateClient.BotState.SetUserData(activity.ChannelId, activity.From.Id, userData);
2) You can send the activity.ChannelId and activity.From.Id to the BuildForm method:
await Conversation.SendAsync(activity, ()=> FormDialog.FromForm(() => FlightBooking.BuildForm(activity.ChannelId, activity.From.Id)));
and create the StateClient, and access the UserData:
public class FlightBooking
{
public string title { get; set; }
public static IForm<FlightBooking> BuildForm(string channelId, string userId)
{
string appId = ConfigurationManager.AppSettings["MicrosoftAppId"];
string password = ConfigurationManager.AppSettings["MicrosoftAppPassword"];
StateClient stateClient = new StateClient(new MicrosoftAppCredentials(appId, password));
BotData userData = stateClient.BotState.GetUserData(channelId, userId);
var loginId = userData.GetProperty<string>("LoginID");
//do something with loginId?
return new FormBuilder<FlightBooking>().Message("Tell me flight details!")
.Field(nameof(title))
.Build();
}
}
But, you could also just send the entire activity object to the BuildForm method, and not bother with UserData at all.

Related

Jhipster Microservices - Correct way to get UserId in Microservices?

I am using JHipster's Gateway with JWT and I have a microservice.
When the rest call is forwarded from the gateway to the microservice, in the microservice business class, I want to get the user id of the authenticated user.
The reason for this is I want to save it in the DB with the entity so that one user's data can be completely separate from other user's data (and a user cannot update another user's data...etc..).
While I can get the logged in user name, I don't have the user id.
What is the correct approach to resolving this issue:
call the gateway from the microservice ?
(this doesn't make too much sense to me as the gateway is calling the service and I'll want to know this info for most services).
update the TokenProvider in the gateway to include a user Id ? (not certain how to do this). Is this the correct approach ?
any other suggestions ?
Thanks,
Fergal.
Note: I see other similar questions. This is not a duplicate question. Do not mark this a duplicate unless absolutely certain. Note - I am using JWT
To solve this, I added the user id in the token from the gateway to each microservice.
Here is how I solved this in the JHipster generated code:
In Gateway, add UserService to UserJWTController, and get the user id, and
use it when you are creating a token.
public ResponseEntity<JWTToken> authorize(#Valid #RequestBody LoginVM loginVM) {
...
...
Optional<User> user = userService.getUserWithAuthoritiesByLogin(loginVM.getUsername());
Long userId = user.get().getId();
String jwt = tokenProvider.createToken(authentication, rememberMe, userId);
...
add the claim to the token:
claim(USER_ID_KEY, userId)
note, I added this to Token Provider:
private static final String USER_ID_KEY = "userId";
and then in my microservice's app, I did this:
created a new class:
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import java.util.Collection;
public class SamAuthenticationToken extends UsernamePasswordAuthenticationToken {
public Long getUserId() {
return userId;
}
private final Long userId;
public SamAuthenticationToken(Object principal, Object credentials, Long userId) {
super(principal, credentials);
this.userId = userId;
}
public SamAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities, Long userId) {
super(principal, credentials, authorities);
this.userId = userId;
}
}
and then I changed TokenProvider.getAuthentication to add the following lines:
Long userId = null;
Object userIdObj = claims.get(USER_ID_KEY);
if (userIdObj != null) {
String userIdStr = userIdObj.toString();
userId = Long.parseLong(userIdStr);
log.debug("Claim--> {}", userId);
} else {
log.debug("No user id in token");
}
User principal = new User(claims.getSubject(), "", authorities);
return new SamAuthenticationToken(principal, token, authorities, userId);
and then I added a new method to SecurityUtils
public static Optional<Long> getUserId() {
SecurityContext securityContext = SecurityContextHolder.getContext();
return Optional.ofNullable(securityContext.getAuthentication())
.map(authentication -> {
if (authentication instanceof SamAuthenticationToken) {
SamAuthenticationToken samAuthenticationToken = (SamAuthenticationToken) authentication;
return samAuthenticationToken.getUserId();
}
return null;
});
}
and finally, I can now call this method from any Business class:
Optional<Long> userId = SecurityUtils.getUserId();
if (userId.isPresent()) {
log.info("User Id--->{}", userId.get());
} else {
log.info("No userId present.");
}
Any feedback welcome.

How to deal with Context.Done(R value)

I have a msbot chat dialog that I want to have the following behaviour:
user -> get me some info about GARY
bot -> which gary, (prompt: choice options)
user -> gary peskett
bot -> sure, (hero card with gary's contact details)
I have this code
public class CustomerRepository
{
private IList<Customer> _customerList = new List<Customer>
{
new Customer
{
Name = "Gary Peskett"
},
new Customer
{
Name = "Gary Richards"
},
new Customer
{
Name = "Barry White"
}
};
public async Task<IEnumerable<Customer>> GetAll()
{
// usually calls a database (which is why async is on this method)
return _customerList;
}
}
public class XDialog : IDialog
{
private readonly IIntent _intent;
private readonly CustomerRepository _customerRepository;
public XDialog(IIntent intent, CustomerRepository customerRepository)
{
// An intent is decided before this point
_intent = intent;
_customerRepository = customerRepository;
}
public async Task StartAsync(IDialogContext context)
{
// // An intent can provide parameters
string name = _intent.Parameters["Name"] as string;
IEnumerable<Customer> customers = await _customerRepository.GetAll();
IList<Customer> limitedList = customers.Where(x => x.Name.Contains(name)).ToList();
if (limitedList.Any())
{
if (limitedList.Count > 1)
{
PromptDialog.Choice(context, LimitListAgain, limitedList,
"Can you specify which customer you wanted?");
}
else
{
Customer customer = limitedList.FirstOrDefault();
Finish(context, customer);
}
}
else
{
context.Done("No customers have been found");
}
}
private static async Task LimitListAgain(IDialogContext context, IAwaitable<Customer> result)
{
Customer customer = await result;
Finish(context, customer);
}
private static void Finish(IDialogContext context, Customer customer)
{
HeroCard heroCard = new HeroCard
{
Title = customer?.Name
};
context.Done(heroCard);
}
}
What i'm finding is that usually when I do context.Done(STRING) then that is output to the user, and this is really useful to end the dialog. As I want to end with a hero card, its outputing the typename
Microsoft.Bot.Connector.HeroCard
Can anyone help by either explaining a better way to use context.Done(R value) or help me return a hero card to end the dialog?
The dialog is being called with
Chain.PostToChain()
.Select(msg => Task.Run(() => _intentionService.Get(msg.ChannelId, msg.From.Id, msg.Text)).Result)
.Select(intent => _actionDialogFactory.Create(intent)) // returns IDialog based on intent
.Unwrap()
.PostToUser();
I think the problem is a side effect of using Chain.
As you may know, the context.Done doesn't post anything back to the user, it just ends the current dialog with the value provided.
The post to user is effectively happening in the .PostToUser() at the end of your Chain. Now, by looking into the PostToUser's code, I realized that at the end of the game, it's doing a context.PostAsync of item.ToString(), being item the payload provided in the context.Done in this case. See this.
One option (I haven't tested this), could be using .Do instead of .PostToUser() and manually perform what the PostToUserDialog does and finally perform a context.PostAsync() by creating a new IMessageActivity and adding the HeroCard as an attachment.

How to get user object of currently logged in user

What is the correct way to get an Identity User Object (from the identity framework) of the currently logged in user in a controller?
I need to update some properties against the user (table AspNetUsers) and do not know the correct method of getting the user object so that I could do things such as:
var menuItem = context.MenuItems.First(m => m.Description == "New Order");
var user = ??????????
user.MenuItems.Add(menuItem);
context.SaveChanges();
I've slightly modified the original user model by adding a few properties and renaming the class:
public class User : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<User> manager)
{
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
return userIdentity;
}
public virtual ICollection<MenuItem> MenuItems { get; set; }
}
So how can I retrieve this User object from my controllers?
In the controller class use the namespace Microsoft.AspNet.Identity and the get the current user like this:
var user = User.Identity;
Example:
using Microsoft.AspNet.Identity;
...
public class FooController : Controller
{
[Authorize]
public ActionResult Index()
{
var user = User.Identity;
...
return View();
}
}
Edited
Getting Profile Information:
This link gives the steps to add profile information to your user.
To retrieve your user data you should:
Get the current logged in UserId, so you can look the user up in
ASP.NET Identity system
var currentUserId = User.Identity.GetUserId();
Instantiate the UserManager in ASP.Identity system so you can look
up the user in the system
var manager = new UserManager(new UserStore(new MyDbContext()));
Get the User object
var currentUser = manager.FindById(User.Identity.GetUserId());
Get the profile information about the user
currentUser.MyUserInfo.FirstName
Get username
var userName = User.Identity.Name;
Get the user using static method created below.
var user = GetCurrentUser(userName);
Static method to get user
public static User GetCurrentUser(string userName)
{
var userManager = new UserManager<User>(new UserStore<User>(new YourIdentityDbContext()));
var user = userManager.FindByName(userName);
return user;
}

ASP.net Identity Disable User

Using the new ASP.net Identity in MVC 5, How do we disable a user from logging in? I don't want to delete them, maybe just disable their account for a time period.
Does anyone have any ideas on this as I don't see a status column or anything on the ASPNetUsers table.
await userManager.SetLockoutEnabledAsync(applicationUser.Id, true);
await userManager.SetLockoutEndDateAsync(DateTime.Today.AddYears(10));
Update: As CountZero points out, if you're using v2.1+, then you should try and use the lockout functionality they added first, before trying the solution below. See their blog post for a full sample: http://blogs.msdn.com/b/webdev/archive/2014/08/05/announcing-rtm-of-asp-net-identity-2-1-0.aspx
Version 2.0 has the IUserLockoutStore interface that you can use to lockout users, but the downside is that there is no OOB functionality to actually leverage it beyond the pass-through methods exposed by the UserManager class. For instance, it would be nice if it would actually increment the lockout count as a part of the standard username/password verification process. However, it's fairly trivial to implement yourself.
Step #1: Create a custom user store that implements IUserLockoutStore.
// I'm specifying the TKey generic param here since we use int's for our DB keys
// you may need to customize this for your environment
public class MyUserStore : IUserLockoutStore<MyUser, int>
{
// IUserStore implementation here
public Task<DateTimeOffset> GetLockoutEndDateAsync(MyUser user)
{
//..
}
public Task SetLockoutEndDateAsync(MyUser user, DateTimeOffset lockoutEnd)
{
//..
}
public Task<int> IncrementAccessFailedCountAsync(MyUser user)
{
//..
}
public Task ResetAccessFailedCountAsync(MyUser user)
{
//..
}
public Task<int> GetAccessFailedCountAsync(MyUser user)
{
//..
}
public Task<bool> GetLockoutEnabledAsync(MyUser user)
{
//..
}
public Task SetLockoutEnabledAsync(MyUser user, bool enabled)
{
//..
}
}
Step #2: Instead of UserManager, use the following class in your login/logout actions, passing it an instance of your custom user store.
public class LockingUserManager<TUser, TKey> : UserManager<TUser, TKey>
where TUser : class, IUser<TKey>
where TKey : IEquatable<TKey>
{
private readonly IUserLockoutStore<TUser, TKey> _userLockoutStore;
public LockingUserManager(IUserLockoutStore<TUser, TKey> store)
: base(store)
{
if (store == null) throw new ArgumentNullException("store");
_userLockoutStore = store;
}
public override async Task<TUser> FindAsync(string userName, string password)
{
var user = await FindByNameAsync(userName);
if (user == null) return null;
var isUserLockedOut = await GetLockoutEnabled(user);
if (isUserLockedOut) return user;
var isPasswordValid = await CheckPasswordAsync(user, password);
if (isPasswordValid)
{
await _userLockoutStore.ResetAccessFailedCountAsync(user);
}
else
{
await IncrementAccessFailedCount(user);
user = null;
}
return user;
}
private async Task<bool> GetLockoutEnabled(TUser user)
{
var isLockoutEnabled = await _userLockoutStore.GetLockoutEnabledAsync(user);
if (isLockoutEnabled == false) return false;
var shouldRemoveLockout = DateTime.Now >= await _userLockoutStore.GetLockoutEndDateAsync(user);
if (shouldRemoveLockout)
{
await _userLockoutStore.ResetAccessFailedCountAsync(user);
await _userLockoutStore.SetLockoutEnabledAsync(user, false);
return false;
}
return true;
}
private async Task IncrementAccessFailedCount(TUser user)
{
var accessFailedCount = await _userLockoutStore.IncrementAccessFailedCountAsync(user);
var shouldLockoutUser = accessFailedCount > MaxFailedAccessAttemptsBeforeLockout;
if (shouldLockoutUser)
{
await _userLockoutStore.SetLockoutEnabledAsync(user, true);
var lockoutEndDate = new DateTimeOffset(DateTime.Now + DefaultAccountLockoutTimeSpan);
await _userLockoutStore.SetLockoutEndDateAsync(user, lockoutEndDate);
}
}
}
Example:
[AllowAnonymous]
[HttpPost]
public async Task<ActionResult> Login(string userName, string password)
{
var userManager = new LockingUserManager<MyUser, int>(new MyUserStore())
{
DefaultAccountLockoutTimeSpan = /* get from appSettings */,
MaxFailedAccessAttemptsBeforeLockout = /* get from appSettings */
};
var user = await userManager.FindAsync(userName, password);
if (user == null)
{
// bad username or password; take appropriate action
}
if (await _userManager.GetLockoutEnabledAsync(user.Id))
{
// user is locked out; take appropriate action
}
// username and password are good
// mark user as authenticated and redirect to post-login landing page
}
If you want to manually lock someone out, you can set whatever flag you're checking in MyUserStore.GetLockoutEnabledAsync().
You can have a new class, which should be derived from IdentityUser class. YOu can add a boolean property in the new class and can use this new property of take care per check for login process. I also done it pretty well. I might wanna take a look at : blog
UserManager.RemovePasswordAsync("userId") will effectively disable a user. If the user has no password he will not be able to log in. You will need to set a new password to enable the user again.

Get Resource based on currently authenticated user

If I have an operation using ServiceStack such as GetOrders:
[Route("/orders")]
public class GetOrders : IReturn<List<Order>> { }
I then use this in a service:
[Authenticate]
public class OrdersService : Service
{
public object Get(GetOrders request)
{
var dbOrders = Db.Select<Order>().ToList();
// What I want is to only get orders of the user making the request
// var dbOrders = Db.Select<Order>().Where(x=>x.UserId == ??).ToList();
return dbOrders;
}
}
Assuming my Order entity has a property called UserId, how do I get access to the currently logged in user where I can then map to the UserId and select only those orders from my database?
You can get access to your typed UserSession via the SessionAs<T> method, e.g:
[Authenticate]
public class OrdersService : Service
{
public object Get(GetOrders request)
{
var userSession = base.SessionAs<AuthUserSession>();
var userId = int.Parse(userSession.UserAuthId);
var dbOrders = Db.Select<Order>(x => x.UserId == userId);
return dbOrders;
}
}

Resources