Web-Shiro getSuccessUrl of PassThruAuthenticationFilter - authentication

I've used Shiro's inbuilt login behavior for sometime. The user is redirected to the login page when they try to access a protected resource, then after successful login, they are redirected back to the resource they were trying to access; that is, the successUrl property of PassThruAuthenticationFilter.
Now that I'm using my own custom login, I can't seem to find a way of getting this successUrl right. Below is shiro.ini configuration:
shiro.ini
[main]
authc = org.apache.shiro.web.filter.authc.PassThruAuthenticationFilter
authc.loginUrl = /login.xhtml
authc.successUrl = /index.xhtml #index.xhtml is fallback url
Below is my login code
Factory<SecurityManager> factory = new IniSecurityManagerFactory(configFileDir + "shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject currentUser = SecurityUtils.getSubject();
session = currentUser.getSession();
PassThruAuthenticationFilter filter = new PassThruAuthenticationFilter();
String url = filter.getSuccessUrl();
if (!currentUser.isAuthenticated()) {
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
//
//
//
}
I know it does not make sense to use getSuccessUrl on a newly initiated PassThruAuthenticationFilter class, so I'm wondering, what's the correct way of getting the filter object?

I eventually found the solution; I added the following lines in my login code:
import org.apache.shiro.web.util.WebUtils;
//...
String successUrl = WebUtils.getSavedRequest(request);
It worked like a charm. I din't have to worry about PassThruAuthenticationFilter anymore.
Updated
I found it from this link
Use the following from within your Spring MVC controller:
import org.apache.shiro.web.util.WebUtils; ... String fallbackUrl =
"/path/to/go/to/incase/there/is/no/saved/request";
WebUtils.redirectToSavedRequest(request, response, fallbackUrl);
return null; //tell Spring MVC not to render a view, we're redirecting
explicitly

Related

How to connect TFS Online using PAT or OAUT?

Can't believe I'm stuck with a LOGIN :( hate when this happens.
Can somebody enlight me how to connect TF.EXE by using PAT password or in the best case an OAuth token?
I might add that I already have a Pat token and an OAuth token, not a problem while trying to get those, but every time I try this example:
TF.exe workspaces /collection:xxxx.visualstudio.com/xxxx /loginType:OAuth /login:.,MyPatTokenOrMyOauthToken /noprompt
I get the following response:
TF30063: You are not authorized to access xxxx.visualstudio.com\xxxx.
So, I Know command it's ok, because if I don't specify a login, a modal window prompts for credentials, and I tested already with that approach and works fine.
For the end, I might change everything to change tf.exe for the TFS api, but I'm unable to find same methods in the api (see reference: https://learn.microsoft.com/es-es/rest/api/vsts/?view=vsts )
If API has same methods than TF.exe, that will be useful, but so far I don't see same methods in the API.
Hope somebody has the solution for my problem.
Thanks in advance.
From my test, PAT token doesn't work in the following command, you have to get a OAuth token:
tf workspaces /collection:https://xxxx.visualstudio.com /loginType:OAuth /login:.,[OAuth token]
For the api that authenticate with Visual Studio Team Services (VSTS), you could refer to the examples in this link:
Here is an example getting a list of projects for your account:
REST API
using System.Net.Http;
using System.Net.Http.Headers;
...
//encode your personal access token
string credentials = Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(string.Format("{0}:{1}", "", personalAccessToken)));
ListofProjectsResponse.Projects viewModel = null;
//use the httpclient
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://{accountname}.visualstudio.com"); //url of our account
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", credentials);
//connect to the REST endpoint
HttpResponseMessage response = client.GetAsync("_apis/projects?stateFilter=All&api-version=1.0").Result;
//check to see if we have a succesfull respond
if (response.IsSuccessStatusCode)
{
//set the viewmodel from the content in the response
viewModel = response.Content.ReadAsAsync<ListofProjectsResponse.Projects>().Result;
//var value = response.Content.ReadAsStringAsync().Result;
}
}
.Net Client Libraries
using Microsoft.TeamFoundation.Core.WebApi;
using Microsoft.VisualStudio.Services.Common;
...
//create uri and VssBasicCredential variables
Uri uri = new Uri(url);
VssBasicCredential credentials = new VssBasicCredential("", personalAccessToken);
using (ProjectHttpClient projectHttpClient = new ProjectHttpClient(uri, credentials))
{
IEnumerable<TeamProjectReference> projects = projectHttpClient.GetProjects().Result;
}
Add a screenshot:
Update:
I've tested with a new account, and the result is as below. If I remove /loginType and /login parameters, a window will pop up to ask me logon.
The screenshot without /loginType and /login parameters:
The screenshot with /loginType and /login parameters:

How to trap a WS-Federation callback in an OWIN MVC5 app to automatically create an identity in local database?

I am currently working on a vb.net MVC5 application and using WS-Federation to authenticate all the users from an ADSF 3.0 server. Everything is working fine; when the users try to access a secured controller marked with the AUTHORIZE attribute, the users are redirected to the STS login page, they login and they come back. I am able to read the CLAIMS provided by the ADFS server.
My problem is that i need to create a local entry in my database when a new authenticated user comes in after login to store additional informations. I do not want the user to register manually, if he is authenticated, it means i can trust this user.
The Startup.Auth.vb looks like this :
Partial Public Class Startup
Private Shared realm As String = ConfigurationManager.AppSettings("ida:Wtrealm")
Private Shared adfsMetadata As String = ConfigurationManager.AppSettings("ida:ADFSMetadata")
Public Sub ConfigureAuth(app As IAppBuilder)
app.UseCookieAuthentication(New CookieAuthenticationOptions() With {
.AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
.LoginPath = New PathString("/Home/Login")
})
app.UseWsFederationAuthentication(New WsFederationAuthenticationOptions() With {
.AuthenticationType = "ExternalCookie",
.Wtrealm = realm,
.MetadataAddress = adfsMetadata,
.Wreply = "https://myapp.com/Home/SSOLogin"
})
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType)
End Sub
End Class
In my Home controller, i have created the SSOLogin Action to be able to do what i need to do to test the presence of this user and create it if it does not exists
Public Function SSOLogin() As ActionResult
' Some code here to check if user exists or not and create it
End Function
I put a breakpoint in my action but it is never hit because the middleware handles and detect the postback before it hits the action method and does a Redirect 302 to the originally requested secured page.
The question
Are there any way to trap the callback in the WSFederation middleware or maybe add an event to the global.asax to do my user automatic creation only after authentication, not on all requests ?
Thanks for your time !
After the WsFederationAuthenticationHandler has validated the inbound token, you can register to receive a notification. WsFederationAuthenticationOptions.Notifications is where you can do that.
In startup.cs you want to set notification for: WsFederationAuthenticationNotifications.SecurityTokenValidated.
Patrice, here is some code that will give you an idea of how to hook the notification. this code is usually placed in startup.auth.cs.
var wsFederationOptions = new WsFederationAuthenticationOptions
{
Notifications = new WsFederationAuthenticationNotifications
{
SecurityTokenValidated = (notification) =>
{
var identity = notification.AuthenticationTicket.Identity;
var defaultName = identity.FindFirst ("<claim-that-identifies-user>");
// do work here
return Task.FromResult<object>(null);
}
},
MetadataAddress = wsFedMetadataAddress,
Wreply = host,
Wtrealm = clientId
};
app.UseWsFederationAuthentication(wsFederationOptions);

Apache shiro with jersey

