Skip to content
This repository has been archived by the owner on May 19, 2020. It is now read-only.

Commit

Permalink
Merge pull request #438 from 18F/ms-limit_current_user_actions
Browse files Browse the repository at this point in the history
Limit current user actions
  • Loading branch information
jcscottiii authored Jul 12, 2016
2 parents b560a9f + 452f555 commit e78c5a8
Show file tree
Hide file tree
Showing 13 changed files with 301 additions and 21 deletions.
8 changes: 8 additions & 0 deletions static_src/actions/user_actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,5 +120,13 @@ export default {
type: userActionTypes.USER_CHANGE_VIEWED_TYPE,
userType
});
},

receivedCurrentUserInfo(user) {
AppDispatcher.handleServerAction({
type: userActionTypes.CURRENT_USER_INFO_RECEIVED,
currentUser: user
});
}

};
31 changes: 22 additions & 9 deletions static_src/components/user_list.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,19 @@ export default class UserList extends React.Component {
this.props = props;
this.state = {
users: props.initialUsers,
userType: props.initialUserType
userType: props.initialUserType,
currentUserAccess: props.initialCurrentUserAccess
};
this.styler = createStyler(baseStyles, navStyles);
this._handleDelete = this._handleDelete.bind(this);
}

componentWillReceiveProps(nextProps) {
this.setState({ users: nextProps.initialUsers,
userType: nextProps.initialUserType });
this.setState({
users: nextProps.initialUsers,
userType: nextProps.initialUserType,
currentUserAccess: nextProps.initialCurrentUserAccess
});
}

