Deeplinking: React-Router-Dom Redirect to "Not Found Page" is not working when brought into a component through React Context - redirect

I am trying to create Deeplinking in a project, where there is a hierarchical relationship between Redux information.
When a link is pasted into a new browser's URL, it should be able to navigate to that page from the URL. This part is works.
But, if the hierarchical information found through Redux (starting with the jobID > projectID > engagementID > clientID ) doesn't match what is found in Redux then it should redirect to a "Not Found" page.
Here's grabbing the Redux information I need:
With that Redux information, I created this function for the Deeplinking (handleDeepLinkingURL):
If any of the Redux information I'm grabbing returns "undefined" (like if the wrong jobId Guid was entered into the URL), then I want it to redirect to the "Not Found" page.
The handleDeepLinkingURL function is called in the Dashboard component.
It should work like, if I am getting the jobId from the URL (using useParams from React-router-dom) and a Job doesn't already exist in Redux (it does not because we are using deeplinking - I am testing functionality in anonymous browsers and using a fake Guid that would not be found in Redux), then it should call my DeepLinking function, and should be Returning me the "Not Found Page."
Instead, it is loading the dashboard page without the proper data being added to Redux or displayed on the page. (note it's missing from the navigation breadcrumbs)
Here are the Routes in App.tsx
It's loading up the dashboard based on this dynamic route instead of redirecting to the "Not Found" page.
Why won't the redirect functionality found in Context and called in the Dashboard component Redirect like it's supposed to? And instead is still loading the Dashboard instead of Redirecting?
I have console.logged the conditional function calls, and indeed, the Redux information is returning as undefined (and there is no SelectedJob because we are doing Deeplinking), and so it should be calling the first part of the handleDeepLinkingURL function and redirecting to a "Not Found" page.
Instead it is incorrectly loading the dashboard page based on the dynamic routing in App.tsx.

You can't return JSX from functions like this and expect them to be rendered to the DOM to effect any actual navigation action if they are called as callbacks.
Based on the usage of the Redirect component I'll assume you are using react-router-dom#5 and have access to the useHistory hook. The component should use the useHistory hook to access the history object and in the handleDeepLinkingURL callback issue an imperative redirect.
Example:
const history = useHistory();
...
const handleDeepLinkingURL = (jobId: string) = {
... logic ...
if (/* condition to redirect */) {
history.replace("/page-not-found"); // <-- imperative redirect
} else {
... batching logic ...
}
};
If handleDeepLinkingURL is called as part of the render return and is intended that it does actually return JSX to be rendered, then it needs to actually return valid JSX.
Example:
const handleDeepLinkingURL = (jobId: string) = {
... logic ...
if (/* condition to redirect */) {
return <Redirect to="/page-not-found" />; // <-- declarative redirect
} else {
... batching logic ...
return null; // <-- still return valid JSX
}
};
...
if (!isJobSelected && jobId) {
return handleDeepLinkingURL(jobId); // <-- return result
} else if (!isJobSelected && !jobId) {
return <Redirect to="/home" />;
}

Related

Modify routing in Sails to use something else than id?

I have a web app which talks to my backend node.js+sails app through a socket.
Default routes for sockets use id. As example
io.socket.get('/chatroom/5')
My app doesn't authenticate users and as result I want id's to be random, so nobody can guess it. However, id's are generated by mongoDB and aren't that random.
As result, I want to use some other field (a.e. "randomId") and update routing for this model to use this field instead of id.
What's the best way to do it?
P.S. It looks like I have to use policies, but still struggling to figure out what should I do exactly.
You aren't forced to use the default blueprint routes in your app; you can always override them with custom controller methods or turn them off entirely.
The GET /chatroom/:id method automatically routes to the find action of your ChatroomController.js file. If you don't have a custom action, the blueprint action is used. So in your case, you could define something like the following in ChatroomController.js:
find: function (req, res) {
// Get the id parameter from the route
var id = req.param('id');
// Use it to look up a different field
Chatroom.find({randomId: id}).exec(function(err, chatrooms) {
if (err) {return res.serverError(err);}
// Subscribe to these rooms (optional)
Chatroom.subscribe(req, chatrooms);
// Return the room records
return res.json(chatrooms);
});
}
If you don't like the name find or the param id, you can set your own route in config/routes.js:
"GET /chatroom/:randomid": "ChatroomController.myFindActionName"
Also, re:
Default routes for sockets use id.
those routes aren't just for sockets--they respond to regular HTTP requests as well!
I created a policy. This policy converts randomId (which is passed as :id) to real id and saves it in req.options.id (which Sails will pick up).
module.exports = function(req, res, next) {
var Model = req._sails.models[req.options.model];
var randomId = req.params.all()['id'];
Model.findOne().where({ randomId: randomId }).exec(function(err, record) {
req.options.id = record.id;
return next();
});
};
And I apply this policy to findOne and update actions of my controller:
ChatRoomController: {
findOne : 'useRandomId',
update : 'useRandomId'
}

How to assert the full response, not just the"view" part using Zend_Test_PHPUnit?

I want to test that my /login page is working well and rejecting invalid credentials i.e. not redirecting to the user's dashboard and showing an aller message identified here with the .alert HTML class. So I've created a test like this:
class AuthControllerTest extends Zend_Test_PHPUnit_ControllerTestCase {
...
public function testUserFailingLogin() {
$this->request->setPost(array(
'email' => 'wrong#email.com',
'password' => 'wrongpassword',
));
$this->request->setMethod('POST');
$this->dispatch('/login');
$this->assertQuery('.alert');
}
}
My problem is that the assertQuery() method is running against the render of login.phtml view file and is not including my Zend_Layout set up (that's where the .alert's are shown) and thereof, the assertQuery() assertion fails always.
Is there any way to get assert*Query*() assertions evaluating the full response ("layout" + "view"), instead of just the "view" part?
You(I) should use the undocumented outputBody() method.

Symfony 1.4: Check if form has errors inside form class

Is there a simple way in Symfony 1.4 to know whether a submitted form had any errors inside the form class? I'm familiar with the $form['some_field']->hasErrors() for templates but in this case I'd like to run a post-validator only if the form didn't have any errors with the standard validators. I'm basically after something like:
public function configure() {
// widgets
// standard validators
if (!this->hasErrors()) {
// run post-validator
}
}
The API documentation is as cryptic as usual. Thanks in advance.
Since the validation is perfom on the bind call, I don't see other place to post validate on error than in the bind function. So, in your form class:
public function bind(array $taintedValues = null, array $taintedFiles = null)
{
parent::bind($taintedValues, $taintedFiles);
if ($this->hasErrors())
{
// do post validate
// you can access values from your form using $taintedValues
}
}
But you will have to manually call the validator instead of just define a new one (since the bind process has already been done).

How do you password protect a page with Wicket?

I want to password protect a webpage in Wicket so the user may only access it if he/she has logged in.
I'd also like the page to show the login page, and then after logging in the original page the user was trying to get to.
How is this done with wicket? I've already created a login page and extended the session class.
The framework-supplied way is to provide an IAuthorizationStrategy instance for your application, e.g., by adding to your Application init() method:
init() {
...
getSecuritySettings().setAuthorizationStrategy(...)
}
A working example of Wickets authorization functionality is on Wicket Stuff here, which demonstrates some reasonably complex stuff. For really simple cases, have a look at the SimplePageAuthorizationStrategy. At a very basic level, this could be used like so (taken from the linked Javadoc):
SimplePageAuthorizationStrategy authorizationStrategy = new SimplePageAuthorizationStrategy(
MySecureWebPage.class, MySignInPage.class)
{
protected boolean isAuthorized()
{
// Authorize access based on user authentication in the session
return (((MySession)Session.get()).isSignedIn());
}
};
getSecuritySettings().setAuthorizationStrategy(authorizationStrategy);
Edit in response to comment
I think the best way forward, if you're just going to use something like SimplePageAuthorizationStrategy rather than that class itself. I did something like this to capture pages that are annotated with a custom annotation:
IAuthorizationStrategy authorizationStrategy = new AbstractPageAuthorizationStrategy()
{
protected boolean isPageAuthorized(java.lang.Class<Page.class> pageClass)
{
if (pageClass.getAnnotation(Protected.class) != null) {
return (((MySession)Session.get()).isSignedIn());
} else {
return true;
}
}
};
Then you'd need to register an IUnauthorizedComponentInstantiationListener similar to what is done in SimplePageAuthorizationStrategy (link is to the source code), which should be something like:
new IUnauthorizedComponentInstantiationListener()
{
public void onUnauthorizedInstantiation(final Component component)
{
if (component instanceof Page)
{
throw new RestartResponseAtInterceptPageException(MySignInPage.class);
}
else
{
throw new UnauthorizedInstantiationException(component.getClass());
}
}
});

