Added ability to set password for groups

• Added:
- ability to set password for groups
- GroupPasswordModal
- checks for if has password
- rate limiting in rack_attack
This commit is contained in:
mgabdev
2020-09-11 17:27:00 -05:00
parent 1baa123e25
commit 6d85c76c8f
13 changed files with 435 additions and 71 deletions

View File

@@ -0,0 +1,157 @@
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import { defineMessages, injectIntl } from 'react-intl'
import {
joinGroup,
checkGroupPassword,
checkGroupPasswordReset,
} from '../../actions/groups'
import ModalLayout from './modal_layout'
import Button from '../button'
import Input from '../input'
import Text from '../text'
class GroupPasswordModal extends ImmutablePureComponent {
state = {
text: '',
isError: false,
}
componentDidMount() {
const { url } = this.props
this.props.onCheckGroupPasswordReset()
}
componentDidUpdate(prevProps) {
if (this.props.group !== prevProps.group) {
this.props.onCheckGroupPasswordReset()
}
if (this.props.passwordCheckIsError && prevProps.passwordCheckIsLoading) {
this.setState({ isError: true })
}
if (this.props.passwordCheckIsSuccess) {
this.props.onClose()
}
}
componentWillUnmount() {
this.props.onCheckGroupPasswordReset()
}
handlePasswordChange = (value) => {
this.setState({
text: value,
isError: false,
})
}
handleOnClick = () => {
this.props.onCheckGroupPassword(this.props.group.get('id'), this.state.text)
}
render() {
const {
intl,
group,
onClose,
passwordCheckIsLoading,
passwordCheckIsError,
passwordCheckIsSuccess,
} = this.props
const { text, isError } = this.state
if (!group) {
//loading
return <div/>
}
const hasPassword = group.get('has_password')
const isPrivate = group.get('is_private')
const instructions = isPrivate ? 'Enter the group password and then your join request will be sent to the group admin.' : 'Enter the group password to join the group.'
return (
<ModalLayout
title={intl.formatMessage(messages.title)}
onClose={onClose}
width={360}
>
<div className={_s.d}>
<div className={[_s.d, _s.my10].join(' ')}>
{
isError &&
<Text color='error' className={[_s.pb15, _s.px15].join(' ')}>There was an error submitting the form.</Text>
}
<Input
isDisabled={passwordCheckIsLoading}
type='text'
value={text}
placeholder='•••••••••••'
id='group-password'
title='Enter group password'
onChange={this.handlePasswordChange}
/>
<Text className={[_s.my10, _s.ml15].join(' ')} size='small' color='secondary'>
{instructions}
</Text>
</div>
<Button
isDisabled={passwordCheckIsLoading}
onClick={this.handleOnClick}
icon={passwordCheckIsLoading ? 'loading' : null}
iconSize='20px'
className={[_s.aiCenter, _s.jcCenter].join(' ')}
>
<Text color='inherit' className={_s.px10}>{intl.formatMessage(messages.submit)}</Text>
</Button>
</div>
</ModalLayout>
)
}
}
const messages = defineMessages({
title: { id: 'group.password_required', defaultMessage: 'Group password required' },
submit: { id: 'report.submit', defaultMessage: 'Submit' },
})
const mapStateToProps = (state) => ({
passwordCheckIsLoading: state.getIn(['group_lists', 'passwordCheck', 'isLoading'], false),
passwordCheckIsError: state.getIn(['group_lists', 'passwordCheck', 'isError'], false),
passwordCheckIsSuccess: state.getIn(['group_lists', 'passwordCheck', 'isSuccess'], false),
})
const mapDispatchToProps = (dispatch) => ({
onCheckGroupPassword(groupId, password) {
dispatch(checkGroupPassword(groupId, password))
},
onCheckGroupPasswordReset() {
dispatch(checkGroupPasswordReset())
},
onJoinGroup(groupId) {
dispatch(joinGroup(groupId))
},
})
GroupPasswordModal.propTypes = {
group: ImmutablePropTypes.map,
intl: PropTypes.object.isRequired,
onClose: PropTypes.func.isRequired,
onError: PropTypes.func.isRequired,
onCheckGroupPassword: PropTypes.func.isRequired,
onCheckGroupPasswordReset: PropTypes.func.isRequired,
onJoinGrouponJoinGroup: PropTypes.func.isRequired,
passwordCheckIsLoading: PropTypes.bool.isRequired,
passwordCheckIsError: PropTypes.bool.isRequired,
passwordCheckIsSuccess: PropTypes.bool.isRequired,
}
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(GroupPasswordModal))

