I have customized sign-up page using a hook.
I want to add the security question in the sign up page where as the default security question is set when user logs-in Liferay the first time.
So how can I add security question in the sign-up page instead on first time login?
Firstly, you have to customize the create_account.jsp page, adding the same select boxes and inputs normally found on update_reminder_query.jsp: that is, you have to add the UI for entering the password reminder while creating the account.
After that, you have to save those values for the new User, and you can probably hook the UserLocalService implementation, adding the code to update the reminder query. A thing like this should work:
public class UserLocalServiceImpl extends UserLocalServiceWrapper {
public UserLocalServiceImpl(UserLocalService userLocalService) {
super(userLocalService);
}
#Override
public User addUser(
long creatorUserId, long companyId, boolean autoPassword,
String password1, String password2, boolean autoScreenName,
String screenName, String emailAddress, long facebookId,
String openId, Locale locale, String firstName, String middleName,
String lastName, int prefixId, int suffixId, boolean male,
int birthdayMonth, int birthdayDay, int birthdayYear,
String jobTitle, long[] groupIds, long[] organizationIds,
long[] roleIds, long[] userGroupIds, boolean sendEmail,
ServiceContext serviceContext)
throws PortalException, SystemException {
// User
User user = super.addUser(
creatorUserId, companyId, autoPassword, password1, password2,
autoScreenName, screenName, emailAddress, facebookId, openId,
locale, firstName, middleName, lastName, prefixId, suffixId, male,
birthdayMonth, birthdayDay, birthdayYear, jobTitle, groupIds,
organizationIds, roleIds, userGroupIds, sendEmail, serviceContext);
// Reminder query
String question = ParamUtil.getString(
serviceContext, "reminderQueryQuestion");
String answer = ParamUtil.getString(
serviceContext, "reminderQueryAnswer");
if (Validator.isNotNull(question) || Validator.isNotNull(answer)) {
if (question.equals(UsersAdminUtil.CUSTOM_QUESTION)) {
question = ParamUtil.getString(
serviceContext, "reminderQueryCustomQuestion");
}
updateReminderQuery(user.getUserId(), question, answer);
}
return user;
}
}
You could override the CreateAccount Struts actions, but I think it's safer to do this thing in the service, in order to be transactional in case of errors (for example, if the User does not enter an answer for the reminder query)... but you should check it to be sure!
Also, you'll probably have to customize the Struts action anyway, in order to deal with a possible UserReminderQueryException...
Related
In a Jhipster 4.4.1 application with Mongodb, JWT.
I need the user logged in for a query, and I do not know how I can retrieve it in a java controller (Resource)
In SecurityUtils I do not see how to get the ID
public static String getCurrentUserLogin() {
SecurityContext securityContext = SecurityContextHolder.getContext();
Authentication authentication = securityContext.getAuthentication();
String userName = null;
if (authentication != null) {
if (authentication.getPrincipal() instanceof UserDetails) {
UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal();
userName = springSecurityUser.getUsername();
} else if (authentication.getPrincipal() instanceof String) {
userName = (String) authentication.getPrincipal();
}
}
return userName;
}
I can capture the user ID logged in, without consulting the database.
Thank you
If you need the current user, then you can query against login as given by getCurrentUserLogin() in your question because it's unique anyway.
If you really want the ID, then have a poke around AccountResource and you'll see that UserDTO and User have a .getID() method.
In my MVC 5 web app I have this (in AccountController.cs):
// Used for XSRF protection when adding external sign ins
private const string XsrfKey = "XsrfId";
and
public string SocialAccountProvider { get; set; }
public string RedirectUri { get; set; }
public string UserId { get; set; }
public override void ExecuteResult(ControllerContext context)
{
var properties = new AuthenticationProperties { RedirectUri = RedirectUri };
if (UserId != null)
{
properties.Dictionary[XsrfKey] = UserId;
}
context.HttpContext.GetOwinContext().Authentication.Challenge(properties, SocialAccountProvider);
}
How exactly is it being used for protection?
Should I set the value of XsrfKey to something more random?
Take a look at ManageController methods LinkLogin and LinkLoginCallback:
//
// POST: /Manage/LinkLogin
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LinkLogin(string provider)
{
// Request a redirect to the external login provider to link a login for the current user
return new AccountController.ChallengeResult(provider, Url.Action("LinkLoginCallback", "Manage"), User.Identity.GetUserId());
}
//
// GET: /Manage/LinkLoginCallback
public async Task<ActionResult> LinkLoginCallback()
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId());
if (loginInfo == null)
{
return RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error });
}
var result = await UserManager.AddLoginAsync(User.Identity.GetUserId(), loginInfo.Login);
return result.Succeeded ? RedirectToAction("ManageLogins") : RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error });
}
These are the methods that handle linking of external accounts (i.e. Google, Facebook, etc.). The flow goes like this:
User clicks "Link Account" button, which calls a POST to LinkLogin method.
LinkLogin returns ChallengeResult object, with callback url set to LinkLoginCallback method.
ChallengeResult.ExecuteResult is called by MVC framework, calls IAuthenticationManager.Challenge, which causes a redirect to the specific external login provider (let's say: google).
User authenticates with google, then google redirects to callback url.
The callback is handled with LinkLoginCallback. Here, we want to prevent XSRF and verify that the call was initiated by a user, from a page served by our server (and not by some malicious site).
Normally, if it was a simple GET-POST sequence, you would add a hidden <input> field with an anti-forgery token and compare it with a corresponding cookie value (that's how Asp.Net Anti-Forgery Tokens work).
Here, the request comes from external auth provider (google in our example). So we need to give the anti-forgery token to google and google should include it in the callback request. That's exactly what state parameter in OAuth2 was designed for.
Back to our XsrfKey: everything you put in AuthenticationProperties.Dictionary will be serialized and included in the state parameter of OAuth2 request - and consequentially, OAuth2 callback. Now, GetExternalLoginInfoAsync(this IAuthenticationManager manager, string xsrfKey, string expectedValue) will look for the XsrfKey in the received state Dictionary and compare it to the expectedValue. It will return an ExternalLoginInfo only if the values are equal.
So, answering your original question: you can set XsrfKey to anything you want, as long as the same key is used when setting and reading it. It doesn't make much sense to set it to anything random - the state parameter is encrypted, so no one expect you will be able to read it anyway.
Just leave it as is:
As the name of the member states it is a key:
private const string XsrfKey = "XsrfId";
It is defined in this manner to avoid "magic numbers" and then is used a little down in the scaffold code:
public override void ExecuteResult(ControllerContext context)
{
var properties = new AuthenticationProperties { RedirectUri = RedirectUri };
if (UserId != null)
{
properties.Dictionary[XsrfKey] = UserId;
}
context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);
}
The value of the dictionary item is then set to the UserId property in the above code by using the XsrfKey member as the key.
IOW the code is already setting the XSRF dictionary item to the value of the user ID in the snippet. If you change the XsrfKey members value to anything else you will cause problems down the line, since the expected key "XsrfId" will have no value set.
If by changing it to something more random you are implying to change the value and not they key of the dictionary, or in other words, not set it to the user id then please see the following for an explanation of the anti forgery token inner workings.
http://www.asp.net/mvc/overview/security/xsrfcsrf-prevention-in-aspnet-mvc-and-web-pages
I've been working with Directory Services since the last few days.
Using the UserPrincipal Object, I've tried to get the email and company field from current AD user. Email is not a problem since it's exposed by default. Company is an other story though.
I found in here a post that explains how to do so: Get job title using System.DirectoryServices.AccountManagement
Although, I am having an unfortunate issue with this method. The post shows how to create the FindByIdentity method in the extended class, but in order for that to work you have to set search type to your extended class type, which has for result to find no entries for my specific user. If I set the search type to UserPrincipal in the FindByIdentityWithType, it does find my AD user, but as you can expect, I'm getting an conversion error.
So My question is very simple, Is there any known way to Find by Identity in an extended class?
For Reference here is my extended class:
[DirectoryObjectClass("group")]
[DirectoryRdnPrefix("CN")]
public class UserPrincipalClassExtensions : System.DirectoryServices.AccountManagement.UserPrincipal
{
PrincipalContext context;
public UserPrincipalClassExtensions(PrincipalContext context)
: base(context)
{ this.context = context; }
public UserPrincipalClassExtensions(PrincipalContext context, string samAccountName, string Password,bool enabled)
: base(context, samAccountName, Password, enabled)
{
}
[DirectoryProperty("company")]
public string Company
{
get
{
if (ExtensionGet("company").Length != 1)
return null;
return (string)ExtensionGet("company")[0];
}
set { this.ExtensionSet("company", value); }
}
// Implement the overloaded search method FindByIdentity.
public static new UserPrincipalClassExtensions FindByIdentity(PrincipalContext context, string identityValue)
{
return (UserPrincipalClassExtensions)FindByIdentityWithType(context, typeof(UserPrincipalClassExtensions), identityValue);
}
// Implement the overloaded search method FindByIdentity.
public static new UserPrincipalClassExtensions FindByIdentity(PrincipalContext context, IdentityType identityType, string identityValue)
{
return (UserPrincipalClassExtensions)FindByIdentityWithType(context, typeof(UserPrincipalClassExtensions), identityType, identityValue);
}
}
This is a Call that Returns null:
SPUser user = SPContext.Current.Web.CurrentUser;
using (PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, getUserDomain(user)))
{
UserPrincipalClassExtensions UserInfos = UserPrincipalClassExtensions.FindByIdentity(principalContext,IdentityType.SamAccountName,user.Name);
}
This is a Call that Returns a UserPrincipal but without the company field value:
SPUser user = SPContext.Current.Web.CurrentUser;
using (PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, getUserDomain(user)))
{
UserPrincipal UserInfos = UserPrincipal.FindByIdentity(principalContext,IdentityType.SamAccountName,user.Name);
}
If you need any further info, let me know!
Thanks for in advance!
EDIT
After searching a little more and decompiling de DLL in IL Spy, I've noticed that my code had an issue:
[DirectoryObjectClass("Person")]
[DirectoryRdnPrefix("CN")]
DirectoryObjectClass had to be "Person"
Hope this is going to help some body else!
I'm using Service Builder in my portlet. Here is my addProduct method in PRProductLocalServiceBaseImpl:
public class PRProductLocalServiceImpl extends PRProductLocalServiceBaseImpl {
public PRProduct addProduct(long companyID, long groupID, String productName, String serialNumber, long userID) throws SystemException, PortalException{
PRProduct product = prProductPersistence.create(counterLocalService.increment(PRProduct.class.getName()));
resourceLocalService.addResources(companyID, groupID, userID, PRProduct.class.getName(), product.getPrimaryKey(), false, true, true);
product.setProductName(productName);
product.setSerialNumber(serialNumber);
product.setCompanyId(companyID);
product.setGroupId(groupID);
return prProductPersistence.update(product, false);
}
}
When I call this method from my portlet class and pass 1 as companyID it gives "No Role exists with the key {companyId=1, name=Owner}". and here is my portlet class:
public void addProduct(ActionRequest actionReaquest, ActionResponse actionResponse)
{
PortletSession session = actionReaquest.getPortletSession();
try
{
String productName = actionReaquest.getParameter("productName");
String userID = actionReaquest.getParameter("userID");
String companyID = actionReaquest.getParameter("companyID");
String groupID = actionReaquest.getParameter("groupID");
String serialNumber = actionReaquest.getParameter("serialNumber");
PRProduct product = PRProductLocalServiceUtil.addProduct(Long.parseLong(companyID), Long.parseLong(groupID), productName,
serialNumber, Long.parseLong(userID));
session.setAttribute("errorMessage", "Product added successfully");
actionResponse.setRenderParameter("jspPage", "/ProductAdded.jsp");
}
catch(Exception e)
{
session.setAttribute("errorMessage", e.getMessage());
actionResponse.setRenderParameter("jspPage", "/ProductAdded.jsp");
}
}
Can any body help? Any help is appreciated in advance.
Have checked to see if the Company ID is in fact 1?
The best way to get the current Liferay User ID, Group ID, and Company ID is by the ThemeDisplayobject. So instead of using your code:
String userID = actionReaquest.getParameter("userID");
String companyID = actionReaquest.getParameter("companyID");
String groupID = actionReaquest.getParameter("groupID");
You should use:
ThemeDisplay themeDisplay = (ThemeDisplay) actionReaquest.getAttribute(WebKeys.THEME_DISPLAY);
long realUserId = themeDisplay.getRealUserId();
long companyId = themeDisplay.getCompanyId();
long groupId = themeDisplay.getScopeGroupId();
This way you'll get the values from Liferay, rather than having to pass them in yourself. It also means you don't have to a Long.parseLong() to get the Long value of a String.
See if this helps! It's also better practice to do it this way for any future portlets. The ThemeDisplay objects holds a lot of useful information!
Also minor thing, it's spelt "request" not "reaquest" :)
Probably you need to add the content as an admin user or Owner user, below is the example to ass the content as the admin user set the adminUser permission before adding the content, try same for Owner:
User adminUser = UserLocalServiceUtil.getUserByEmailAddress(companyId,"test#liferay.com");
permissionChecker = PermissionCheckerFactoryUtil.create(adminUser);
PermissionThreadLocal.setPermissionChecker(permissionChecker);
or just fetch the owner the using code below:
Role role=com.liferay.portal.service.RoleLocalServiceUtil.getRole(long companyId,"Owner");
and update the add method add one more argument in add method i.e. serviceContext and all role(owner) in it,as we do while adding User in liferay.
I am trying to create a Site for the Organization when the Organization is created using hook and adding listener to Organization entity.
I created site using GroupLocalServiceUtil and set up siteName = String.valueOf(org.getOrganizationId()) + "LFR_ORGANIZATION" + org.getName() like in DB.
Also I have set className and classPK like it is done when you create site to organization in liferay, but nothing happens. Site creates successfully, but it isn't connected with the Organization.
UPD.
Liferay 6.1 GA1
public Group addSite(Organization org) throws PortalException, SystemException
{
ServiceContext serviceContext = new ServiceContext();
String siteName = String.valueOf(org.getOrganizationId()) + "LFR_ORGANIZATION" + org.getName();
Group newSite = GroupLocalServiceUtil.addGroup(
getDefaultUserId(),
"com.liferay.portal.model.Organization", // Class Name
org.getOrganizationId(), // Class PK
siteName , // Name
"", // Description
GroupConstants.TYPE_SITE_PRIVATE, // Type
org.getTreePath(), // Friendly URL
true, // Site
true, // Active
serviceContext);
OrganizationUtil.addGroup(org.getPrimaryKey(), newSite);
return newSite;
}
How can I create such an Organizational Site programmatically?
In Liferay 6.1, it appears that when you create an Organization there is already a backing group. That group may not be visible depending on the boolean site parameter you pass:
OrganiztionLocalServiceUtil.addOrganization(
long userId, long parentOrganizationId, String name, String type,
boolean recursable, long regionId, long countryId, int statusId,
String comments, boolean site, ServiceContext serviceContext)
To make that Site visible for pages, simply call:
OrganiztionLocalServiceUtil.updateOrganization(
long companyId, long organizationId, long parentOrganizationId,
String name, String type, boolean recursable, long regionId,
long countryId, int statusId, String comments, boolean site,
ServiceContext serviceContext)
with site parameter set to true.