I am using the Spring Security Core plugin and can successfully log users in and out of my application. However, when the user successfully signs up, I don't understand how to set that user's security context on the signup postback so that they can be redirected to a secure page without having to log in again. Any help is welcome. Thanks.
The other link you reference is 2 years old. Since then I've added a reauthenticate method to SpringSecurityService. See section "6.2 SpringSecurityService" in the docs:
http://grails-plugins.github.com/grails-spring-security-core/docs/
I eventually came upon this link, which does the trick: https://stackoverflow.com/a/7112838/469106
Here are the contents of that link:
If you don't have the password, you can load the user via
def user = User.findByUsername(username)
and setting the authority array in the 3-parameter constructor. Create the auths via
GrantedAuthority[] auths = user.authorities.collect { new GrantedAuthorityImpl(it.authority) }
Then you can omit the call to authenticate() and use:
SecurityContextHolder.context.authentication = new UsernamePasswordAuthenticationToken(username, 'unknown', auths)
Related
I've just started using loopback4 and I would like to protect the /explorer from being public. The user would initially see a page where username and password must be entered. If successful, the user is redirected to /explorer where he can see all API methods (and execute them). If user is not authenticated, accessing the path /explorer would give a response of "Unauthorized". Is there a way to easily implement this?
There is issue talking about a GLOBAL default strategy is enabled for all routes including explorer in https://github.com/strongloop/loopback-next/issues/5758
The way is to specify a global metadata through the options:
this.configure(AuthenticationBindings.COMPONENT).to({
defaultMetadata: {
strategy: 'JWTStrategy'
}
})
this.component(AuthenticationComponent);
registerAuthenticationStrategy(this, JWTAuthenticationStrategy)
But in terms of enabling a single endpoint added by route.get(), it's not supported yet, see code of how explorer is registered. #loopback/authentication retrieves auth strategy name from a controller class or its members, but if the route is not defined in the controller, it can only fall back to the default options, see implementation
Here is my issue.
Situation:
I am trying to add some custom logic during user login. I could find to ways to do so:
hard way (but with a lot of control); building my own authentication provider, following this guidelines of the cookbook or this complementing publication of vandenbrand
easy way (exactly what I need ): use simple_form. simple_form is a key which has the same options as form_login, but for which I can define an "authenticator".
cookbook tuto I used can be found here
Issue
I had an existing and operational app/security.yml configuration with 'form_login' key.
secured_area:
pattern: ^/foo/user/secured/
form_login:
check_path: /foo/user/secured/login_check
login_path: /foo/user/login
I followed steps of the tutorial described above. therefore, my security.yml gets modified to:
secured_area:
pattern: ^/foo/user/secured/
#form_login:
simple_form:
authenticator: foo_authenticator
check_path: /foo/user/secured/login_check
login_path: /foo/user/login
when I try to access a page /foo/user/secured/target of the secured area, the firewall does its job: it catches the query and asks for credentials (via intermediary page /foo/user/login).
However, once right credentials input (and obviously validated), I keep staying on the same page. It does not redirect to the page /foo/user/secured/target I was asking for in the first place. There is no refreshing to trying to go to that page via new request: I remain stuck at login stage.
EDIT 1: here are the steps I identify based on logs and debugging:
1) user tries to access /foo/user/secured/target, for which you need to be identified at least with ROLE_USER to access
2) firewall intercepts this request, as it matches listened routes (app/config/security.yml):
secured_area:
pattern: ^/foo/user/secured/
3) it redirects toward login route
4) user fills in with username and password, and submits post
5) when form is received, a token gets created by createToken method of custom authenticater. It returns an object of class UsernamePasswordToken created with parameter username, password in clear, authenticater key: UsernamePasswordToken($username, $password, $providerKey)
6) token gets passed onto authenticateToken method de of authenticater object. this method compares clear password hash contained in token andd accessed through $token->getCredentials()) with hashed password in database.
7) authentication worked: we get redirected toward /foo/user/secured/target . token and user get serialized in session (ISSUE STARTS HERE: indeed, user clear password is erased so that it doesn't leave tracks in session, and getCredentials() will return empty string now).
8) while loading page, le firewall is activated. It detects user logged in, seems to want to check its token. Therefore, it calls authenticateToken.
9) authenticateToken tries to compare sha1($token->getCredentials()) to hashed password in database. comme $token->getCredentials() is empty, comparison fails. authenticateToken raises an exception.
10) raised exception triggers firewall redirection toward login page. There we are: stuck in infinite loop landing systematically on login page.
STOP EDIT 1.
Solution
Does anyone know why this change of behaviour between 'form_login' and 'simple_form'? Most of all, would you know a good way to fix this ? I guess authenticate method or custom authenticater should be slightly changed, but I am not yet confident enough with security to solve this elegantly.
Many thanks in advance.
Kind regards,
Wisebes
You have to use some string (not the object) from sample. Or implement __toString() for User entity.
NOT
return new UsernamePasswordToken($user, ...
USE
return new UsernamePasswordToken($user->getEmail() or whatever, ...
if you want to access to the page you was requesting, you could use any of the options that Symfony offers to you:
Redirecting after Login:
always_use_default_target_path (type: Boolean, default: false)
default_target_path (type: string, default: /)
target_path_parameter (type: string, default: _target_path)
use_referer (type: Boolean, default: false)
You could see the section of the book 'SecurityBundle Configuration ("security")'
http://symfony.com/doc/current/reference/configuration/security.html
I hope that this be useful for you.
Kind regards.
well, as I was not able to make it work fine, I created my own custom authentication provider. I hope the issue reported above will be fixed asap. If anyone has got an answer, I still am interested!
For other people facing the same issue, I recommend creating a custom authentication provider. You may even inherit from existing authentication provider, and therefore limit modifications to be done. All in all, you are able to add your custom logic with a limited amount of trouble that way.
I have a web app (.NET 3.5) which is sending notifications by email to users. In order to do this, I search Active Directory to find each person's email.
At the moment, I am hardcoding my own username and password like so in order to search AD:
Dim entry As New DirectoryEntry("LDAP://companyad", "myUsername", "myPassword", AuthenticationTypes.Secure)
Dim srch As New DirectorySearcher(entry)
srch.Filter = [String].Format("(&(objectClass=person)(sAMAccountName={0}))", "someOtherUsername")
Dim result As SearchResult = srch.FindOne()
Now, obviously, this is not ideal and I don't want those credentials hardcoded. My web app is using Windows Authentication. It also uses impersonation (as the logged in user) to access files and SQL Server. Is there also a way for me to "impersonate" the logged in user in order to search AD?
EDIT 1
I thought I'd better explain why I chose this answer. The problem turned out to not be the multi-hop issue or kerberos as it seems I have set these up correctly.
I had recently changed my app to only allow access to a certain group through the web.config settings. I had previously been only allowing access to myself. I set up the group and added myself to it. I then removed the hardcoded credentials and attempted to run the app WITHOUT RESTARTING my computer.
According to my network admin, I would not be logged on under that new group until I restarted my computer which I think is what was causing my problem. So, Preet's answer is actually the most accurate as I just needed to pass the LDAP path to DirectoryEntry.
EDIT 2
I also needed to register a Service Principal Name.
I ran this:
setspn -A HTTP/[dns name of the site] [machine name]
on my development machine.
Thanks to everyone else for their answers.
Doesn't
Dim entry As New DirectoryEntry("LDAP://companyad")
work?
Why not create a new user for this purpose alone? A user with only searching rights.
I set <identity impersonate="true"/> in my web.config and added the following code to my my page load event handler. It worked fine. Are you sure you are not dealing with a multi hop situation? In that case your app pool account needs to be configured for kerberos authentication to support impersonation in a multihop scenario. More info on this is here: http://support.microsoft.com/kb/329986
Response.Write(User.Identity.Name);
DirectoryEntry entry = new DirectoryEntry("LDAP://[mydomain.net]");
DirectorySearcher srch = new DirectorySearcher(entry);
srch.Filter = string.Format("(&(objectClass=person)(sAMAccountName={0}))", "[user]");
SearchResult result = srch.FindOne();
Response.Write(result.Path);
If you wish to use the Windows logged in user account as the credentials against AD, you have to use the following:
public bool IsExistingUser() {
DirectoryEntry de = new DirectoryEntry(Environment.UserDomainName)
DirectorySearcher ds = new DirectorySearcher(de)
ds.Filter = string.Format("((objectClass=user)(SAMAccountName={0}))", Environment.UserName)
try
SearchResult sr = ds.FindOne();
if (sr != null && sr.DirectoryEntry.Name.Contains(Environment.UserName))
return true;
catch (DirectoryServicesCOMException ex)
catch (COMException ex)
throw new Exception("Can't find logged in user in AD", ex);
return false;
}
Assuming this code would compile and run, it will verify whether the existing logged in user is known by your Domain Controller.
Impersonation is discouraged as it lets litteral password strings travel in your network. So, try to avoid it as much as possible.
EDIT Here's a pretty useful link for AD: Howto: (Almost) Everything In Active Directory via C# I found this post awesome!
I'm using the Acegi plugin in my Grails app. After a user registers, he is redirected to an action that is protected. Therefore, he is shown the login form.
However, I would prefer if the user was automatically logged in as soon as he completes registration. It seems like I could achieve this by redirecting to the action that the login form uses
redirect(uri:"/j_acegi_security_check?j_username=${username}&j_password=${passed}")
But this would send a HTTP request to the client (and back to the server) which shows the user's password. Is there a way I can login automatically in a secure fashion?
Thanks,
Don
If you generate the controller classes for the spring security plugin (grails generate-registration) you'll see the following lines in RegisterController which does just what you want:
class RegisterController {
def daoAuthenticationProvider
...
def save = {
...
def auth = new AuthToken(person.username, params.passwd)
def authtoken = daoAuthenticationProvider.authenticate(auth)
SecurityContextHolder.context.authentication = authtoken
redirect uri: '/'
}
Be sure that params.passwd is the plain-text password (i.e. not hashed) and it works like a charm.
I haven't tried this with non-test code, but this is the method that I created to log a user in within my integration tests (after building/saving the appropriate users/roles in my test setup):
import org.codehaus.groovy.grails.plugins.springsecurity.GrailsDaoAuthenticationProvider
import org.springframework.security.providers.UsernamePasswordAuthenticationToken
import org.springframework.security.context.SecurityContextHolder
...
def logInAsUser(username, password) {
daoAuthenticationProvider.getUserCache().removeUserFromCache(username)
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password)
SecurityContextHolder.context.authentication = daoAuthenticationProvider.authenticate(token)
}
I construct and inject the authentication token in the security context. There might be a little more that you need to do to get your user logged in and past security, but this would be the start of whatever that is.
I'll actually need to do exactly what you're asking in a week or two for my current app, post back if you figure it out fully before I do :).
This is Burt Beckwith's answer (not mine)
(It was left as a comment by Burt, but I think it deserves to be more prominent)
If you don't have the password, you can load the user via
def user = User.findByUsername(username)
and setting the authority array in the 3-parameter constructor. Create the auths via
GrantedAuthority[] auths = user.authorities.collect { new GrantedAuthorityImpl(it.authority) }
Then you can omit the call to authenticate() and use:
SecurityContextHolder.context.authentication = new UsernamePasswordAuthenticationToken(username, 'unknown', auths)
I'm attempting to develop a portlet for liferay.
How can I get access to the username and password (and other data liferay has) of the user that's currently logged in?
I'd also like to be able to execute some code when users change their password.
You can get the User ID by calling getRemoteUser() in the PortletRequest object. This is defined by JSR-168 therefore it's cross-portal compatible.
Once you have the ID you can fetch the additional informations by calling getUserById() (a Liferay specific service). This is something not covered by Portlet API specification, so it locks you to the Liferay.
Liferay Specific stuff, here is a code sample to be written in your Portlet Class to retrieve the User:
ThemeDisplay themeDisplay = (ThemeDisplay)portletRequest.getAttribute(WebKeys.THEME_DISPLAY);
User user = themeDisplay.getRealUser(); // it gives you the actual Logged in User
//you can also use
// User user = themeDisplay.getUser(); // this would fetch the User you are impersonating
long userId = user.getUserId();
String userName = user.getEmailAddress();
Alternatively;
long userId = themeDisplay.getRealUserId(); // themeDisplay.getUserId();
User user = UserLocalServiceUtil.getUser(userId);
Impersonate User:
Liferay has a concept that admins (or persons with the correct set of permissions) can impersonate a particular user of the portal. Through this they can see how the portal looks to that user.
For executing the code when user change their passwords:
One approach would be to create a hook plugin and overriding the services by extending the UserLocalServiceWrapper class. Then checking for the password change and executing your code inside the your custom class.
Hope this helps.
Or you can just use javascript:
Liferay.ThemeDisplay.getUserId()
There are many nice to haves in the Liferay namespace, take a look at the not so well documented API:
https://www.liferay.com/community/wiki/-/wiki/Main/Liferay+JavaScript+API
https://www.liferay.com/web/pankaj.kathiriya/blog/-/blogs/usage-of-liferay-js-object
Also, take a look at the web services available under localhost:8080/api/jsonws which you can invoke with a javascript call:
Liferay.Service(
'/user/get-user-by-id',
{
userId: 10199
},
function(obj) {
console.log(obj);
}
);
One simple and easy way to get the user in Liferay is PortalUtil.getUser function.
User user = PortalUtil.getUser(portletRequest);