Zend Framework: How to display multiple actions, each requiring different authorizations levels, on a single page

Imagine I have 4 database tables, and an interface that presents forms for the management of the data in each of these tables on a single webpage (using the accordion design pattern to show only one form at a time). Each form is displayed with a list of rows in the table, allowing the user to insert a new row or select a row to edit or delete. AJAX is then used to send the request to the server.
A different set of forms must be displayed to different users, based on the application ACL.
My question is: In terms of controllers, actions, views, and layouts, what is the best architecture for this interface?
For example, so far I have a controller with add, edit and delete actions for each table. There is an indexAction for each, but it's an empty function. I've also extended Zend_Form for each table. To display the forms, I then in the IndexController pass the Forms to it's view, and echo each form. Javascript then takes care of populating the form and sending requests to the appropraite add/edit/delete action of the appropriate controller. This however doesn't allow for ACL to control the display or not of Forms to different users.
Would it be better to have the indexAction instantiate the form, and then use something like $this->render(); to render each view within the view of the indexAction of the IndexController? Would ACL then prevent certain views from being rendered?
Cheers.
There are a couple of places you could run your checks against your ACL:
Where you have your loop (or hardcoded block) to load each form.
In the constructor of each of the Form Objects, perhaps throwing a custom exception, which can be caught and appropriately handled.
From the constructor of an extension of Zend_Form from which all your custom Form objects are extended (probably the best method, as it helps reduce code duplication).
Keep in mind, that if you are using ZF to perform an AJAXy solution for your updating, your controller needs to run the ACL check in it's init() method as well, preventing unauthorized changes to your DB.
Hope that helps.
Have you solved this one yet?
I'm building a big database app with lots of nested sub-controllers as panels on a dashboard shown on the parent controller.
Simplified source code is below: comes from my parentController->indexAction()
$dashboardControllers = $this->_helper->model( 'User' )->getVisibleControllers();
foreach (array_reverse($dashboardControllers) as $controllerName) // lifo stack so put them on last first
{
if ($controllerName == 'header') continue; // always added last
// if you are wondering why a panel doesn't appear here even though the indexAction is called: it is probably because the panel is redirecting (eg if access denied). The view doesn't render on a redirect / forward
$this->_helper->actionStack( 'index', $this->parentControllerName . '_' . $controllerName );
}
$this->_helper->actionStack( 'index', $this->parentControllerName . '_header' );
If you have a better solution I'd be keen to hear it.
For my next trick I need to figure out how to display these in one, two or three columns depending on a user preference setting
I use a modified version of what's in the "Zend Framework in Action" book from Manning Press (available as PDF download if you need it now). I think you can just download the accompanying code from the book's site. You want to look at the Chapter 7 code.
Overview:
The controller is the resource, and the action is the privilege.
Put your allows & denys in the controller's init method.
I'm also using a customized version of their Controller_Action_Helper_Acl.
Every controller has a public static getAcls method:
public static function getAcls($actionName)
{
$acls = array();
$acls['roles'] = array('guest');
$acls['privileges'] = array('index','list','view');
return $acls;
}
This lets other controllers ask about this controller's permissions.
Every controller init method calls $this->_initAcls(), which is defined in my own base controller:
public function init()
{
parent::init(); // sets up ACLs
}
The parent looks like this:
public function init()
{
$this->_initAcls(); // init access control lists.
}
protected function _initAcls()
{
$to_call = array(get_class($this), 'getAcls');
$acls = call_user_func($to_call, $this->getRequest()->getActionName());
// i.e. PageController::getAcls($this->getRequest()->getActionName());
if(isset($acls['roles']) && is_array($acls['roles']))
{
if(count($acls['roles'])==0) { $acls['roles'] = null; }
if(count($acls['privileges'])==0){ $acls['privileges'] = null; }
$this->_helper->acl->allow($acls['roles'], $acls['privileges']);
}
}
Then I just have a function called:
aclink($link_text, $link_url, $module, $resource, $privilege);
It calls {$resource}Controller::getAcls() and does permission checks against them.
If they have permission, it returns the link, otherwise it returns ''.
function aclink($link_text, $link_url, $module, $resource, $privilege)
{
$auth = Zend_Auth::getInstance();
$acl = new Acl(); //wrapper for Zend_Acl
if(!$acl->has($resource))
{
$acl->add(new Zend_Acl_Resource($resource));
}
require_once ROOT.'/application/'.$module.'/controllers/'.ucwords($resource).'Controller.php';
$to_call = array(ucwords($resource).'Controller', 'getAcls');
$acls = call_user_func($to_call, $privilege);
if(isset($acls['roles']) && is_array($acls['roles']))
{
if(count($acls['roles'])==0) { $acls['roles'] = null; }
if(count($acls['privileges'])==0){ $acls['privileges'] = null; }
$acl->allow($acls['roles'], $resource, $acls['privileges']);
}
$result = $acl->isAllowed($auth, $resource, $privilege);
if($result)
{
return ''.$link_text.'';
}
else
{
return '';
}
}