Skip to content

Commit

Permalink
Merge pull request #4186 from appirio-tech/dev
Browse files Browse the repository at this point in the history
Connect 2.14.0
  • Loading branch information
RishiRajSahu committed Nov 11, 2020
2 parents 5e55b9f + ca74afe commit 193a054
Show file tree
Hide file tree
Showing 92 changed files with 2,062 additions and 1,589 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ workflows:
- build-dev
filters:
branches:
only: ['feature/faqs', 'feature/project_september']
only: ['feature/unified-permissions']

- deployProd:
context : org-global
Expand Down
13 changes: 8 additions & 5 deletions docs/permissions-guide/permissions-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,23 @@ Let's say you would like to add a new place in code where you want to check user

2. After you add a new permission, regenerate [permissions list](https://htmlpreview.github.io/?https://github.com/appirio-tech/connect-app/blob/dev/docs/permissions.html) by running `npm run generate:doc:permissions`.

3. To check if user has permission in code use method `hasPermission(permission)`.
3. To check if logged-in user has permission in code use method `hasPermission(permission)`.

Example:<br>

```js
import PERMISSIONS from 'config/permissions'
import { PERMISSIONS } from 'config/permissions'
import { hasPermission } from 'helpers/permissions'

if (hasPermission(PERMISSIONS.MANAGE_PROJECT_PLAN)) {
...
}
```

- Note, optionally, you may pass the `project` object like this `hasPermission(permission, project)`. But you don't have to as `hasPermission` gets `project` object from the Redux Store (`projectState.project`) automatically. Only in case if you want to check user permission to another project which is not loaded into the Redux Store then you may pass `project` explicitly.
4. If you would like to check permissions for other user (not the current user) or for other project (not the current project) you may pass the second argument `entities: { project?: object, user?: object }`:
- `hasPermission(permission, { project })` - check permissions for another project
- `hasPermission(permission, { user })` - check permissions for another user
- `hasPermission(permission, { project, user })` - check permissions for another project and user

## Roles

Expand All @@ -49,4 +52,4 @@ By default every user has one role `Topcoder User`, generally this means that su

When user joins some project and become a member of the project, such a user has one **Project Role** inside that project. One user may have different **Project Role** in different projects. See [the list of all Project Roles](https://github.com/appirio-tech/connect-app/blob/dev/src/config/constants.js#L638-L647) which we use in Connect App.

<img src="./images/project-roles.png" width="720">
<img src="./images/project-roles.png" width="720">
1,209 changes: 983 additions & 226 deletions docs/permissions.html

Large diffs are not rendered by default.

20 changes: 13 additions & 7 deletions scripts/permissions-doc/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import _ from 'lodash'
import fs from 'fs'
import path from 'path'
import handlebars from 'handlebars'
import PERMISSIONS from '../../src/config/permissions'
import { PERMISSIONS } from '../../src/config/permissions'
import {
PROJECT_ROLE_CUSTOMER,
PROJECT_ROLE_COPILOT,
Expand Down Expand Up @@ -42,6 +42,8 @@ import {
const docTemplatePath = path.resolve(__dirname, './template.hbs')
const outputDocPath = path.resolve(__dirname, '../../docs/permissions.html')

handlebars.registerHelper('istrue', value => value === true)

/**
* All Project Roles
*/
Expand Down Expand Up @@ -121,8 +123,8 @@ function normalizePermission(permission) {

if (!normalizedPermission.allowRule) {
normalizedPermission = {
_meta: permission._meta,
allowRule: _.omit(permission, '_meta')
meta: permission.meta,
allowRule: _.omit(permission, 'meta')
}
}

Expand All @@ -143,13 +145,17 @@ const renderDocument = handlebars.compile(templateStr)
const permissionKeys = _.keys(PERMISSIONS)
const permissions = permissionKeys.map((key) => ({
...PERMISSIONS[key],
_meta: {
...PERMISSIONS[key]._meta,
meta: {
...PERMISSIONS[key].meta,
key,
}
}))
const groupsObj = _.groupBy(permissions, '_meta.group')
const groups = _.toPairs(groupsObj).map(([title, permissions]) => ({ title, permissions }))
const groupsObj = _.groupBy(permissions, 'meta.group')
const groups = _.toPairs(groupsObj).map(([title, permissions]) => ({
title,
permissions,
anchor: `section-${title.toLowerCase().replace(/\s+/g, '-')}`,
}))

groups.forEach((group) => {
group.permissions = group.permissions.map(normalizePermission)
Expand Down
98 changes: 79 additions & 19 deletions scripts/permissions-doc/template.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,53 @@
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<title>Permissions</title>
<style>
body {
padding-bottom: 100px;
}
.small-text {
font-size: 62.5%;
line-height: 120%;
}
.permission-variable {
line-height: 80%;
margin: 5px 0;
word-break: break-word;
}
.permission-title {
line-height: 120%;
margin: 5px 0;
}
.anchor-container .anchor {
float: left;
opacity: 1;
line-height: 1;
margin-left: -20px;
}
.anchor-container .anchor::before {
visibility: hidden;
font-size: 16px;
content: '🔗';
display: inline-block;
width: 20px;
}
.anchor-container:hover .anchor {
text-decoration: none;
}
.anchor-container:hover .anchor::before {
visibility: visible;
}
.badge-crossed {
opacity: 0.4;
text-decoration: line-through;
}
</style>
</head>
<body>
Expand All @@ -21,42 +64,59 @@
<p>Legend:</p>
<ul>
<li><span class="badge badge-primary">allowed Project Role</span> - users with such a <strong>Project Role</strong> are allowed to perform the action</li>
<li><span class="badge badge-warning">denied Project Role</span> - users with such a <strong>Project Role</strong> are denied to perform the action even they have some other allow roles</li>
<li><span class="badge badge-primary badge-crossed">denied Project Role</span> - users with such a <strong>Project Role</strong> are denied to perform the action even they have some other allow roles</li>
<li><span class="badge badge-success">allowed Topcoder Role</span> - users with such a <strong>Topcoder Role</strong> are allowed to perform the action</li>
<li><span class="badge badge-danger">denied Topcoder Role</span> - users with such a <strong>Topcoder Role</strong> are denied to perform the action even they have some other allow roles</li>
<li><span class="badge badge-success badge-crossed">denied Topcoder Role</span> - users with such a <strong>Topcoder Role</strong> are denied to perform the action even they have some other allow roles</li>
</ul>
</div>

{{#each groups}}
<div class="row">
<div class="col pt-5 pb-2">
<h2>{{title}}</h2>
<h2 class="anchor-container">
<a href="#{{anchor}}" name="{{anchor}}" class="anchor"></a>{{title}}
</h2>
</div>
</div>
{{#each permissions}}
<div class="row border-top">
<div class="col py-2">
{{_meta.title}}
<div><small><code>{{_meta.key}}</code></small></div>
<div class="text-black-50 small-text">{{_meta.description}}</div>
<div class="permission-title anchor-container">
<a href="#{{meta.key}}" name="{{meta.key}}" class="anchor"></a>{{meta.title}}
</div>
<div class="permission-variable"><small><code>{{meta.key}}</code></small></div>
<div class="text-black-50 small-text">{{meta.description}}</div>
</div>
<div class="col-9 py-2">
{{#each allowRule.projectRoles}}
<span class="badge badge-primary" title="Allowed Project Role">{{this}}</span>
{{/each}}
{{#each denyRule.projectRoles}}
<span class="badge badge-warning" title="Denied Project Role">{{this}}</span>
{{/each}}
{{#each allowRule.topcoderRoles}}
<span class="badge badge-success" title="Allowed Topcoder Role">{{this}}</span>
{{/each}}
{{#each denyRule.topcoderRoles}}
<span class="badge badge-danger" title="Denied Topcoder Role">{{this}}</span>
{{/each}}
<div>
{{#if (istrue allowRule.projectRoles)}}
<span class="badge badge-primary" title="Allowed">Any Project Member</span>
{{else}}
{{#each allowRule.projectRoles}}
<span class="badge badge-primary" title="Allowed Project Role">{{this}}</span>
{{/each}}
{{/if}}
{{#each denyRule.projectRoles}}
<span class="badge badge-primary badge-crossed" title="Denied Project Role">{{this}}</span>
{{/each}}
</div>

<div>
{{#if (istrue allowRule.topcoderRoles)}}
<span class="badge badge-success" title="Allowed">Any Logged-in User</span>
{{else}}
{{#each allowRule.topcoderRoles}}
<span class="badge badge-success" title="Allowed Topcoder Role">{{this}}</span>
{{/each}}
{{/if}}
{{#each denyRule.topcoderRoles}}
<span class="badge badge-success badge-crossed" title="Denied Topcoder Role">{{this}}</span>
{{/each}}
</div>
</div>
</div>
{{/each}}
{{/each}}
</div>
</body>
</html>
</html>
2 changes: 1 addition & 1 deletion src/components/AssetsLibrary/FilesGridView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
PROJECT_FEED_TYPE_MESSAGES
} from '../../config/constants'
import { hasPermission } from '../../helpers/permissions'
import PERMISSIONS from '../../config/permissions'
import { PERMISSIONS } from '../../config/permissions'

let selectedLink
let clearing = false
Expand Down
2 changes: 1 addition & 1 deletion src/components/AssetsLibrary/LinksGridView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
} from '../../config/constants'
import FilterColHeader from './FilterColHeader'
import { hasPermission } from '../../helpers/permissions'
import PERMISSIONS from '../../config/permissions'
import { PERMISSIONS } from '../../config/permissions'

let selectedLink
let clearing = false
Expand Down
6 changes: 4 additions & 2 deletions src/components/Feed/Feed.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import RichTextArea from '../RichTextArea/RichTextArea'
import NotificationsReader from '../../components/NotificationsReader'
import IconButton from '../IconButton/IconButton'

import { EVENT_TYPE, PROJECT_FEED_TYPE_MESSAGES, PROJECT_ROLE_CUSTOMER } from '../../config/constants'
import { EVENT_TYPE, PROJECT_FEED_TYPE_MESSAGES } from '../../config/constants'

import XMarkIcon from '../../assets/icons/x-mark.svg'
import FullscreenIcon from '../../assets/icons/ui-fullscreen.svg'
Expand All @@ -20,6 +20,8 @@ import CloseIcon from 'appirio-tech-react-components/components/Icons/CloseIcon'
import MoreIcon from '../../assets/icons/more.svg'

import './Feed.scss'
import { hasPermission } from '../../helpers/permissions'
import { PERMISSIONS } from '../../config/permissions'

class Feed extends React.Component {
constructor(props) {
Expand Down Expand Up @@ -95,7 +97,7 @@ class Feed extends React.Component {
}

filterProjectMembers(projectMembers, isPrivate) {
return isPrivate ? _.pickBy(projectMembers, pm => pm.role !== PROJECT_ROLE_CUSTOMER) : projectMembers
return isPrivate ? _.filter(projectMembers, member => hasPermission(PERMISSIONS.ACCESS_PRIVATE_POST, { user: member })) : projectMembers
}

render() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ const IncompleteUserProfileDialog = ({
IncompleteUserProfileDialog.propTypes = {
profileSettings: PT.object.isRequired,
saveProfileSettings: PT.func.isRequired,
isTopcoderUser: PT.bool.isRequired,
user: PT.object.isRequired,
onCloseDialog: PT.func.isRequired,
title: PT.string.isRequired,
Expand Down
11 changes: 5 additions & 6 deletions src/components/IncompleteUserProfile/IncompleteUserProfile.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@
*/
import React from 'react'
import PT from 'prop-types'
import { PROFILE_FIELDS_CONFIG } from '../../config/constants'
import ProfileSettingsForm from '../../routes/settings/routes/profile/components/ProfileSettingsForm'
import { getDefaultTopcoderRole } from '../../helpers/permissions'
import { getDefaultTopcoderRole, hasPermission } from '../../helpers/permissions'
import { timezones } from 'appirio-tech-react-components/constants/timezones'
import { getUserProfileFieldsConfig } from '../../helpers/tcHelpers'
import { PERMISSIONS } from '../../config/permissions'

const IncompleteUserProfile = ({
profileSettings,
saveProfileSettings,
isTopcoderUser,
user,
...restProps
}) => {
const fieldsConfig = isTopcoderUser ? PROFILE_FIELDS_CONFIG.TOPCODER : PROFILE_FIELDS_CONFIG.CUSTOMER
const fieldsConfig = getUserProfileFieldsConfig()
// never show avatar
delete fieldsConfig.avatar
// config the form to only show required fields which doesn't have the value yet
Expand All @@ -40,7 +40,7 @@ const IncompleteUserProfile = ({
console.log('Auto-detected timezone', prefilledProfileSettings.settings.timeZone)
}

if (isTopcoderUser) {
if (!hasPermission(PERMISSIONS.VIEW_USER_PROFILE_AS_CUSTOMER)) {
// We don't ask Topcoder User for "Company Name" and "Title"
// but server requires them, so if they are not yet defined, we set them automatically
if (!profileSettings.settings.companyName) {
Expand Down Expand Up @@ -71,7 +71,6 @@ const IncompleteUserProfile = ({
IncompleteUserProfile.propTypes = {
profileSettings: PT.object.isRequired,
saveProfileSettings: PT.func.isRequired,
isTopcoderUser: PT.bool.isRequired,
user: PT.object.isRequired,
}

Expand Down
6 changes: 3 additions & 3 deletions src/components/Posts/PostsContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class PostsContainer extends React.Component {
* which is accepted by Feed component
*/
prepareFeed() {
const { topic, error, allMembers, currentMemberRole, tag } = this.props
const { topic, error, allMembers, tag } = this.props
const { showAll } = this.state

if (!topic || !tag) {
Expand All @@ -73,7 +73,7 @@ class PostsContainer extends React.Component {
// Github issue##623, allow comments on all posts (including system posts)
allowComments: true,
user: isSystemUser(topic.userId) ? SYSTEM_USER : allMembers[topic.userId],
unread: !topic.read && !!currentMemberRole,
unread: !topic.read,
totalComments: topic.totalPosts,
comments: [],
}
Expand All @@ -88,7 +88,7 @@ class PostsContainer extends React.Component {
isSavingComment: p.isSavingComment,
isDeletingComment: p.isDeletingComment,
error: p.error,
unread: !p.read && !!currentMemberRole,
unread: !p.read,
date,
createdAt: p.date,
edited,
Expand Down
Loading

0 comments on commit 193a054

Please sign in to comment.