This is an automated email from the ASF dual-hosted git repository. hugh pushed a commit to branch hugh/rbac-2 in repository https://gitbox.apache.org/repos/asf/superset.git
commit 70c3033393db7edf64331362de5a17876b3a308d Author: hughhhh <[email protected]> AuthorDate: Tue Aug 31 11:04:34 2021 -0700 user into dumb components --- superset-frontend/src/components/Menu/Menu.tsx | 5 +- .../src/components/Menu/MenuRight.tsx | 255 +++++++++++---------- superset-frontend/src/views/App.tsx | 2 +- 3 files changed, 142 insertions(+), 120 deletions(-) diff --git a/superset-frontend/src/components/Menu/Menu.tsx b/superset-frontend/src/components/Menu/Menu.tsx index 098aa3a..10baa91 100644 --- a/superset-frontend/src/components/Menu/Menu.tsx +++ b/superset-frontend/src/components/Menu/Menu.tsx @@ -61,6 +61,7 @@ export interface MenuProps { brand: BrandProps; navbar_right: NavBarProps; settings: MenuObjectProps[]; + user: object; }; isFrontendRoute?: (path?: string) => boolean; } @@ -171,7 +172,7 @@ const { SubMenu } = DropdownMenu; const { useBreakpoint } = Grid; export function Menu({ - data: { menu, brand, navbar_right: navbarRight, settings }, + data: { menu, brand, navbar_right: navbarRight, settings, user }, isFrontendRoute = () => false, }: MenuProps) { const [showMenu, setMenu] = useState<MenuMode>('horizontal'); @@ -283,7 +284,6 @@ export function Menu({ if (typeof c === 'string') { return c; } - return { ...c, isFrontendRoute: isFrontendRoute(c.url), @@ -301,6 +301,7 @@ export function Menu({ settings={settings} navbarRight={navbarRight} isFrontendRoute={isFrontendRoute} + user={user} /> </Col> </Row> diff --git a/superset-frontend/src/components/Menu/MenuRight.tsx b/superset-frontend/src/components/Menu/MenuRight.tsx index 35d2cb0..a9f1456 100644 --- a/superset-frontend/src/components/Menu/MenuRight.tsx +++ b/superset-frontend/src/components/Menu/MenuRight.tsx @@ -21,6 +21,7 @@ import { MainNav as Menu } from 'src/common/components'; import { t, styled, css, SupersetTheme } from '@superset-ui/core'; import { Link } from 'react-router-dom'; import Icons from 'src/components/Icons'; +import findPermission from 'src/dashboard/util/findPermission'; import LanguagePicker from './LanguagePicker'; import { NavBarProps, MenuObjectProps } from './Menu'; @@ -29,16 +30,22 @@ export const dropdownItems = [ label: t('SQL query'), url: '/superset/sqllab?new=true', icon: 'fa-fw fa-search', + perm: 'can_sqllab', + view: 'Superset', }, { label: t('Chart'), url: '/chart/add', icon: 'fa-fw fa-bar-chart', + perm: 'can_write', + view: 'Dashboard', }, { label: t('Dashboard'), url: '/dashboard/new', icon: 'fa-fw fa-dashboard', + perm: 'can_write', + view: 'Chart', }, ]; @@ -76,6 +83,7 @@ interface RightMenuProps { settings: MenuObjectProps[]; navbarRight: NavBarProps; isFrontendRoute: (path?: string) => boolean; + user: any; } const RightMenu = ({ @@ -83,127 +91,140 @@ const RightMenu = ({ settings, navbarRight, isFrontendRoute, -}: RightMenuProps) => ( - <StyledDiv align={align}> - <Menu mode="horizontal"> - {!navbarRight.user_is_anonymous && ( - <SubMenu - data-test="new-dropdown" - title={ - <StyledI data-test="new-dropdown-icon" className="fa fa-plus" /> - } - icon={<Icons.TriangleDown />} - > - {dropdownItems.map(menu => ( - <Menu.Item key={menu.label}> - <a href={menu.url}> - <i - data-test={`menu-item-${menu.label}`} - className={`fa ${menu.icon}`} - />{' '} - {menu.label} - </a> - </Menu.Item> - ))} - </SubMenu> - )} - <SubMenu title="Settings" icon={<Icons.TriangleDown iconSize="xl" />}> - {settings.map((section, index) => [ - <Menu.ItemGroup key={`${section.label}`} title={section.label}> - {section.childs?.map(child => { - if (typeof child !== 'string') { - return ( - <Menu.Item key={`${child.label}`}> - {isFrontendRoute(child.url) ? ( - <Link to={child.url || ''}>{child.label}</Link> - ) : ( - <a href={child.url}>{child.label}</a> - )} - </Menu.Item> - ); - } - return null; - })} - </Menu.ItemGroup>, - index < settings.length - 1 && <Menu.Divider />, - ])} + user, +}: RightMenuProps) => { + const { roles } = user; - {!navbarRight.user_is_anonymous && [ - <Menu.Divider key="user-divider" />, - <Menu.ItemGroup key="user-section" title={t('User')}> - {navbarRight.user_profile_url && ( - <Menu.Item key="profile"> - <a href={navbarRight.user_profile_url}>{t('Profile')}</a> - </Menu.Item> - )} - {navbarRight.user_info_url && ( - <Menu.Item key="info"> - <a href={navbarRight.user_info_url}>{t('Info')}</a> - </Menu.Item> + // if user has any of these roles the dropdown will appear + const canSql = findPermission('can_sqllab', 'Superset', roles); + const canDashboard = findPermission('can_write', 'Dashboard', roles); + const canChart = findPermission('can_write', 'Chart', roles); + const showActionDropdown = canSql || canChart || canDashboard; + return ( + <StyledDiv align={align}> + <Menu mode="horizontal"> + {!navbarRight.user_is_anonymous && showActionDropdown && ( + <SubMenu + data-test="new-dropdown" + title={ + <StyledI data-test="new-dropdown-icon" className="fa fa-plus" /> + } + icon={<Icons.TriangleDown />} + > + {dropdownItems.map( + menu => + findPermission(menu.perm, menu.view, roles) && ( + <Menu.Item key={menu.label}> + <a href={menu.url}> + <i + data-test={`menu-item-${menu.label}`} + className={`fa ${menu.icon}`} + />{' '} + {menu.label} + </a> + </Menu.Item> + ), )} - <Menu.Item key="logout"> - <a href={navbarRight.user_logout_url}>{t('Logout')}</a> - </Menu.Item> - </Menu.ItemGroup>, - ]} - {(navbarRight.version_string || navbarRight.version_sha) && [ - <Menu.Divider key="version-info-divider" />, - <Menu.ItemGroup key="about-section" title={t('About')}> - <div className="about-section"> - {navbarRight.show_watermark && ( - <div css={versionInfoStyles}> - {t('Powered by Apache Superset')} - </div> - )} - {navbarRight.version_string && ( - <div css={versionInfoStyles}> - Version: {navbarRight.version_string} - </div> + </SubMenu> + )} + <SubMenu title="Settings" icon={<Icons.TriangleDown iconSize="xl" />}> + {settings.map((section, index) => [ + <Menu.ItemGroup key={`${section.label}`} title={section.label}> + {section.childs?.map(child => { + if (typeof child !== 'string') { + return ( + <Menu.Item key={`${child.label}`}> + {isFrontendRoute(child.url) ? ( + <Link to={child.url || ''}>{child.label}</Link> + ) : ( + <a href={child.url}>{child.label}</a> + )} + </Menu.Item> + ); + } + return null; + })} + </Menu.ItemGroup>, + index < settings.length - 1 && <Menu.Divider />, + ])} + + {!navbarRight.user_is_anonymous && [ + <Menu.Divider key="user-divider" />, + <Menu.ItemGroup key="user-section" title={t('User')}> + {navbarRight.user_profile_url && ( + <Menu.Item key="profile"> + <a href={navbarRight.user_profile_url}>{t('Profile')}</a> + </Menu.Item> )} - {navbarRight.version_sha && ( - <div css={versionInfoStyles}> - SHA: {navbarRight.version_sha} - </div> + {navbarRight.user_info_url && ( + <Menu.Item key="info"> + <a href={navbarRight.user_info_url}>{t('Info')}</a> + </Menu.Item> )} - </div> - </Menu.ItemGroup>, - ]} - </SubMenu> - {navbarRight.show_language_picker && ( - <LanguagePicker - locale={navbarRight.locale} - languages={navbarRight.languages} - /> + <Menu.Item key="logout"> + <a href={navbarRight.user_logout_url}>{t('Logout')}</a> + </Menu.Item> + </Menu.ItemGroup>, + ]} + {(navbarRight.version_string || navbarRight.version_sha) && [ + <Menu.Divider key="version-info-divider" />, + <Menu.ItemGroup key="about-section" title={t('About')}> + <div className="about-section"> + {navbarRight.show_watermark && ( + <div css={versionInfoStyles}> + {t('Powered by Apache Superset')} + </div> + )} + {navbarRight.version_string && ( + <div css={versionInfoStyles}> + Version: {navbarRight.version_string} + </div> + )} + {navbarRight.version_sha && ( + <div css={versionInfoStyles}> + SHA: {navbarRight.version_sha} + </div> + )} + </div> + </Menu.ItemGroup>, + ]} + </SubMenu> + {navbarRight.show_language_picker && ( + <LanguagePicker + locale={navbarRight.locale} + languages={navbarRight.languages} + /> + )} + </Menu> + {navbarRight.documentation_url && ( + <StyledAnchor + href={navbarRight.documentation_url} + target="_blank" + rel="noreferrer" + title={t('Documentation')} + > + <i className="fa fa-question" /> + + </StyledAnchor> + )} + {navbarRight.bug_report_url && ( + <StyledAnchor + href={navbarRight.bug_report_url} + target="_blank" + rel="noreferrer" + title={t('Report a bug')} + > + <i className="fa fa-bug" /> + </StyledAnchor> + )} + {navbarRight.user_is_anonymous && ( + <StyledAnchor href={navbarRight.user_login_url}> + <i className="fa fa-fw fa-sign-in" /> + {t('Login')} + </StyledAnchor> )} - </Menu> - {navbarRight.documentation_url && ( - <StyledAnchor - href={navbarRight.documentation_url} - target="_blank" - rel="noreferrer" - title={t('Documentation')} - > - <i className="fa fa-question" /> - - </StyledAnchor> - )} - {navbarRight.bug_report_url && ( - <StyledAnchor - href={navbarRight.bug_report_url} - target="_blank" - rel="noreferrer" - title={t('Report a bug')} - > - <i className="fa fa-bug" /> - </StyledAnchor> - )} - {navbarRight.user_is_anonymous && ( - <StyledAnchor href={navbarRight.user_login_url}> - <i className="fa fa-fw fa-sign-in" /> - {t('Login')} - </StyledAnchor> - )} - </StyledDiv> -); + </StyledDiv> + ); +}; export default RightMenu; diff --git a/superset-frontend/src/views/App.tsx b/superset-frontend/src/views/App.tsx index 445a875..b4a2d19 100644 --- a/superset-frontend/src/views/App.tsx +++ b/superset-frontend/src/views/App.tsx @@ -41,7 +41,7 @@ setupApp(); const container = document.getElementById('app'); const bootstrap = JSON.parse(container?.getAttribute('data-bootstrap') ?? '{}'); const user = { ...bootstrap.user }; -const menu = { ...bootstrap.common.menu_data }; +const menu = { ...bootstrap.common.menu_data, user }; const common = { ...bootstrap.common }; initFeatureFlags(bootstrap.common.feature_flags);