_handleDelete(userGuid, ev) {
Expand Down Expand Up @@ -90,15 +94,21 @@ export default class UserList extends React.Component {
{ this.state.users.map((user) => {
let actions;
if (this.props.onRemove) {
actions = (
<td column="Actions">
let button = <span></span>;
if (this.state.currentUserAccess) {
button = (
<Button
classes={[this.styler('usa-button-secondary')]}
onClickHandler={ this._handleDelete.bind(this, user.guid) }
label="delete">
<span>Remove User From Org</span>
</Button>
</td>
);
}
actions = (
<td column="Actions" style={{ width: '14rem' }}>
{ button }
</td>
);
}
return ([
Expand All @@ -107,6 +117,7 @@ export default class UserList extends React.Component {
<td column="Permissions" key={ `${user.guid}-role` }>
<UserRoleListControl
initialUserType={ this.state.userType }
initialCurrentUserAccess={ this.state.currentUserAccess }
onAddPermissions={ this.props.onAddPermissions }
onRemovePermissions={ this.props.onRemovePermissions }
user={ user }
Expand All @@ -115,8 +126,8 @@ export default class UserList extends React.Component {
<td column="Date Created">{ formatDateTime(user.created_at) }</td>
{ actions }
</tr>
]);
})}
]);
})}
</tbody>
</table>
</div>
Expand All @@ -135,6 +146,7 @@ export default class UserList extends React.Component {
UserList.propTypes = {
initialUsers: React.PropTypes.array,
initialUserType: React.PropTypes.string,
initialCurrentUserAccess: React.PropTypes.bool,
// Set to a function when there should be a remove button.
onRemove: React.PropTypes.func,
onRemovePermissions: React.PropTypes.func,
Expand All @@ -143,5 +155,6 @@ UserList.propTypes = {

UserList.defaultProps = {
initialUsers: [],
initialUserType: 'space_users'
initialUserType: 'space_users',
initialCurrentUserAccess: false
};
11 changes: 9 additions & 2 deletions static_src/components/user_role_control.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ export default class UserRoleControl extends React.Component {
super(props);
this.props = props;
this.state = {
checked: props.initialValue
checked: props.initialValue,
enableControl: props.initialEnableControl
};
this._handleChange = this._handleChange.bind(this);
}

componentWillReceiveProps(nextProps) {
this.setState({checked: nextProps.initialValue});
this.setState({
checked: nextProps.initialValue,
enableControl: nextProps.initialEnableControl
});
}

_handleChange(ev) {
Expand All @@ -27,6 +31,7 @@ export default class UserRoleControl extends React.Component {
onChange={ this._handleChange }
name={ this.props.roleKey }
checked={ this.state.checked }
disabled={ !this.state.enableControl }
id={ this.props.roleKey + this.props.userId }
/>
{ this.props.roleName }
Expand All @@ -39,9 +44,11 @@ UserRoleControl.propTypes = {
roleName: React.PropTypes.string.isRequired,
roleKey: React.PropTypes.string.isRequired,
initialValue: React.PropTypes.bool,
initialEnableControl: React.PropTypes.bool,
onChange: React.PropTypes.func
};
UserRoleControl.defaultProps = {
initialValue: false,
initialEnableControl: false,
onChange: function() { }
}
11 changes: 8 additions & 3 deletions static_src/components/user_role_list_control.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ export default class UserRoleListControl extends React.Component {
this.props = props;
this.state = {
user: props.user,
userType: props.initialUserType
userType: props.initialUserType,
currentUserAccess: props.initialCurrentUserAccess
};
this._onChange = this._onChange.bind(this);
this.checkRole = this.checkRole.bind(this);
Expand All @@ -44,7 +45,8 @@ export default class UserRoleListControl extends React.Component {
componentWillReceiveProps(nextProps) {
this.setState({
user: nextProps.user,
userType: nextProps.initialUserType
userType: nextProps.initialUserType,
currentUserAccess: nextProps.initialCurrentUserAccess
});
}

Expand Down Expand Up @@ -81,6 +83,7 @@ export default class UserRoleListControl extends React.Component {
roleName={ role.label }
roleKey={ role.key }
initialValue={ this.checkRole(role.key) }
initialEnableControl={ this.state.currentUserAccess }
onChange={ this._onChange.bind(this, role.key) }
userId={ this.props.user.guid }
/>
Expand All @@ -93,11 +96,13 @@ export default class UserRoleListControl extends React.Component {
UserRoleListControl.propTypes = {
user: React.PropTypes.object.isRequired,
initialUserType: React.PropTypes.string,
initialCurrentUserAccess: React.PropTypes.bool,
onRemovePermissions: React.PropTypes.func,
onAddPermissions: React.PropTypes.func
onAddPermissions: React.PropTypes.func,
};
UserRoleListControl.defaultProps = {
initialUserType: 'space_users',
initialCurrentUserAccess: false,
onRemovePermissions: function defaultRemove() { },
onAddPermissions: function defaultAdd() { }
};
16 changes: 12 additions & 4 deletions static_src/components/users.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,19 @@ const TAB_ORG_NAME = 'org_users';
function stateSetter(currentState) {
let users = [];
const currentTab = UserStore.currentlyViewedType;
let currentUserAccess: false;

if (currentTab === TAB_SPACE_NAME) {
users = UserStore.getAllInSpace(currentState.currentSpaceGuid);
currentUserAccess = UserStore.currentUserHasSpaceRole('space_manager');
} else {
users = UserStore.getAllInOrg(currentState.currentOrgGuid);
currentUserAccess = UserStore.currentUserHasOrgRole('org_manager');
}

return {
error: UserStore.getError(),
currentUserAccess: currentUserAccess,
currentTab,
loading: UserStore.fetching,
users
Expand All @@ -41,6 +46,7 @@ export default class Users extends React.Component {
currentSpaceGuid: props.initialSpaceGuid,
currentTab: props.initialCurrentTab,
loading: UserStore.fetching,
currentUserAccess: UserStore.currentUserHasOrgRole('org_manager'),
users: (props.initialCurrentTab === TAB_ORG_NAME) ?
UserStore.getAllInOrg(props.initialOrgGuid) :
UserStore.getAllInSpace(props.initialSpaceGuid)
Expand Down Expand Up @@ -129,18 +135,20 @@ export default class Users extends React.Component {
render() {
let removeHandler;
let errorMessage;

if (this.state.currentTab === TAB_ORG_NAME) {
removeHandler = this.handleRemove;
}

let content = (<UserList
initialUsers={ this.state.users }
initialUserType= { this.state.currentTab }
initialCurrentUserAccess={ this.state.currentUserAccess }
onRemove={ removeHandler }
onAddPermissions={ this.handleAddPermissions }
onRemovePermissions={ this.handleRemovePermissions }
/>);

if (this.state.currentTab === TAB_ORG_NAME) {
removeHandler = this.handleRemove;
}

if (this.state.loading) {
content = <Loading text="Loading users" />;
}
Expand Down
4 changes: 3 additions & 1 deletion static_src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,9 @@ const userActionTypes = keymirror({
// Action when there's an error when trying to remove a user.
ERROR_REMOVE_USER: null,
// Action when the type of users being looked at changes
USER_CHANGE_VIEWED_TYPE: null
USER_CHANGE_VIEWED_TYPE: null,
// Action when current user info received from server.
CURRENT_USER_INFO_RECEIVED: null
});

const routeActionTypes = keymirror({
Expand Down
5 changes: 4 additions & 1 deletion static_src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import serviceActions from './actions/service_actions.js';
import Space from './components/space.jsx';
import SpaceList from './components/space_list.jsx';
import { trackPageView } from './util/analytics.js';
import uaaApi from './util/uaa_api.js';
import userActions from './actions/user_actions.js';

const mainEl = document.querySelector('.js-app');
Expand Down Expand Up @@ -98,7 +99,9 @@ function marketplace(orgGuid, serviceGuid, servicePlanGuid) {
}

function checkAuth() {
cfApi.getAuthStatus();
cfApi.getAuthStatus().then(() => {
uaaApi.fetchUserInfo();
});
orgActions.fetchAll();
}

Expand Down
28 changes: 28 additions & 0 deletions static_src/stores/user_store.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class UserStore extends BaseStore {
this.subscribe(() => this._registerToActions.bind(this));
this._data = new Immutable.List();
this._currentViewedType = 'space_users';
this._currentUserGuid = null;
this._error = null;
}

Expand Down Expand Up @@ -194,6 +195,15 @@ class UserStore extends BaseStore {
break;
}

case userActionTypes.CURRENT_USER_INFO_RECEIVED: {
const user = this.get(action.currentUser.user_id);
if (user) {
this._currentUserGuid = user.guid;
this.emitChange();
}
break;
}

case userActionTypes.USER_CHANGE_VIEWED_TYPE: {
if (this._currentViewedType !== action.userType) {
this._currentViewedType = action.userType;
Expand Down Expand Up @@ -240,6 +250,24 @@ class UserStore extends BaseStore {
return this._currentViewedType;
}

_hasRole(roleToCheck, userType) {
const user = this.currentUser;
if (!user) return false;
return !!(user[userType].find((role) => role === roleToCheck));
}

currentUserHasSpaceRole(role) {
return this._hasRole(role, 'space_roles');
}

currentUserHasOrgRole(role) {
return this._hasRole(role, 'organization_roles');
}

get currentUser() {
return this.get(this._currentUserGuid);
}

}

const _UserStore = new UserStore();
Expand Down
15 changes: 15 additions & 0 deletions static_src/test/unit/actions/user_actions.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -283,4 +283,19 @@ describe('userActions', function() {
expectedParams);
});
});

describe('receivedCurrentUserInfo()', function() {
it('should call a server action with user object passed in', function() {
const user = { user_id: 'zcxvkjadsuf', user_name: 'john' };
const expectedParams = {
currentUser: user
};
let spy = setupServerSpy(sandbox);

userActions.receivedCurrentUserInfo(user);

assertAction(spy, userActionTypes.CURRENT_USER_INFO_RECEIVED,
expectedParams);
});
});
});
2 changes: 1 addition & 1 deletion static_src/test/unit/stores/app_store.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ describe('AppStore', function() {

beforeEach(() => {
AppStore._data = Immutable.List();
AppStore.fetching = false;
AppStore._fetching = false;
sandbox = sinon.sandbox.create();
});

Expand Down
Loading

0 comments on commit e78c5a8

Please sign in to comment.