Hy guys!
I haven’t been using Cake from gen 2 so i am a bit stuck with all the new stuff. I added the Authentication Component to my project and it works fine (login/out/pass hash etc) but i can’t seem to find how to make only the “Admin” prefix to require Authentication.
In the old times was something “simple” like:
if($this->params[‘prefix’] == ‘admin’){
$this->Auth->deny(’*’);
}
While you could do something similar with the authentication plugin's authentication component, authentication is now performed at middleware level, that is before a controller is being invoked, hence it is probably best to avoid trying to authenticate a user in the first place when it's not actually needed.
Scope the middleware
With the authentication plugin there's a few way to handle this, the easiest way would probably be to apply the authentication middleware on routing level, so that it is scoped to your prefixed routes.
You'd just remove adding the middleware in your Application::middleware() method (src/Application.php), and instead add it in either your config/routes.php file, or your Application::routes() method, depending on where you're connecting your prefix route:
$routes->prefix('Admin', function (RouteBuilder $routes) {
$routes->registerMiddleware(
'auth',
new \Authentication\Middleware\AuthenticationMiddleware($this)
);
$routes->applyMiddleware('auth');
// ...
});
This way only the routes connected in that prefix are going have authentication applied.
Conditionally require an authenticated user
As a second step you'd still need to handle checking for the possibly authenticated user, so that your endpoints are actually protected. By default the authentication component will do automatically, so one way would be to load the authentication component only for the Admin prefix, for example in your AppController::initialize() method:
if ($this->request->getParam('prefix') === 'Admin') {
$this->loadComponent('Authentication.Authentication');
}
Note the capital A in Admin! In CakePHP 4.x prefixes on are now camel cased, while the generated URLs are lower cased and dash separated!
See also
Cookbook > Routing > Connecting Scoped Middleware
Cookbook > Routing > Prefix Routing
Cookbook > Request & Response Objects > Request > Request Parameters
I don't know if this is a conventional method, but it works.
In the callback method beforeFilter, in my global Appcontroller.php, I had this code:
public function beforeFilter(\Cake\Event\EventInterface $event)
{
if($this->request->getParam('prefix') == 'Api') {
$this->Authentication->allowUnauthenticated([$this->request->getParam('action')]);
}
}
}
It allows access to all methods of my prefix API.
Related
I am fairly new to webapps programming, so I thought of asking here.
I am implementing the SAML2 protocol in an open source app (namely OFBiz) but I am encountering a problem related to session loss after the protocol made its course.
I am following these steps to implement the protocol. Suppose ofbizwebsite.com is the URL of the site.
Installed a custom plugin named SAMLIntegration which exposes the ACS page and the logic for login. To my understanding, a plugin (gradle) is like an indipendent java project, which translates to a new set of resources for the application (the plugin enables, for example, to visit ofbizwebsite.com/SAMLIntegration and setup some resources).
Exposed the ACS page to ofbizwebsite.com/SAMLIntegration/control/acs, as well as metadata ofbizwebsite.com/SAMLIntegration/control/metadata.jsp
Created the logic for login. Basically, an entity called UserLogin is saved in the session and recovered by a "checker" to understand if an user is logged in. Suppose that this checker is a HTTP WebEvent handler which can be called by any resource requiring authentication.
Now, the problem. If redirect the user to a resource on SAMLIntegration (for example ofbizwebsite.com/SAMLIntegration/control/aview or any ofbizwebsite.com/SAMLIntegration/control/* by calling response.sendRedirect("aview")) check works and login is preserved. Visiting any resource (for example ofbizwebsite.com/aplugin/control/anotherview) by navigating the application does not preserve the session.
OFBiz use internally a mechanism for preserving the userLogin between webapps, by creating an HashMap between and UUID and a UserLogin object. The UUID is passed between two different resources, appending this key to each path (so ofbizwebsite.com/aplugin/control/anotherview?externalKey=THEEFFECTIVEUUID)
To my understanding, changing from ofbizwebsite.com/SAMLIntegration/control/* to ofbizwebsite.com/aplugin/control/* determine a session loss. So, my idea was to replace the UUID mechanism with SAML2. However, I do not know how to solve this problem.
In particular, I would like to execute a SAML request each time the checker function is executed. If I can't find the user in the session, a SAML request is fired. However, my problem is HOW to manage the response. Normally, I would redirect it to the acs ofbizwebsite.com/SAMLIntegration/control/acs. Doing so, however, does not allow me to handle the response in the checker function, as the control is passed to another servlet by an external request (the SAML response fired by the IdP). Should I provide a different acs for each different path? (so one for SAMLIntegration and one for aplugin?) And, even if this was the case, how can I return the control to the checker function which has invoked the SAML request?
Here you go for installing the Shibboleth HTTPD module: https://pad.nereide.fr/SAMLWithShibboleth
You also need this method somewhere in OFBiz (I recommend LoginWorker.java, but you can put it where you want). It allows to use the externalAuthId of the userLogin for authentication, with the uid returned by the sso:
public static String checkShibbolethRequestRemoteUserLogin(HttpServletRequest request, HttpServletResponse response) {
LocalDispatcher dispatcher = (LocalDispatcher) request.getAttribute("dispatcher");
Delegator delegator = dispatcher.getDelegator();
// make sure the user isn't already logged in
if (!LoginWorker.isUserLoggedIn(request)) {
String remoteUserId = (String) request.getAttribute("uid"); // This is the one which works, uid at Idp, remoteUserId here
if (UtilValidate.isNotEmpty(remoteUserId)) {
//we resolve if the user exist with externalAuthId
String userLoginId = null;
GenericValue userLogin;
try {
List<GenericValue> userLogins = delegator.findList("UserLogin",
EntityCondition.makeConditionMap("externalAuthId", remoteUserId, "enabled", "Y"),
null, null, null, true);
userLogin = userLogins.size() == 1 ? userLogins.get(0) : null;
} catch (GenericEntityException e) {
Debug.logError(e, module);
return "error";
}
if (userLogin != null) {
userLoginId = userLogin.getString("userLoginId");
}
//now try to log the user found
return LoginWorker.loginUserWithUserLoginId(request, response, userLoginId);
}
}
return "success";
}
You also need to have this method as an OFBiz preprocessor in the webapp controllers. I suggest to have a look at common-controller.xml.
Finally you need the configs that redirect to the SSO page if no session. That should do the job, at least it works for them...
Finally I recommend https://www.varonis.com/blog/what-is-saml in case of need.
I have a MEAN application, and I'm handling authentication using passport. What I want to do is exactly what happens to the user on the passport, which can be accessed from the request like req.user. I have not found any solutions to reach that result. Can you give me any advice?
You can add properties to the request or response objects by creating a middleware and using it in your app. E.g.
// Defining middleware
function myMiddleware(req, res, next) {
req.myField = 12;
next();
}
// Using it in an app for all routes (you can replace * with any route you want)
app.use('*', myMiddleware)
Now all your request objects in your handlers will have myField property.
To add extra properties to the request and response object you need to extend the response and request interface.
index.d.ts files are used to provide typescript type information about a module that’s written in JavaScript.For express, the index.d.ts is present inside the #types/express folder inside the node_modules folder.
Use this link-
https://dev.to/kwabenberko/extend-express-s-request-object-with-typescript-declaration-merging-1nn5
I am having a very similar issue to this post here: How to use custom route middleware with Sails.js? (ExpressJS)
in that I want all non ajax requests (or all routes with the prefix /api) to load the same view, regardless of route. I have implemented the given answer in that question, but came across the issue that the policy is not called for any unspecified routes.
If I was to catch all routes so that the policy was called, all my blueprints would be overwritten.
Ideally, I would catch all routes last, after the blueprints, since every non API route should be sent to the front end.
I am using angularjs for the front end and want angular to deal with all non API routing.
I would rather not use a .htaccess file as I need to put session information into the page on it's initial load.
Thanks
It seems like your use case is very similar to a HTTP 404 Error situation - you want all requests which don't satisfy blueprint (and possibly route.js) routes to be handled in the same manner.
From api/responses/notFound.js:
* NOTE:
* If a request doesn't match any explicit routes (i.e. `config/routes.js`)
* or route blueprints (i.e. "shadow routes", Sails will call `res.notFound()`
* automatically.
*/
You can have special handling code here which calls the appropriate view if the request path contains /api:
if (req.path.match('^/api')) {
return res.view('your-view-here');
}
Working on my first EmberJS app. The entire app requires that a user be logged in. I'm trying to wrap my head around the best way to enforce that a user is logged in now (when the page is initially loaded) and in the future (when user is logged out and there is no refresh).
I have the user authentication hooks handled - right now I have an ember-data model and associated store that connects that handles authorizing a user and creating a user "session" (using sessionStorage).
What I don't know how to do is enforce that a user is authenticated when transitioning across routes, including the initial transition in the root route. Where do I put this logic? If I have an authentication statemanager, how do I hook that in to the routes? Should I have an auth route that is outside of the root routes?
Note: let me know if this question is poorly worded or I need to explain anything better, I will be glad to do so.
Edit:
I ended up doing something that I consider a little more ember-esque, albeit possibly a messy implementation. I have an auth statemanager that stores the current user's authentication key, as well as the current state.
Whenever something needs authentication, it simply asks the authmanager for it and passes a callback function to run with the authentication key. If the user isn't logged in, it pulls up a login form, holding off the callback function until the user logs in.
Here's some select portions of the code I'm using. Needs cleaning up, and I left out some stuff. http://gist.github.com/3741751
If you need to perform a check before initial state transition, there is a special function on the Ember.Application class called deferReadiness(). The comment from the source code:
By default, the router will begin trying to translate the current URL into
application state once the browser emits the DOMContentReady event. If you
need to defer routing, you can call the application's deferReadiness() method.
Once routing can begin, call the advanceReadiness() method.
Note that at the time of writing this function is available only in ember-latest
In terms of rechecking authentication between route transitions, you can add hooks to the enter and exit methods of Ember.Route:
var redirectToLogin = function(router){
// Do your login check here.
if (!App.loggedIn) {
Ember.run.next(this, function(){
if (router.currentState.name != "login") {
router.transitionTo('root.login');
}
})
}
};
// Define the routes.
App.Router = Ember.Router.extend({
root: Ember.Route.extend({
enter: redirectToLogin,
login: Ember.Route.Extend({
route: 'login',
exit: redirectToLogin,
connectOutlets: function(router){
router.get('applicationController').connectOutlet('login');
}
}),
....
})
});
The problem with such a solution is that Ember will actually transition to the new Route (and thus load all data, etc) before then transitioning back to your login route. So that potentially exposes bits of your app you don't want them seeing any longer. However, the reality is that all of that data is still loaded in memory and accessible via the JavaScript console, so I think this is a decent solution.
Also remember that since Ember.Route.extend returns a new object, you can create your own wrapper and then reuse it throughout your app:
App.AuthenticatedRoute = Ember.Route.extend({
enter: redirectToLogin
});
App.Router = Ember.Router.extend({
root: Ember.Route.extend({
index: App.AuthenticatedRoute.extend({
...
})
})
});
If you use the above solution then you can cherry pick exactly which routes you authenticate. You can also drop the "check if they're transitioning to the login screen" check in redirectToLogin.
I put together a super simple package to manage session and auth called Ember.Session https://github.com/andrewreedy/ember-session
Please also take a look at :
http://www.embercasts.com/
There are two screencasts there about authentication.
Thanks.
Assume a blackboard type application. There are 2 Projects - ProjectA and ProjectB. User 'nupul' (me) is part of both projects. For A I'm an admin and for B I'm just a 'member' (no admin rights)
When accessing the resource at /MySite/ProjectA/Items I want to check if the user is an admin or not.
I know it can be simply done by picking out the {projectName} parameter from the request and using the identifier (of the user making the request) and forwarding that to check against a DB etc.,
My question is 'how' can I add the roles using an Enroler 'during' authentication itself. Since I don't have access to the {projectName} parameter at that stage. I don't know if you have to use Groups/Realms etc., to make this work, but honestly it's just taking me tooooooooooooooooooo long to even understand how to effectively use this? (i.e., before the request is forwarded to the resource)
I mean I know I can create these groups/realms but how do I access the correct 'role' from the resource???? Restlet seriously needs to have more realistic examples and a much better documentation showing the use of it's classes!! It's driving me insane!! Authentication shouldn't be THIS DIFFICULT! :)
The way to do what you want is to split your routers basing on project name within your application (method createInboundRoot). In this case, the projectname will be evaluated before calling the authenticator. See below some examples of implementing such approach:
public Restlet createInboundRoot() {
Router rootRouter = new Router(getContext());
rootRouter.setDefaultMatchingMode(Template.MODE_STARTS_WITH);
rootRouter.attach("/{projectname}/", createApplicationForProject());
return rootRouter;
}
private Restlet createApplicationForProject() {
Router router = new Router(getContext());
ChallengeAuthenticator guard
= new ChallengeAuthenticator(getContext(),
ChallengeScheme.HTTP_BASIC, "realm");
guard.setVerifier(verifier);
guard.setEnroler(enroler);
guard.setNext(router);
router.attach("items", ItemsServerResource.class);
return guard;
}
Using such approach, you'll have access to the value of the projectname variable within the verifier and be able to use it in the authentication processing.
Hope it helps you,
Thierry