Im building a web service app using jersey.
For authorization/authentication im using apache shiro.
I found some tutorials showing how to use apache shiro in a web app. They show the login method using a .jsp page that have a username and password field and than this .jsp page is configured in shiro.ini like this:
[main]
shiro.loginUrl = /login.jsp
[urls]
/login.jsp = authc
/logout = logout
I Wanna know how to make this authentication without a any .jsp page, because my project have only web services. So i think that i need a login service, than i created one:
#POST
#Path("/login")
public Response login(#FormParam("username") final String username, #FormParam("password") final String password, #FormParam("remember") final boolean remember) {
final Subject currentUser = SecurityUtils.getSubject();
if (!currentUser.isAuthenticated()) {
final UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
token.setRememberMe(remember);
currentUser.login(token);
} catch (final AuthenticationException e) {
return Response.status(Status.BAD_REQUEST).entity("Usuário ou senha inválido").build();
}
}
And this is my shiro.ini conf:
[urls]
/security/login = anon
/security/isAuthenticated = anon
/** = authcBasic
Once that the user wont be authenticated to log in i include /security/login = anon.
Is this the correct way to authenticated a user with apache shiro in a webservice environment?
You don't need a login service. Actually, authenticating and using the service should be two different things. What you need to do is:
Know what pages do you want to authenticate
Configure Shiro to authenticate those pages through your authentication method.
your shiro.ini will look to something like this:
[main]
myRealm = com.my.package.MyRealm
myAuthc = com.my.package.MyAuthenticationFilter
[urls]
/public/** = anon
/** = myAuthc
You will need to implement both the realm and the filter. You can implement the filter using AuthenticatingFilter or even one of the sub-classes, like BasicHttpAuthenticationFilter. The realm can be implemented using the AuthenticatingRealm class.
More on realms here and more on Shiro on web here. Notice that to make your filter available what you will need to do is basically set up the filter on the web.xml
After coding the realm and the filter, your code should work as expected. As defined on the shiro.ini any path that starts with public/ will not be authenticated and all the other paths will be authenticated through your com.my.package.MyAuthenticationFilter. Please notice that order matters: if you define the /** = myAuthc line first it will authenticate everything, including paths that start with /public/.

How to request for the crumb issuer for Jenkins

I want to use the Jenkins Remote API, and I am looking for safe solution. I came across Prevent Cross Site Request Forgery exploits and I want to use it, but I read somewhere that you have to make a crumb request.
How do I get a crumb request in order to get the API working?
I found this https://github.com/entagen/jenkins-build-per-branch/pull/20, but still I don't know how to fix it.
My Jenkins version is 1.50.x.
Authenticated remote API request responds with 403 when using POST request
I haven't found this in the documentation either. This code is tested against an older Jenkins (1.466), but should still work.
To issue the crumb use the crumbIssuer
// left out: you need to authenticate with user & password -> sample below
HttpGet httpGet = new HttpGet(jenkinsUrl + "crumbIssuer/api/json");
String crumbResponse = toString(httpclient, httpGet);
CrumbJson crumbJson = new Gson().fromJson(crumbResponse, CrumbJson.class);
This will get you a response like this
{"crumb":"fb171d526b9cc9e25afe80b356e12cb7","crumbRequestField":".crumb"}
This contains two pieces of information you need
the field name with which you need to pass the crumb
the crumb itself
If you now want to fetch something from Jenkins, add the crumb as header. In the sample below I fetch the latest build results.
HttpPost httpost = new HttpPost(jenkinsUrl + "rssLatest");
httpost.addHeader(crumbJson.crumbRequestField, crumbJson.crumb);
Here is the sample code as a whole. I am using gson 2.2.4 to parse the response and Apache's httpclient 4.2.3 for the rest.
import org.apache.http.auth.*;
import org.apache.http.client.*;
import org.apache.http.client.methods.*;
import org.apache.http.impl.client.*;
import com.google.gson.Gson;
public class JenkinsMonitor {
public static void main(String[] args) throws Exception {
String protocol = "http";
String host = "your-jenkins-host.com";
int port = 8080;
String usernName = "username";
String password = "passwort";
DefaultHttpClient httpclient = new DefaultHttpClient();
httpclient.getCredentialsProvider().setCredentials(
new AuthScope(host, port),
new UsernamePasswordCredentials(usernName, password));
String jenkinsUrl = protocol + "://" + host + ":" + port + "/jenkins/";
try {
// get the crumb from Jenkins
// do this only once per HTTP session
// keep the crumb for every coming request
System.out.println("... issue crumb");
HttpGet httpGet = new HttpGet(jenkinsUrl + "crumbIssuer/api/json");
String crumbResponse= toString(httpclient, httpGet);
CrumbJson crumbJson = new Gson()
.fromJson(crumbResponse, CrumbJson.class);
// add the issued crumb to each request header
// the header field name is also contained in the json response
System.out.println("... issue rss of latest builds");
HttpPost httpost = new HttpPost(jenkinsUrl + "rssLatest");
httpost.addHeader(crumbJson.crumbRequestField, crumbJson.crumb);
toString(httpclient, httpost);
} finally {
httpclient.getConnectionManager().shutdown();
}
}
// helper construct to deserialize crumb json into
public static class CrumbJson {
public String crumb;
public String crumbRequestField;
}
private static String toString(DefaultHttpClient client,
HttpRequestBase request) throws Exception {
ResponseHandler<String> responseHandler = new BasicResponseHandler();
String responseBody = client.execute(request, responseHandler);
System.out.println(responseBody + "\n");
return responseBody;
}
}
Or you can use Python and requests instead
req = requests.get('http://JENKINS_URL/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)', auth=(username, password))
print(req.text)
will give you the name and the crumb:
Jenkins-Crumb:e2e41f670dc128f378b2a010b4fcb493
This Python function gets the crumb, and additionally uses the crumb to post to a Jenkins endpoint. This is tested with Jenkins 2.46.3 with CSRF protection turned on:
import urllib.parse
import requests
def build_jenkins_job(url, username, password):
"""Post to the specified Jenkins URL.
`username` is a valid user, and `password` is the user's password or
(preferably) hex API token.
"""
# Build the Jenkins crumb issuer URL
parsed_url = urllib.parse.urlparse(url)
crumb_issuer_url = urllib.parse.urlunparse((parsed_url.scheme,
parsed_url.netloc,
'crumbIssuer/api/json',
'', '', ''))
# Use the same session for all requests
session = requests.session()
# GET the Jenkins crumb
auth = requests.auth.HTTPBasicAuth(username, password)
r = session.get(crumb_issuer_url, auth=auth)
json = r.json()
crumb = {json['crumbRequestField']: json['crumb']}
# POST to the specified URL
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
headers.update(crumb)
r = session.post(url, headers=headers, auth=auth)
username = 'jenkins'
password = '3905697dd052ad99661d9e9f01d4c045'
url = 'http://jenkins.example.com/job/sample/build'
build_jenkins_job(url, username, password)
Meanwhile you can generate an API token in order to prevent having to include your password in the source code provided by the solutions above:
https://wiki.jenkins.io/display/JENKINS/Authenticating+scripted+clients
Refer - https://support.cloudbees.com/hc/en-us/articles/219257077-CSRF-Protection-Explained
If you authenticate with a username and a user API token then a crumb is not needed from Jenkins 2.96 weekly/2.107 LTS. For more information please refer to CSRF crumb no longer required when authenticating using API token or JENKINS-22474.
User cheffe's answer helped 90%. Thanks for giving us the right direction.
The missing 10% revolved around HTTP username and password authentication.
Since the Codenameone Java API I was using did not have the Authentication Class,
new UsernamePasswordCredentials(usernName, password));
I used:
String apiKey = "yourJenkinsUsername:yourJenkinsPassword";
httpConnection.addRequestHeader("Authorization", "Basic " + Base64.encode(apiKey.getBytes()));
User cheffe's Java snippet worked great for me on Jenkins v2.89.3 (Eclipse.org) and another Jenkins instance I use, at v2.60.3 (once enabled1).
I've added this to a Maven mojo2 I use for pushing locally-edited config.xml changes back to the server.
1 CSRF Protection
2 Hudson job sync plugin
In any of these answers I didn't find an option to use Jenkins API token.
I really tried all of these options but if you're enabling CSRF protection, you should access Jenkins APIs with Jenkins API token instead of normal password.
This token can be generated by each individual user in the user config page.
The token can be used as follows-
JenkinsApi::Client.new(server_url: jenkins_url, username: jenkins_user, password: jenkins_token)
P.S. - This initialization is for a Ruby Jenkins API client

Forms Authentication with NancyFx

I am using NancyFx to make a simple website were users can login and out using an ajax control.
I have read the documentation on Forms Authentication with Nancy and I think I have completed all the required steps.
Install the Nancy.Authentication.Forms package
Implement an IUserMapper
Implement routes to handle login and logout
Configure and enable Forms Authentication
I am having an issue where after calling login, requests do not have a current user set.
I can see a cookie set after the login is executed. However the user mapper is not getting called. I have tried requesting routes with and with out the this.RequiresAuthentication(); and still no user.
Here is my Bootstrapper Implementation for step 4
public class Bootstrapper : DefaultNancyBootstrapper
{
protected override void ConfigureApplicationContainer(TinyIoCContainer container)
{
container.Register<ISessionFactory>((c, p) => SessionFactory.Factory);
}
protected override void ConfigureConventions(NancyConventions conventions)
{
base.ConfigureConventions(conventions);
conventions.StaticContentsConventions.Add(StaticContentConventionBuilder.AddDirectory("assets", #"content/assets"));
conventions.StaticContentsConventions.Add(StaticContentConventionBuilder.AddDirectory("application", #"content/application"));
}
protected override void ConfigureRequestContainer(TinyIoCContainer container, NancyContext context)
{
base.ConfigureRequestContainer(container, context);
container.Register<IUserMapper, UserMapper>();
}
protected override void RequestStartup(TinyIoCContainer container, IPipelines pipelines, NancyContext context)
{
base.RequestStartup(container, pipelines, context);
var formsAuthConfiguration =
new FormsAuthenticationConfiguration()
{
RedirectUrl = "~/",
UserMapper = container.Resolve<IUserMapper>()
};
FormsAuthentication.Enable(pipelines, formsAuthConfiguration);
}
}
Here is my login & logout logic for step 3.
Post["/login"] = x =>
{
//Verify here, hardcode for testing
string email = "test#example.com";
User user = ExecuteCommand(new FetchUser(email));
this.LoginWithoutRedirect(user.Session);
return new { email = user.Email, authorized = true, status = "okay" };
};
Post["/logout"] = x =>
{
return this.Logout("~/");
};
My IUsermapper simply looks up a user from a database with the given id.
I can see it gets constructed when the RequestStartup resolves the IUserMapper but then there are never any calls to the get GetUserFromIdentifier function.
Any help would be great.
The GetUserFromIdentifier method is not being called because you are using the LoginWithoutRedirect extension. It is not the login that calls GetUserFromIdentifier but rather any subsequent redirect.
A more usual way of doing things would be:
string email = "test#example.com";
User user = ExecuteCommand(new FetchUser(email));
this.LoginAndRedirect(user.Session);
It is not expected that the login route would be accessed directly. Instead the user would normally request a protected resource, be authenticated and then redirected to the requested resource.
A couple of other points:
When I tried your code I got an error returning an anonymous type. Instead I needed to return the type as json, like this:
this.LoginWithoutRedirect(user.Session);
return Response.AsJson(new
{
email = user.Email,
authorized = true,
status = "okay"
});
This works fine, it logs in and returns your anonymous type as a json object and since there is no redirect then, as expected, it does not call GetUserFromIdentifier.
Finally, your /logout route should be protected using this.RequiresAuthentication(). It makes sense because only authenticated users need to logout. It will also protect you when GetUserFromIdentifier returns null - perhaps because a cached user has been timed out. RequiresAuthentication detects this null user and redirects back to Get["/login"].