Skip to content

Commit

Permalink
🔀 Merge pull request #691 from pinarruiz/feature/extend-visibility-items
Browse files Browse the repository at this point in the history
[FEATURE] Extend visibility to section items
  • Loading branch information
Lissy93 committed Jun 3, 2022
2 parents d470f5b + 48a367f commit ef786db
Show file tree
Hide file tree
Showing 8 changed files with 233 additions and 83 deletions.
21 changes: 14 additions & 7 deletions docs/authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ Once authentication is enabled, so long as there is no valid token in cookie sto
With authentication setup, by default no access is allowed to your dashboard without first logging in with valid credentials. Guest mode can be enabled to allow for read-only access to a secured dashboard by any user, without the need to log in. A guest user cannot write any changes to the config file, but can apply modifications locally (stored in their browser). You can enable guest access, by setting `appConfig.auth.enableGuestAccess: true`.

### Granular Access
You can use the following properties to make certain sections only visible to some users, or hide sections from guests.
- `hideForUsers` - Section will be visible to all users, except for those specified in this list
- `showForUsers` - Section will be hidden from all users, except for those specified in this list
- `hideForGuests` - Section will be visible for logged in users, but not for guests
You can use the following properties to make certain sections or items only visible to some users, or hide sections and items from guests.
- `hideForUsers` - Section or Item will be visible to all users, except for those specified in this list
- `showForUsers` - Section or Item will be hidden from all users, except for those specified in this list
- `hideForGuests` - Section or Item will be visible for logged in users, but not for guests

For Example:

Expand All @@ -71,7 +71,9 @@ For Example:
displayData:
hideForGuests: true
items:
...
- title: Hide Me
displayData:
hideForUsers: [alicia, bob]
```

### Permissions
Expand Down Expand Up @@ -149,9 +151,9 @@ appConfig:
Note that if you are using Keycloak V 17 or older, you will also need to set `legacySupport: true` (also under `appConfig.auth.keycloak`). This is because the API endpoint was updated in later versions.

### 4. Add groups and roles (Optional)
Keycloak allows you to assign users roles and groups. You can use these values to configure who can access various sections in Dashy.
Keycloak allows you to assign users roles and groups. You can use these values to configure who can access various sections or items in Dashy.
Keycloak server administration and configuration is a deep topic; please refer to the [server admin guide](https://www.keycloak.org/docs/latest/server_admin/index.html#assigning-permissions-and-access-using-roles-and-groups) to see details about creating and assigning roles and groups.
Once you have groups or roles assigned to users you can configure access under each sections `displayData.showForKeycloakUser` and `displayData.hideForKeycloakUser`.
Once you have groups or roles assigned to users you can configure access under each section or item `displayData.showForKeycloakUser` and `displayData.hideForKeycloakUser`.
Both show and hide configurations accept a list of `groups` and `roles` that limit access. If a users data matches one or more items in these lists they will be allowed or excluded as defined.
```yaml
sections:
Expand All @@ -161,6 +163,11 @@ sections:
roles: ['canViewDevResources']
hideForKeycloakUsers:
groups: ['ProductTeam']
items:
- title: Not Visible for developers
displayData:
hideForKeycloakUsers:
groups: ['DevelopmentTeam']
```

Depending on how you're hosting Dashy and Keycloak, you may also need to set some HTTP headers, to prevent a CORS error. This would typically be the `Access-Control-Allow-Origin [URL-of Dashy]` on your Keycloak instance. See the [Setting Headers](https://github.com/Lissy93/dashy/blob/master/docs/management.md#setting-headers) guide in the management docs for more info.
Expand Down
27 changes: 22 additions & 5 deletions docs/configuring.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,12 @@ The following file provides a reference of all supported configuration options.
- [`keycloak`](#appconfigauthkeycloak-optional) - Auth config for Keycloak
- [**`sections`**](#section) - List of sections
- [`displayData`](#sectiondisplaydata-optional) - Section display settings
- [`show/hideForKeycloakUsers`](#sectiondisplaydatahideforkeycloakusers-and-sectiondisplaydatashowforkeycloakusers) - Set user controls
- [`show/hideForKeycloakUsers`](#sectiondisplaydatahideforkeycloakusers-sectiondisplaydatashowforkeycloakusers-itemdisplaydatahideforkeycloakusers-and-itemdisplaydatashowforkeycloakusers) - Set user controls
- [`icon`](#sectionicon-and-sectionitemicon) - Icon for a section
- [`items`](#sectionitem) - List of items
- [`icon`](#sectionicon-and-sectionitemicon) - Icon for an item
- [`displayData`](#itemdisplaydata-optional) - Item display settings
- [`show/hideForKeycloakUsers`](#sectiondisplaydatahideforkeycloakusers-sectiondisplaydatashowforkeycloakusers-itemdisplaydatahideforkeycloakusers-and-itemdisplaydatashowforkeycloakusers) - Set user controls
- [`widgets`](#sectionwidget-optional) - List of widgets
- [**Notes**](#notes)
- [Editing Config through the UI](#editing-config-through-the-ui)
Expand Down Expand Up @@ -224,9 +226,24 @@ For more info, see the **[Authentication Docs](/docs/authentication.md)**
**`color`** | `string` | _Optional_ | An optional color for the text and font-awesome icon to be displayed in. Note that this will override the current theme and so may not display well
**`backgroundColor`** | `string` | _Optional_ | An optional background fill color for the that given item. Again, this will override the current theme and so might not display well against the background
**`provider`** | `string` | _Optional_ | The name of the provider for a given service, useful for when including hosted apps. In some themes, this is visible under the item name
**`displayData`** | `object` | _Optional_ | Meta-data to optionally overide display settings for a given item. See [`displayData`](#itemdisplaydata-optional)

**[⬆️ Back to Top](#configuring)**


### `item.displayData` _(optional)_

**Field** | **Type** | **Required**| **Description**
--- | --- | --- | ---
**`hideForUsers`** | `string[]` | _Optional_ | Current item will be visible to all users, except for those specified in this list
**`showForUsers`** | `string[]` | _Optional_ | Current item will be hidden from all users, except for those specified in this list
**`hideForGuests`** | `boolean` | _Optional_ | Current item will be visible for logged in users, but not for guests (see `appConfig.enableGuestAccess`). Defaults to `false`
**`hideForKeycloakUsers`** | `object` | _Optional_ | Current item will be visible to all keycloak users, except for those configured via these groups and roles. See `hideForKeycloakUsers`
**`showForKeycloakUsers`** | `object` | _Optional_ | Current item will be hidden from all keycloak users, except for those configured via these groups and roles. See `showForKeycloakUsers`

**[⬆️ Back to Top](#configuring)**


### `section.widget` _(optional)_

**Field** | **Type** | **Required**| **Description**
Expand Down Expand Up @@ -259,7 +276,7 @@ For more info, see the **[Authentication Docs](/docs/authentication.md)**
**`showForUsers`** | `string[]` | _Optional_ | Current section will be hidden from all users, except for those specified in this list
**`hideForGuests`** | `boolean` | _Optional_ | Current section will be visible for logged in users, but not for guests (see `appConfig.enableGuestAccess`). Defaults to `false`
**`hideForKeycloakUsers`** | `object` | _Optional_ | Current section will be visible to all keycloak users, except for those configured via these groups and roles. See `hideForKeycloakUsers`
**`showForKeycloakUsers`** | `object` | _Optional_ | Current section will be hidden from all keyclaok users, except for those configured via these groups and roles. See `showForKeycloakUsers`
**`showForKeycloakUsers`** | `object` | _Optional_ | Current section will be hidden from all keycloak users, except for those configured via these groups and roles. See `showForKeycloakUsers`

**[⬆️ Back to Top](#configuring)**

Expand All @@ -271,12 +288,12 @@ For more info, see the **[Authentication Docs](/docs/authentication.md)**

**[⬆️ Back to Top](#configuring)**

### `section.displayData.hideForKeycloakUsers` and `section.displayData.showForKeycloakUsers`
### `section.displayData.hideForKeycloakUsers`, `section.displayData.showForKeycloakUsers`, `item.displayData.hideForKeycloakUsers` and `item.displayData.showForKeycloakUsers`

**Field** | **Type** | **Required**| **Description**
--- |------------| --- | ---
**`groups`** | `string[]` | _Optional_ | Current Section will be hidden or shown based on the user having any of the groups in this list
**`roles`** | `string[]` | _Optional_ | Current Section will be hidden or shown based on the user having any of the roles in this list
**`groups`** | `string[]` | _Optional_ | Current Section or Item will be hidden or shown based on the user having any of the groups in this list
**`roles`** | `string[]` | _Optional_ | Current Section or Item will be hidden or shown based on the user having any of the roles in this list

**[⬆️ Back to Top](#configuring)**

Expand Down
10 changes: 9 additions & 1 deletion src/components/Workspace/SideBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<transition name="slide">
<SideBarSection
v-if="isOpen[index]"
:items="section.items"
:items="filterTiles(section.items)"
@launch-app="launchApp"
/>
</transition>
Expand All @@ -36,6 +36,7 @@ import SideBarItem from '@/components/Workspace/SideBarItem.vue';
import SideBarSection from '@/components/Workspace/SideBarSection.vue';
import IconHome from '@/assets/interface-icons/application-home.svg';
import IconMinimalView from '@/assets/interface-icons/application-minimal.svg';
import { checkItemVisibility } from '@/utils/CheckItemVisibility';
export default {
name: 'SideBar',
Expand Down Expand Up @@ -77,6 +78,13 @@ export default {
});
});
},
/* Return a list with visible items on a section to the user or guest */
filterTiles(allTiles) {
if (!allTiles) {
return [];
}
return allTiles.filter((tile) => checkItemVisibility(tile));
},
},
mounted() {
if (this.sections.length === 1) { // If only 1 section, go ahead and open it
Expand Down
8 changes: 6 additions & 2 deletions src/mixins/HomeMixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import Defaults, { localStorageKeys, iconCdns } from '@/utils/defaults';
import Keys from '@/utils/StoreMutations';
import { searchTiles } from '@/utils/Search';
import { checkItemVisibility } from '@/utils/CheckItemVisibility';

const HomeMixin = {
props: {
Expand Down Expand Up @@ -64,8 +65,11 @@ const HomeMixin = {
},
/* Returns only the tiles that match the users search query */
filterTiles(allTiles) {
if (!allTiles) return [];
return searchTiles(allTiles, this.searchValue);
if (!allTiles) {
return [];
}
const visibleTiles = allTiles.filter((tile) => checkItemVisibility(tile));
return searchTiles(visibleTiles, this.searchValue);
},
/* Checks if any sections or items use icons from a given CDN */
checkIfIconLibraryNeeded(prefix) {
Expand Down
19 changes: 19 additions & 0 deletions src/utils/CheckItemVisibility.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* A helper function that checks if an item is visible based on current users permissions
* Checks an item displayData for hideForUsers, showForUsers and hideForGuests
* Returns a boolean that determines if the user has the required permissions
*/

// Import helper functions from auth, to get current user, and check if guest
import { getCurrentUser, isLoggedInAsGuest } from '@/utils/Auth';
import { isVisibleToUser } from '@/utils/IsVisibleToUser';

/* Putting it all together, the function to export */
export const checkItemVisibility = (item) => {
const currentUser = getCurrentUser(); // Get current user object
const isGuest = isLoggedInAsGuest(); // Check if current user is a guest
const displayData = item.displayData || {};
return isVisibleToUser(displayData, currentUser, isGuest);
};

export default checkItemVisibility;
71 changes: 3 additions & 68 deletions src/utils/CheckSectionVisibility.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,80 +6,15 @@

// Import helper functions from auth, to get current user, and check if guest
import { getCurrentUser, isLoggedInAsGuest } from '@/utils/Auth';
import { localStorageKeys } from '@/utils/defaults';

/* Helper function, checks if a given testValue is found in the visibility list */
const determineVisibility = (visibilityList, testValue) => {
let isFound = false;
visibilityList.forEach((visibilityItem) => {
if (visibilityItem.toLowerCase() === testValue.toLowerCase()) isFound = true;
});
return isFound;
};

/* Helper function, determines if two arrays have any intersecting elements
(one or more items that are the same) */
const determineIntersection = (source = [], target = []) => {
const intersections = source.filter(item => target.indexOf(item) !== -1);
return intersections.length > 0;
};

/* Returns false if this section should not be rendered for the current user/ guest */
const isSectionVisibleToUser = (displayData, currentUser, isGuest) => {
// Checks if user explicitly has access to a certain section
const checkVisibility = () => {
if (!currentUser) return true;
const hideForUsers = displayData.hideForUsers || [];
const cUsername = currentUser.user.toLowerCase();
return !determineVisibility(hideForUsers, cUsername);
};
// Checks if user is explicitly prevented from viewing a certain section
const checkHiddenability = () => {
if (!currentUser) return true;
const cUsername = currentUser.user.toLowerCase();
const showForUsers = displayData.showForUsers || [];
if (showForUsers.length < 1) return true;
return determineVisibility(showForUsers, cUsername);
};
const checkKeycloakVisibility = () => {
if (!displayData.hideForKeycloakUsers) return true;

const { groups, roles } = JSON.parse(localStorage.getItem(localStorageKeys.KEYCLOAK_INFO) || '{}');
const hideForGroups = displayData.hideForKeycloakUsers.groups || [];
const hideForRoles = displayData.hideForKeycloakUsers.roles || [];

return !(determineIntersection(hideForRoles, roles)
|| determineIntersection(hideForGroups, groups));
};
const checkKeycloakHiddenability = () => {
if (!displayData.showForKeycloakUsers) return true;

const { groups, roles } = JSON.parse(localStorage.getItem(localStorageKeys.KEYCLOAK_INFO) || '{}');
const showForGroups = displayData.showForKeycloakUsers.groups || [];
const showForRoles = displayData.showForKeycloakUsers.roles || [];

return determineIntersection(showForRoles, roles)
|| determineIntersection(showForGroups, groups);
};
// Checks if the current user is a guest, and if section allows for guests
const checkIfHideForGuest = () => {
const hideForGuest = displayData.hideForGuests;
return !(hideForGuest && isGuest);
};
return checkVisibility()
&& checkHiddenability()
&& checkIfHideForGuest()
&& checkKeycloakVisibility()
&& checkKeycloakHiddenability();
};
import { isVisibleToUser } from '@/utils/IsVisibleToUser';

/* Putting it all together, the function to export */
const checkSectionVisibility = (sections) => {
export const checkSectionVisibility = (sections) => {
const currentUser = getCurrentUser(); // Get current user object
const isGuest = isLoggedInAsGuest(); // Check if current user is a guest
return sections.filter((currentSection) => {
const displayData = currentSection.displayData || {};
return isSectionVisibleToUser(displayData, currentUser, isGuest);
return isVisibleToUser(displayData, currentUser, isGuest);
});
};

Expand Down
84 changes: 84 additions & 0 deletions src/utils/ConfigSchema.json
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,90 @@
"type": "string",
"description": "The destination to navigate to when item is clicked, expressed as a valid URL, IP or hostname"
},
"displayData": {
"title": "Display Data",
"type": "object",
"additionalProperties": false,
"description": "Optional meta data for customizing an item",
"properties": {
"hideForUsers": {
"title": "Hide for Users",
"type": "array",
"description": "Item will be visible to all users, except for those specified in this list",
"items": {
"type": "string",
"description": "Username for the user that will not be able to view this item"
}
},
"showForUsers": {
"title": "Show for Users",
"type": "array",
"description": "Item will be hidden from all users, except for those specified in this list",
"items": {
"type": "string",
"description": "Username for the user that will have access to this item"
}
},
"hideForGuests": {
"title": "Hide for Guests?",
"type": "boolean",
"default": false,
"description": "If set to true, item will be visible for logged in users, but not for guests"
},
"showForKeycloakUsers": {
"title": "Show for select Keycloak groups or roles",
"type": "object",
"description": "Configure the Keycloak groups or roles that will have access to this item",
"additionalProperties": false,
"properties": {
"groups": {
"title": "Show for Groups",
"type": "array",
"description": "Item will be hidden from all users except those with one or more of these groups",
"items": {
"type": "string",
"description": "Name of the group that will be able to view this item"
}
},
"roles": {
"title": "Show for Roles",
"type": "array",
"description": "Item will be hidden from all users except those with one or more of these roles",
"items": {
"type": "string",
"description": "Name of the role that will be able to view this item"
}
}
}
},
"hideForKeycloakUsers": {
"title": "Hide for select Keycloak groups or roles",
"type": "object",
"description": "Configure the Keycloak groups or roles that will not have access to this item",
"additionalProperties": false,
"properties": {
"groups": {
"title": "Hide for Groups",
"type": "array",
"description": "Item will be hidden from users with any of these groups",
"items": {
"type": "string",
"description": "name of the group that will not be able to view this item"
}
},
"roles": {
"title": "Hide for Roles",
"type": "array",
"description": "Item will be hidden from users with any of roles",
"items": {
"type": "string",
"description": "name of the role that will not be able to view this item"
}
}
}
}
}
},
"target": {
"title": "Opening Method",
"type": "string",
Expand Down
Loading

0 comments on commit ef786db

Please sign in to comment.