React-admin: reload admin after login ?

Created on 28 Mar 2017  路  8Comments  路  Source: marmelab/react-admin

Hi,

I am very new to react thing, so I may have missed something huge.

I need to set different resources depending of the user profile that just loged in.
As the auth client is part of the admin component, I need to be able to "refresh" the full app after login process.
I tried something like this :

`const App = () => {
let userProfile = localStorage.getItem("profile");
let myResources=[];

myResources.push(<Resource name="contact" key="contact"/>);
myResources.push(<Resource name="userLicense" key="userLicense"/>);
myResources.push(<Resource name="productVersion" key="productVersion"/>);


if('SUPERADMIN' == userProfile) {
    myResources.push(<Resource name="siteLicense" key="siteLicense" options={{ label: 'Licenses' }} list={LicenseList} icon={LicenseIcon} edit={LicenseShow}/>);
    myResources.push(<Resource name="dataLicense" key="dataLicense" options={{ label: 'Data' }} list={DataList} icon={DataIcon} edit={DataShow}/>);
    myResources.push(<Resource name="user" key="user" options={{ label: 'Users' }} list={UserList} icon={UserIcon} edit={UserEdit} create={UserCreate} remove={Delete}/>);
    myResources.push(<Resource name="product" key="product" edit={ProductShow}/>);
} else {
    myResources.push(<Resource name="user" key="user" options={{ label: 'Users' }} list={UserList} icon={UserIcon} edit={UserEdit} />);
    myResources.push(<Resource name="product" key="product" edit={ProductShow}/>);
}
return <Admin authClient={authClient} restClient={restClient}>
    {myResources}           
</Admin>

};
`
But obviously, auth process comes to late and when the App component is built, le local storage profile item contains the profile of the login used the previous time ...

So I really need to refresh the app after login.

How could I achieve this without breaking all this very nice admin on rest thing ?

Samuel.

Most helpful comment

Thanks god a solution came in the form of an additional package :
https://github.com/marmelab/aor-permissions

All 8 comments

I am trying to do something like this in my ListView

const user = localStorage.getItem('user');
export const HostList = (props) => (
    <List {...props} filters={<HostFilter />} sort={{ field: 'id', order: 'ASC' }} perPage={25}>
        <Datagrid>
                { user['sysadmin'] ? <EditButton /> : <ShowButton /> }
                { user['sysadmin'] ? <DeleteButton /> : <span /> }
        </Datagrid>
    </List>
);

But same as you, the App is generated before the localStorage.setItem('user', jsonResponse['user']); that is made in my AuthClient ... So basically Authentication works but Authorization not...

And i don't see how we can conditionnaly load components (like the 'show' or 'edit' button i am trying to implement) from the AUTH_CHECK section of AuthClient

    if (type === AUTH_CHECK) {
        const { resource } = params;
        if (resource === 'posts') {
            // check credentials for the posts resource
        }
        if (resource === 'comments') {
            // check credentials for the comments resource
        }
    }

So to me the sentence Using authClient and checkCredentials is enough to implement a full-featured authorization system if the authentication relies on a username and password. from https://marmelab.com/admin-on-rest/Authentication.html#customizing-the-login-and-logout-components
isn't true

What is the recommended way to achieve this ?

In your case, you could do that I think :

export const HostList = (props) => {
const user = localStorage.getItem('user');
    return <List {...props} filters={<HostFilter />} sort={{ field: 'id', order: 'ASC' }} perPage={25}>
        <Datagrid>
                { user['sysadmin'] ? <EditButton /> : <ShowButton /> }
                { user['sysadmin'] ? <DeleteButton /> : <span /> }
        </Datagrid>
    </List>
};

It should work because HostList will be built only when you get into it, so after the login.

Arf my issue was in fact totally unrelated... sorry to spam

I sorted out that my localStorage.setItem('user', jsonResponse['user']); needed in fact to be localStorage.setItem('user', JSON.stringify(jsonResponse['user'])); or I couldn't read the values (only text is stored in localStorage, see https://stackoverflow.com/questions/2010892/storing-objects-in-html5-localstorage)
So that did the trick, and with the code you gave me this is working as expected..

However I now run into exactly the same issue as you :
Having the possibility to remove some views/routes/items in the menu based on User data.

So we still do not know how to accomplish this "the admin on rest way" because as you said

But obviously, auth process comes to late and when the App component is built, le local storage profile item contains the profile of the login used the previous time ...

Let's go for my really ugly solution !

First of all, we need to overide the menu component to tell it how to display resources depending on user profile. so I copied the original Menu component from admin-on-rest and replaced the render part :
const Menu = ({ hasDashboard, onMenuTap, resources, translate, logout }) =>...

by this :

const Menu = React.createClass({
    componentDidMount: function() {
    window.mainMenuComp = this;
  },
  componentWillUnmount: function() {
    window.mainMenuComp = null;
  },
  render: function() {
     const { hasDashboard, onMenuTap, resources, translate, logout } = this.props;
     const userProfile = localStorage.getItem('profile');
     return <div style={styles.main}>
        {hasDashboard && <DashboardMenuItem onTouchTap={onMenuTap}/>}
        {resources
            .filter(r => r.list && r.options.allowedProfiles.indexOf(userProfile) >= 0)
            .map(resource =>
                <MenuItem
                    key={resource.name}
                    containerElement={<Link to={`/${resource.name}`} />}
                    primaryText={translatedResourceName(resource, translate)}
                    leftIcon={<resource.icon />}
                    onTouchTap={onMenuTap}
                />,
            )
        }
        {logout}
    </div>
  }
});

Then, on the admin declaration, we need to use our wonderfull customized menu and to add allowedProfiles array in resources options :

<Admin menu={myMenu} authClient={authClient} restClient={restClient}>
        <Resource name="productVersion" key="productVersion" options={{ label: 'Licenses',allowedProfiles:['SUPERADMIN','ADVDMIN'] }}/> 
        <Resource name="siteLicense" key="siteLicense" options={{ label: 'Licenses',allowedProfiles:['SUPERADMIN','ADVDMIN'] }} list={LicenseList} icon={LicenseIcon} edit={LicenseShow}/>      
        <Resource name="userLicense" key="userLicense" edit={UserLicenseShow} list={UserLicenseList} options={{ label: 'Licenses',allowedProfiles:['USER'] }}/>
</Admin>

And the last thing (that's the really ugly part ...), in the authClient code, when auth succeded :

if (window.mainMenuComp && window.mainMenuComp != null) {
    window.mainMenuComp.forceUpdate();
}

Ok, it works, but I will have then to handle profile everywhere (because I can define my resources only once), whereas the idea of defining the type of component to edit or show (or even the label) on the resource itself depending on user profile would have been really nice.

Again, if I have missed something huge, please tell me !

Maybe ugly but if at least it works... Might be a solution for my project as well. Thanks for sharing !

And for the "missed something huge" I am as new as you in React so I couldn't tell you if that was the case... I am probably missing lot of stuff as well :/

@fzaninotto could you add an example to the demo app or docs of neat way to handle this, to help us newbies? :)

This is not supported for now, so you'll have to take the route of a Custom App.

Thanks god a solution came in the form of an additional package :
https://github.com/marmelab/aor-permissions

Was this page helpful?
0 / 5 - 0 ratings

Related issues

nicgirault picture nicgirault  路  3Comments

yangjiamu picture yangjiamu  路  3Comments

rkyrychuk picture rkyrychuk  路  3Comments

Dragomir-Ivanov picture Dragomir-Ivanov  路  3Comments

aserrallerios picture aserrallerios  路  3Comments