View File

@@ -19,6 +19,7 @@ import {
MODAL_EMBED,
MODAL_GROUP_CREATE,
MODAL_GROUP_DELETE,
MODAL_GROUP_PASSWORD,
MODAL_HASHTAG_TIMELINE_SETTINGS,
MODAL_HOME_TIMELINE_SETTINGS,
MODAL_HOTKEYS,
@@ -51,6 +52,7 @@ import {
GroupCreateModal,
GroupDeleteModal,
GroupMembersModal,
GroupPasswordModal,
GroupRemovedAccountsModal,
HashtagTimelineSettingsModal,
HomeTimelineSettingsModal,
@@ -84,6 +86,7 @@ MODAL_COMPONENTS[MODAL_EDIT_PROFILE] = EditProfileModal
MODAL_COMPONENTS[MODAL_EMBED] = EmbedModal
MODAL_COMPONENTS[MODAL_GROUP_CREATE] = GroupCreateModal
MODAL_COMPONENTS[MODAL_GROUP_DELETE] = GroupDeleteModal
MODAL_COMPONENTS[MODAL_GROUP_PASSWORD] = GroupPasswordModal
MODAL_COMPONENTS[MODAL_HASHTAG_TIMELINE_SETTINGS] = HashtagTimelineSettingsModal
MODAL_COMPONENTS[MODAL_HOME_TIMELINE_SETTINGS] = HomeTimelineSettingsModal
MODAL_COMPONENTS[MODAL_HOTKEYS] = HotkeysModal

View File

@@ -35,7 +35,7 @@ class GroupInfoPanel extends ImmutablePureComponent {
)
}
const isAdmin = relationships ? relationships.get('admin') : false
const isAdminOrMod = relationships ? (relationships.get('admin') || relationships.get('moderator')) : false
const groupId = !!group ? group.get('id') : ''
const slug = !!group ? !!group.get('slug') ? `g/${group.get('slug')}` : undefined : undefined
const isPrivate = !!group ? group.get('is_private') : false
@@ -129,18 +129,18 @@ class GroupInfoPanel extends ImmutablePureComponent {
<Text size='small' color='inherit' className={_s.px5}>?</Text>
</Button>
</GroupInfoPanelRow>
<Divider isSmall />
<GroupInfoPanelRow title={intl.formatMessage(messages.members)} icon='group'>
<Button
isText
color={isAdmin ? 'brand' : 'primary'}
color={isAdminOrMod ? 'brand' : 'primary'}
backgroundColor='none'
className={_s.mlAuto}
to={isAdmin ? `/groups/${groupId}/members` : undefined}
to={isAdminOrMod ? `/groups/${groupId}/members` : undefined}
>
<Text color='inherit' weight={isAdmin ? 'medium' : 'normal'} size='normal' className={isAdmin ? _s.underline_onHover : undefined}>
<Text color='inherit' weight={isAdminOrMod ? 'medium' : 'normal'} size='normal' className={isAdminOrMod ? _s.underline_onHover : undefined}>
{shortNumberFormat(group.get('member_count'))}
&nbsp;
{intl.formatMessage(messages.members)}
@@ -186,9 +186,14 @@ class GroupInfoPanel extends ImmutablePureComponent {
{
tags.map((tag) => (
<div className={[_s.mr5, _s.mb5].join(' ')}>
<Text size='small' className={[_s.bgSecondary, _s.radiusSmall, _s.px10, _s.py2, _s.lineHeight15].join(' ')}>
{tag}
</Text>
<NavLink
to={`/groups/browse/tags/${slugify(tag)}`}
className={_s.noUnderline}
>
<Text size='small' className={[_s.bgSecondary, _s.radiusSmall, _s.px10, _s.py2, _s.lineHeight15].join(' ')}>
{tag}
</Text>
</NavLink>
</div>
))
}