Mandatory Login BE and Ruby tests (#757)

* Mandatory Login BE and Ruby tests

* registration handle and remove user-system setting

* create specific paths to mandatory login changing

* BE logic not allow turn off mandatory login without registratrion

* fix github issues

* Delete config['user-system-enabled'].

* Add some tabulations.

* Create MandatoryLoginReducer.

* Replace 'user-system' to 'mandatory-login'.

* Replace user-system toggle to mandatory-login checbox.

* Add some button in the header.

* Change onChange function mandatory login name.

* Disabled checkbox when you should not change it.

* Delete consolelog and some irrelevant lines.

* Change name of mandatory login reducer.

* Change style button in install step 1

* Change style button in install step 2

* Fix loading bug in submmit button.

* Change style button in install step 5

* Change style button in install step 6

* Delete UserSystemEnabled in ticket viewer component.

* Delete UserSystemEnabled in some files.

* Delete onRetriveFail function in main view ticket page.

* Replace user-system-enabled to mandatory-login in some files.

* replace user-system-enabled to mandatory-login in install steps.

* Fix style in dashboard-[Ccreate-ticket-page and dashboard-list-article-page.

* Fix mandatory login issues

Co-authored-by: LautaroCesso <lautaro_cesso@hotmail.com>
Co-authored-by: Ivan Diaz <ivan@opensupports.com>
This commit is contained in:
Guillermo Giuliana 2020-05-12 19:22:51 -03:00 committed by GitHub
parent 923b9c47c0
commit 92a96c276b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
95 changed files with 994 additions and 1225 deletions

View File

@ -1,31 +0,0 @@
'use strict';
module.exports = {
'serverport': 3006,
'scripts': {
'src': './src/*.js',
'dest': './build/js/'
},
'images': {
'src': './src/assets/images/**/*.{jpeg,jpg,png}',
'dest': './build/images/'
},
'styles': {
'src': './src/**/*.scss',
'dest': './build/css/'
},
'fonts': {
'src': './src/scss/font_awesome/fonts/*',
'dest': './build/fonts/'
},
'sourceDir': './src/',
'buildDir': './build/'
};

View File

@ -1,11 +0,0 @@
'use strict';
var fs = require('fs');
var onlyScripts = require('./util/script-filter');
var tasks = fs.readdirSync('./gulp/tasks/').filter(onlyScripts);
process.env.NODE_PATH = './src';
tasks.forEach(function(task) {
require('./tasks/' + task);
});

View File

@ -1,14 +0,0 @@
'use strict';
var config = require('../config');
var browserSync = require('browser-sync');
var gulp = require('gulp');
gulp.task('browserSync', function() {
browserSync({
proxy: 'localhost:' + config.serverport,
startPath: '/'
});
});

View File

@ -1,78 +0,0 @@
'use strict';
var gulp = require('gulp');
var gulpif = require('gulp-if');
var gutil = require('gulp-util');
var source = require('vinyl-source-stream');
var streamify = require('gulp-streamify');
var sourcemaps = require('gulp-sourcemaps');
var rename = require('gulp-rename');
var watchify = require('watchify');
var browserify = require('browserify');
var babelify = require('babelify');
var uglify = require('gulp-uglify');
var browserSync = require('browser-sync');
var debowerify = require('debowerify');
var handleErrors = require('../util/handle-errors');
var config = require('../config');
var util = require('gulp-util');
// Based on: http://blog.avisi.nl/2014/04/25/how-to-keep-a-fast-build-with-browserify-and-reactjs/
function buildScript(file, watch) {
var bundler = browserify({
entries: [config.sourceDir + file],
debug: !global.isProd,
insertGlobalVars: {
isProd: function () {
return (global.isProd) ? "'enabled'" : "'disabled'";
},
noFixtures: function() {
return (util.env['api']) ? "'enabled'" : "'disabled'";
}
},
cache: {},
packageCache: {},
fullPaths: false
});
if ( watch ) {
bundler = watchify(bundler);
bundler.on('update', rebundle);
}
bundler.transform(babelify, {'optional': ['es7.classProperties']});
bundler.transform(debowerify);
function rebundle() {
var stream = bundler.bundle();
gutil.log('Rebundle...');
return stream.on('error', handleErrors)
.pipe(source(file))
.pipe(gulpif(global.isProd, streamify(uglify())))
.pipe(streamify(rename({
basename: 'main'
})))
.pipe(gulpif(!global.isProd, sourcemaps.write('./')))
.pipe(gulp.dest(config.scripts.dest))
.pipe(gulpif(browserSync.active, browserSync.reload({ stream: true, once: true })));
}
return rebundle();
}
gulp.task('browserify', function() {
// Only run watchify if NOT production
return buildScript('index.js', !global.isProd);
});
gulp.task('config', function() {
return gulp.src(config.sourceDir + 'config.js')
.pipe(gulp.dest(config.scripts.dest))
});

View File

@ -1,11 +0,0 @@
'use strict';
var config = require('../config');
var gulp = require('gulp');
var del = require('del');
gulp.task('clean', function(cb) {
del([config.buildDir], cb);
});

View File

@ -1,10 +0,0 @@
'use strict';
var gulp = require('gulp');
var config = require('../config');
gulp.task('copyFonts', function() {
return gulp.src(config.fonts.src)
.pipe(gulp.dest(config.fonts.dest))
});

View File

@ -1,11 +0,0 @@
'use strict';
var gulp = require('gulp');
gulp.task('copyIcons', function() {
// Copy icons from root directory to build/
return gulp.src(['./*.png', './favicon.ico'])
.pipe(gulp.dest('build/'));
});

View File

@ -1,12 +0,0 @@
'use strict';
var gulp = require('gulp');
var config = require('../config');
gulp.task('copyIndex', function() {
gulp.src(config.sourceDir + 'index.html').pipe(gulp.dest(config.buildDir));
gulp.src(config.sourceDir + 'index.php').pipe(gulp.dest(config.buildDir));
gulp.src(config.sourceDir + '.htaccess').pipe(gulp.dest(config.buildDir));
});

View File

@ -1,10 +0,0 @@
'use strict';
var gulp = require('gulp');
//var config = require('../config');
gulp.task('deploy', ['prod'], function() {
// Deploy to hosting environment
});

View File

@ -1,15 +0,0 @@
'use strict';
var gulp = require('gulp');
var runSequence = require('run-sequence');
gulp.task('dev', ['clean'], function(callback) {
callback = callback || function() {};
global.isProd = false;
// Run all tasks once
return runSequence(['sass', 'imagemin', 'browserify', 'copyFonts', 'copyIndex', 'copyIcons', 'config'], 'watch', callback);
});

View File

@ -1,17 +0,0 @@
'use strict';
var gulp = require('gulp');
var gulpif = require('gulp-if');
var imagemin = require('gulp-imagemin');
var browserSync = require('browser-sync');
var config = require('../config');
gulp.task('imagemin', function() {
// Run imagemin task on all images
return gulp.src(config.images.src)
.pipe(gulpif(global.isProd, imagemin()))
.pipe(gulp.dest(config.images.dest))
.pipe(gulpif(browserSync.active, browserSync.reload({ stream: true, once: true })));
});

View File

@ -1,15 +0,0 @@
'use strict';
var gulp = require('gulp');
var runSequence = require('run-sequence');
gulp.task('prod', ['clean'], function(callback) {
process.env.NODE_ENV = 'production';
callback = callback || function() {};
global.isProd = true;
runSequence(['sass', 'imagemin', 'browserify', 'copyFonts', 'copyIndex', 'copyIcons'], callback);
});

View File

@ -1,29 +0,0 @@
'use strict';
var gulp = require('gulp');
var sass = require('gulp-sass');
var gulpif = require('gulp-if');
var browserSync = require('browser-sync');
var autoprefixer = require('gulp-autoprefixer');
var bulkSass = require('gulp-sass-bulk-import');
var plumber = require('gulp-plumber');
var handleErrors = require('../util/handle-errors');
var config = require('../config');
gulp.task('sass', function () {
return gulp.src(config.styles.src)
.pipe(bulkSass())
.pipe(plumber())
.pipe(sass({
sourceComments: global.isProd ? 'none' : 'map',
sourceMap: 'sass',
outputStyle: global.isProd ? 'compressed' : 'nested'
}))
.on('error', handleErrors)
.pipe(autoprefixer("last 2 versions", "> 1%", "ie 8"))
.on('error', handleErrors)
.pipe(gulp.dest(config.styles.dest))
.pipe(gulpif(browserSync.active, browserSync.reload({ stream: true })));
});

View File

@ -1,44 +0,0 @@
'use strict';
var config = require('../config');
var http = require('http');
var express = require('express');
var gulp = require('gulp');
var gutil = require('gulp-util');
var morgan = require('morgan');
var proxy = require('express-http-proxy');
gulp.task('server', function() {
var server = express();
// log all requests to the console
server.use(morgan('dev'));
server.use(express.static(config.buildDir));
// Proxy php server api
server.use('/api', proxy('http://localhost:8080', {
forwardPath: function(req, res) {
return require('url').parse(req.url).path;
}
}));
// Serve index.html for all routes to leave routing up to react-router
server.all('/*', function(req, res) {
res.sendFile('index.html', { root: 'build' });
});
// Start webserver if not already running
var s = http.createServer(server);
s.on('error', function(err){
if(err.code === 'EADDRINUSE'){
gutil.log('Development server is already started at port ' + config.serverport);
}
else {
throw err;
}
});
s.listen(config.serverport);
});

View File

@ -1,10 +0,0 @@
'use strict';
var gulp = require('gulp');
//var config = require('../config');
gulp.task('test', function() {
// Run all tests
});

View File

@ -1,13 +0,0 @@
'use strict';
var gulp = require('gulp');
var config = require('../config');
gulp.task('watch', ['browserSync', 'server'], function() {
// Scripts are automatically watched by Watchify inside Browserify task
gulp.watch(config.styles.src, ['sass']);
gulp.watch(config.images.src, ['imagemin']);
gulp.watch(config.sourceDir + 'index.html', ['copyIndex']);
});

View File

@ -1,27 +0,0 @@
'use strict';
var notify = require('gulp-notify');
module.exports = function(error) {
if( !global.isProd ) {
var args = Array.prototype.slice.call(arguments);
// Send error to notification center with gulp-notify
notify.onError({
title: 'Compile Error',
message: '<%= error.message %>'
}).apply(this, args);
// Keep gulp from hanging on this task
this.emit('end');
} else {
// Log the error and stop the process
// to prevent broken code from building
console.log(error);
process.exit(1);
}
};

View File

@ -1,9 +0,0 @@
'use strict';
var path = require('path');
// Filters out non .coffee and .js files. Prevents
// accidental inclusion of possible hidden files
module.exports = function(name) {
return /(\.(js|coffee)$)/i.test(path.extname(name));
};

View File

@ -1,5 +0,0 @@
'use strict';
global.isProd = false;
require('./gulp');

View File

@ -0,0 +1,15 @@
export default {
showLoginForm() {
return {
type: 'SHOW_LOGIN_FORM',
payload: true
};
},
hideLoginForm() {
return {
type: 'HIDE_LOGIN_FORM',
payload: false
};
}
};

View File

@ -101,6 +101,7 @@ export default {
},
initSession() {
console.log('sessionStore.isLoggedIn()', sessionStore.isLoggedIn());
return {
type: 'CHECK_SESSION',
payload: new Promise((resolve, reject) => {

View File

@ -60,7 +60,15 @@ class TicketList extends React.Component {
renderFilterCheckbox() {
return <Checkbox className="ticket-list__checkbox" label={i18n("SHOW_CLOSED_TICKETS")} value={this.props.closedTicketsShown} onChange={this.props.onClosedTicketsShownChange} wrapInLabel/>
return (
<Checkbox
className="ticket-list__checkbox"
label={i18n("SHOW_CLOSED_TICKETS")}
value={this.props.closedTicketsShown}
onChange={this.props.onClosedTicketsShownChange}
wrapInLabel
/>
);
}
renderDepartmentsDropDown() {

View File

@ -635,7 +635,7 @@ class TicketViewer extends React.Component {
path: '/ticket/comment',
dataAsForm: true,
data: _.extend({
ticketNumber: this.props.ticket.ticketNumber
ticketNumber: this.props.ticket.ticketNumber,
}, formState, {private: formState.private ? 1 : 0}, TextEditor.getContentFormData(formState.content))
}).then(this.onCommentSuccess.bind(this), this.onCommentFail.bind(this));
}
@ -712,7 +712,6 @@ export default connect((store) => {
staffMembers: store.adminData.staffMembers,
staffMembersLoaded: store.adminData.staffMembersLoaded,
allowAttachments: store.config['allow-attachments'],
userSystemEnabled: store.config['user-system-enabled'],
userLevel: store.session.userLevel,
tags: store.config['tags']
};

View File

@ -50,7 +50,7 @@ class App extends React.Component {
'application': true,
'application_modal-opened': (this.props.modal.opened),
'application_full-width': (this.props.config.layout === 'full-width' && !_.includes(this.props.location.pathname, '/admin')),
'application_user-system': (this.props.config['user-system-enabled'])
'application_mandatory-login': (this.props.config['mandatory-login'])
};
return classNames(classes);
@ -99,7 +99,7 @@ class App extends React.Component {
history.push('/');
}
if(props.config['user-system-enabled'] && _.includes(props.location.pathname, '/check-ticket')) {
if(props.config['mandatory-login'] && _.includes(props.location.pathname, '/check-ticket')) {
history.push('/');
}

View File

@ -155,7 +155,7 @@ class AdminPanelMenu extends React.Component {
...customLists
])
},
this.props.config['user-system-enabled'] ? {
{
groupName: i18n('USERS'),
path: '/admin/panel/users',
icon: 'user',
@ -177,7 +177,7 @@ class AdminPanelMenu extends React.Component {
level: 1
}
])
} : null,
},
{
groupName: i18n('ARTICLES'),
path: '/admin/panel/articles',

View File

@ -17,6 +17,7 @@ import Listing from 'core-components/listing';
import Form from 'core-components/form';
import FormField from 'core-components/form-field';
import SubmitButton from 'core-components/submit-button';
import Checkbox from 'core-components/checkbox';
class AdminPanelAdvancedSettings extends React.Component {
@ -36,24 +37,33 @@ class AdminPanelAdvancedSettings extends React.Component {
}
render() {
const { config } = this.props;
return (
<div className="admin-panel-advanced-settings">
<Header title={i18n('ADVANCED_SETTINGS')} description={i18n('ADVANCED_SETTINGS_DESCRIPTION')}/>
{(this.state.messageType) ? this.renderMessage() : null}
<div className="row">
<div className="col-md-12">
<div className="col-md-6">
<div className="admin-panel-advanced-settings__user-system-enabled">
<span className="admin-panel-advanced-settings__text">
{i18n('ENABLE_USER_SYSTEM')} <InfoTooltip text={i18n('ENABLE_USER_SYSTEM_DESCRIPTION')} />
</span>
<ToggleButton className="admin-panel-advanced-settings__toggle-button" value={this.props.config['user-system-enabled']} onChange={this.onToggleButtonUserSystemChange.bind(this)}/>
</div>
<div className="col-md-6 admin-panel-advanced-settings__mandatory-login">
<Checkbox
label={i18n('ENABLE_MANDATORY_LOGIN')}
disabled={!config['registration']}
className="admin-panel-advanced-settings__mandatory-login__checkbox"
value={config['mandatory-login']}
onChange={this.onCheckboxMandatoryLoginChange.bind(this)}
wrapInLabel
/>
</div>
<div className="col-md-6">
<div className="admin-panel-advanced-settings__registration">
<span className="admin-panel-advanced-settings__text">{i18n('ENABLE_USER_REGISTRATION')}</span>
<ToggleButton className="admin-panel-advanced-settings__toggle-button" value={this.props.config['registration']} onChange={this.onToggleButtonRegistrationChange.bind(this)}/>
<Checkbox
label={i18n('ENABLE_USER_REGISTRATION')}
disabled={!config['mandatory-login']}
className="admin-panel-advanced-settings__registration__checkbox"
value={config['registration']}
onChange={this.onCheckboxRegistrationChange.bind(this)}
wrapInLabel
/>
</div>
</div>
</div>
@ -95,7 +105,9 @@ class AdminPanelAdvancedSettings extends React.Component {
renderMessage() {
return (
<Message type={this.state.messageType} title={this.state.messageTitle}>{this.state.messageContent}</Message>
<Message className="admin-panel-advanced-settings__message" type={this.state.messageType} title={this.state.messageTitle}>
{this.state.messageContent}
</Message>
);
}
@ -182,17 +194,21 @@ class AdminPanelAdvancedSettings extends React.Component {
});
}
onToggleButtonUserSystemChange() {
onCheckboxMandatoryLoginChange() {
AreYouSure.openModal(null, this.onAreYouSureUserSystemOk.bind(this), 'secure');
}
onToggleButtonRegistrationChange() {
onCheckboxRegistrationChange() {
AreYouSure.openModal(null, this.onAreYouSureRegistrationOk.bind(this), 'secure');
}
onAreYouSureUserSystemOk(password) {
return API.call({
path: this.props.config['user-system-enabled'] ? '/system/disable-user-system' : '/system/enable-user-system',
const {
config,
dispatch
} = this.props;
API.call({
path: config['mandatory-login'] ? '/system/disable-mandatory-login' : '/system/enable-mandatory-login',
data: {
password: password
}
@ -200,15 +216,19 @@ class AdminPanelAdvancedSettings extends React.Component {
this.setState({
messageType: 'success',
messageTitle: null,
messageContent: this.props.config['user-system-enabled'] ? i18n('USER_SYSTEM_DISABLED') : i18n('USER_SYSTEM_ENABLED')
messageContent: config['mandatory-login'] ? i18n('MANDATORY_LOGIN_DISABLED') : i18n('MANDATORY_LOGIN_ENABLED')
});
this.props.dispatch(ConfigActions.updateData());
dispatch(ConfigActions.updateData());
}).catch(() => this.setState({messageType: 'error', messageTitle: null, messageContent: i18n('ERROR_UPDATING_SETTINGS')}));
}
onAreYouSureRegistrationOk(password) {
return API.call({
path: this.props.config['registration'] ? '/system/disable-registration' : '/system/enable-registration',
const {
config,
dispatch
} = this.props;
API.call({
path: config['registration'] ? '/system/disable-registration' : '/system/enable-registration',
data: {
password: password
}
@ -216,9 +236,9 @@ class AdminPanelAdvancedSettings extends React.Component {
this.setState({
messageType: 'success',
messageTitle: null,
messageContent: this.props.config['registration'] ? i18n('REGISTRATION_DISABLED') : i18n('REGISTRATION_ENABLED')
messageContent: config['registration'] ? i18n('REGISTRATION_DISABLED') : i18n('REGISTRATION_ENABLED')
});
this.props.dispatch(ConfigActions.updateData());
dispatch(ConfigActions.updateData());
}).catch(() => this.setState({messageType: 'error', messageTitle: null, messageContent: i18n('ERROR_UPDATING_SETTINGS')}));
}

View File

@ -1,7 +1,7 @@
@import "../../../../scss/vars";
.admin-panel-advanced-settings {
&__user-system-enabled {
&__mandatory-login {
}
@ -9,13 +9,6 @@
}
&__toggle-button {
display: inline-block;
margin-left: 20px;
margin-top: 20px;
margin-bottom: 20px;
}
&__text {
margin-top: 30px;
margin-bottom: 20px;
@ -61,4 +54,8 @@
font-size: $font-size--md;
}
}
&__message {
margin-bottom: 20px;
}
}

View File

@ -15,12 +15,14 @@ class AdminPanelViewTicket extends React.Component {
static propTypes = {
avoidSeen: React.PropTypes.bool,
onRetrieveFail: React.PropTypes.func,
assignmentAllowed: React.PropTypes.bool
assignmentAllowed: React.PropTypes.bool,
editable: React.PropTypes.bool,
};
static defaultProps = {
avoidSeen: false,
assignmentAllowed: true
assignmentAllowed: true,
editable: true,
};
state = {
@ -75,7 +77,7 @@ class AdminPanelViewTicket extends React.Component {
onChange: this.retrieveTicket.bind(this),
assignmentAllowed: this.props.assignmentAllowed,
customResponses: this.props.customResponses,
editable: true
editable: this.props.editable,
};
}

View File

@ -105,7 +105,7 @@
}
}
&.application_user-system {
&.application_mandatory-login {
.main-layout {
background-color: white;

View File

@ -13,7 +13,6 @@ const steps = [
'LANGUAGE',
'SERVER_REQUIREMENTS',
'DATABASE_CONFIGURATION',
'USER_SYSTEM',
'SETTINGS',
'ADMIN_SETUP',
'COMPLETED'

View File

@ -17,7 +17,7 @@ class InstallStep1Language extends React.Component {
<Header title={i18n('STEP_TITLE', {title: i18n('SELECT_LANGUAGE'), current: 1, total: 7})} description={i18n('STEP_1_DESCRIPTION')}/>
<LanguageSelector {...this.getLanguageSelectorProps()} />
<div className="install-step-1__button">
<Button size="medium" type="secondary" onClick={() => history.push('/install/step-2')}>
<Button size="extra-small" type="secondary" onClick={() => history.push('/install/step-2')}>
{i18n('NEXT')}
</Button>
</div>

View File

@ -38,14 +38,14 @@ class InstallStep2Requirements extends React.Component {
</div>
<Table {...this.getTableProps()} />
<div className="install-step-2__buttons">
<div className="install-step-2__previous">
<Button size="extra-small" onClick={this.onPreviousClick.bind(this)}>{i18n('PREVIOUS')}</Button>
</div>
<div className="install-step-2__next">
<Button disabled={!this.isAllOk()} size="medium" type="secondary" onClick={() => history.push('/install/step-3')}>
<Button disabled={!this.isAllOk()} size="extra-small" type="secondary" onClick={() => history.push('/install/step-3')}>
{i18n('NEXT')}
</Button>
</div>
<div className="install-step-2__previous">
<Button size="medium" onClick={this.onPreviousClick.bind(this)}>{i18n('PREVIOUS')}</Button>
</div>
</div>
</div>
);

View File

@ -43,12 +43,16 @@
}
&__previous {
float: left;
}
&__buttons {
width: 55%;
display: inline-flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
&__next {
float: left;
position: absolute;
margin-left: 103px;
}
}

View File

@ -21,19 +21,20 @@ class InstallStep3Database extends React.Component {
};
render() {
const { loading } = this.state;
return (
<div className="install-step-3">
<Header title={i18n('STEP_TITLE', {title: i18n('DATABASE_CONFIGURATION'), current: 3, total: 7})} description={i18n('STEP_3_DESCRIPTION')}/>
{this.renderMessage()}
<Form loading={this.state.loading} onSubmit={this.onSubmit.bind(this)}>
<Form loading={loading} onSubmit={this.onSubmit.bind(this)}>
<FormField name="dbHost" label={i18n('DATABASE_HOST')} fieldProps={{size: 'large'}} required/>
<FormField name="dbPort" label={i18n('DATABASE_PORT')} fieldProps={{size: 'large'}} infoMessage={i18n('DEFAULT_PORT')}/>
<FormField name="dbName" label={i18n('DATABASE_NAME')} fieldProps={{size: 'large'}} infoMessage={i18n('LEFT_EMPTY_DATABASE')}/>
<FormField name="dbUser" label={i18n('DATABASE_USER')} fieldProps={{size: 'large'}} required/>
<FormField name="dbPassword" label={i18n('DATABASE_PASSWORD')} fieldProps={{size: 'large', password: true}}/>
<div className="install-step-3__buttons">
<SubmitButton className="install-step-3__next" size="medium" type="secondary">{i18n('NEXT')}</SubmitButton>
<Button className="install-step-3__previous" size="medium" onClick={this.onPreviousClick.bind(this)}>{i18n('PREVIOUS')}</Button>
<Button className="install-step-3__previous" disabled={loading} size="extra-small" onClick={this.onPreviousClick.bind(this)}>{i18n('PREVIOUS')}</Button>
<SubmitButton className="install-step-3__next" size="extra-small" type="secondary">{i18n('NEXT')}</SubmitButton>
</div>
</Form>
</div>

View File

@ -10,9 +10,14 @@
float: left;
}
&__buttons {
width: 55%;
display: inline-flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
&__next {
float: left;
position: absolute;
margin-left: 286px;
}
}

View File

@ -6,7 +6,6 @@ import i18n from 'lib-app/i18n';
import ConfigActions from 'actions/config-actions';
import ToggleButton from 'app-components/toggle-button';
import Button from 'core-components/button';
import Header from 'core-components/header';
import Form from 'core-components/form';
import FormField from 'core-components/form-field';
@ -16,20 +15,31 @@ class InstallStep4UserSystem extends React.Component {
state = {
form: {
'user-system-enabled': true,
'mandatory-login': true,
'registration': true
}
};
render() {
const { form } = this.state
return (
<div className="install-step-4">
<Header title={i18n('STEP_TITLE', {title: i18n('USER_SYSTEM'), current: 4, total: 7})} description={i18n('STEP_4_DESCRIPTION')}/>
<Form onSubmit={this.onSubmit.bind(this)} values={this.state.form} onChange={this.onChange.bind(this)}>
<FormField name="user-system-enabled" label={i18n('ENABLE_USER_SYSTEM')} decorator={ToggleButton}/>
<FormField name="registration" label={i18n('ENABLE_USER_REGISTRATION')} decorator={ToggleButton} fieldProps={{disabled: this.isDisabled()}}/>
<Form onSubmit={this.onSubmit.bind(this)} values={form} onChange={this.onChange.bind(this)}>
<div className="install-step-4__container">
<FormField
name="mandatory-login"
label={i18n('ENABLE_MANDATORY_LOGIN')}
decorator={ToggleButton}
fieldProps={{disabled: !form['registration']}}/>
<FormField
name="registration"
label={i18n('ENABLE_USER_REGISTRATION')}
decorator={ToggleButton}
fieldProps={{disabled: !form['mandatory-login']}}/>
</div>
<div className="install-step-4__buttons">
<SubmitButton className="install-step-4__next" size="medium" type="secondary">{i18n('NEXT')}</SubmitButton>
<SubmitButton className="install-step-4__next" size="extra-small" type="secondary">{i18n('NEXT')}</SubmitButton>
</div>
</Form>
</div>
@ -39,24 +49,20 @@ class InstallStep4UserSystem extends React.Component {
onChange(form) {
this.setState({
form: {
'user-system-enabled': form['user-system-enabled'],
'registration': form['user-system-enabled'] && form['registration']
'mandatory-login': form['mandatory-login'],
'registration': form['registration']
}
});
}
onSubmit(form) {
this.props.dispatch(ConfigActions.updateUserSystemSettings({
'user-system-enabled': form['user-system-enabled'] * 1,
'mandatory-login': form['mandatory-login'] * 1,
'registration': form['registration'] * 1
}));
history.push('/install/step-5');
}
isDisabled() {
return !this.state.form['user-system-enabled'];
}
}
export default connect((store) => {

View File

@ -1,15 +1,17 @@
@import "../../scss/vars";
.install-step-4 {
&__container {
display: inline-flex;
justify-content: space-between;
width: 70%;
}
&__previous {
margin-right: 20px;
}
&__next {
float: left;
position: absolute;
margin-left: 230px;
min-width: 70px;
}
}

View File

@ -24,11 +24,16 @@ class InstallStep5Settings extends React.Component {
};
render() {
const {
loading,
form
} = this.state;
return (
<div className="install-step-5">
<Header title={i18n('STEP_TITLE', {title: i18n('SETTINGS'), current: 5, total: 7})} description={i18n('STEP_5_DESCRIPTION')}/>
{this.renderMessage()}
<Form loading={this.state.loading} onSubmit={this.onSubmit.bind(this)} value={this.state.form} onChange={(form) => this.setState({form})}>
<Form loading={loading} onSubmit={this.onSubmit.bind(this)} value={form} onChange={(form) => this.setState({form})}>
<FormField name="title" label={i18n('TITLE')} fieldProps={{size: 'large'}} required/>
<FormField className="install-step-5__attachments-field" name="allow-attachments" label={i18n('ALLOW_FILE_ATTACHMENTS')} field="checkbox" fieldProps={{size: 'large'}}/>
<FormField name="server-email" label={i18n('EMAIL_SERVER_ADDRESS')} fieldProps={{size: 'large'}} infoMessage={i18n('EMAIL_SERVER_ADDRESS_DESCRIPTION')}/>
@ -37,13 +42,22 @@ class InstallStep5Settings extends React.Component {
<FormField name="smtp-host" label={i18n('SMTP_SERVER')} fieldProps={{size: 'large'}}/>
<FormField name="smtp-user" label={i18n('SMTP_USER')} fieldProps={{size: 'large'}}/>
<FormField name="smtp-pass" label={i18n('SMTP_PASSWORD')} fieldProps={{size: 'large', password: true}}/>
<SubmitButton className="install-step-5__test-connection" size="medium" onClick={this.onTestSMTPClick.bind(this)} disabled={this.state.loading}>
<SubmitButton className="install-step-5__test-connection" size="extra-small" onClick={this.onTestSMTPClick.bind(this)} disabled={loading}>
{i18n('TEST_SMTP_CONNECTION')}
</SubmitButton>
</div>
<div className="install-step-5__buttons">
<SubmitButton className="install-step-5__next" size="medium" type="secondary">{i18n('NEXT')}</SubmitButton>
<Button className="install-step-5__previous" size="medium" onClick={this.onPreviousClick.bind(this)}>{i18n('PREVIOUS')}</Button>
<SubmitButton className="install-step-5__next" size="extra-small" type="secondary">
{i18n('NEXT')}
</SubmitButton>
<Button
className="install-step-5__previous"
size="extra-small"
disabled={loading}
onClick={this.onPreviousClick.bind(this)}
>
{i18n('PREVIOUS')}
</Button>
</div>
</Form>
</div>
@ -103,8 +117,8 @@ class InstallStep5Settings extends React.Component {
data: _.extend({}, form, {
'url': root,
'language': this.props.language,
'user-system-enabled': this.props['user-system-enabled'],
'registration': this.props['registration']
'mandatory-login': this.props['mandatory-login'] ? 1 : 0,
'registration': this.props['registration'] ? 1 : 0
})
})
.then(() => history.push('/install/step-6'))
@ -119,7 +133,7 @@ class InstallStep5Settings extends React.Component {
export default connect((store) => {
return {
'user-system-enabled': store.config['user-system-enabled'],
'mandatory-login': store.config['mandatory-login'],
'registration': store.config['registration'],
language: store.config.language
};

View File

@ -28,7 +28,7 @@ class InstallStep6Admin extends React.Component {
<FormField name="email" validation="EMAIL" label={i18n('ADMIN_EMAIL')} fieldProps={{size: 'large'}} required/>
<FormField name="password" validation="PASSWORD" label={i18n('ADMIN_PASSWORD')} infoMessage={i18n('ADMIN_PASSWORD_DESCRIPTION')} fieldProps={{size: 'large', autoComplete: 'off'}} required/>
<div className="install-step-6__buttons">
<SubmitButton className="install-step-6__next" size="medium" type="secondary">{i18n('NEXT')}</SubmitButton>
<SubmitButton className="install-step-6__next" size="extra-small" type="secondary">{i18n('NEXT')}</SubmitButton>
</div>
</Form>
</div>

View File

@ -44,7 +44,7 @@ class DashboardCreateTicketPage extends React.Component {
let classes = {
'dashboard-create-ticket-page': true,
'dashboard-create-ticket-page_wrapped': (this.props.location.pathname === '/create-ticket'),
'col-md-10 col-md-offset-1': (!this.props.config['user-system-enabled'])
'col-md-10 col-md-offset-1': !this.props.isLogged
};
return classNames(classes);
@ -53,6 +53,7 @@ class DashboardCreateTicketPage extends React.Component {
export default connect((store) => {
return {
isLogged: store.session.logged,
config: store.config
};
})(DashboardCreateTicketPage);

View File

@ -77,7 +77,7 @@ class DashboardListArticlesPage extends React.Component {
let classes = {
'dashboard-list-articles-page': true,
'dashboard-list-articles-page_wrapped': (this.props.location.pathname == '/dashboard/articles'),
'col-md-10 col-md-offset-1': (!this.props.config['user-system-enabled'])
'col-md-10 col-md-offset-1': !this.props.isLogged
};
return classNames(classes);
@ -125,6 +125,7 @@ class DashboardListArticlesPage extends React.Component {
export default connect((store) => {
return {
isLogged: store.session.logged,
config: store.config,
topics: store.articles.topics,
loading: store.articles.loading

View File

@ -52,7 +52,6 @@ class MainCheckTicketPage extends React.Component {
getClass() {
let classes = {
'main-check-ticket-page': true,
'col-md-10 col-md-offset-1': (!this.props.config['user-system-enabled'])
};
return classNames(classes);
@ -98,7 +97,7 @@ class MainCheckTicketPage extends React.Component {
}
onTicketGetSuccess(result) {
SessionStore.setItem('token', result.data.token);
SessionStore.createSession(result.data.userId, result.data.token, this.state.form.ticketNumber);
setTimeout(() => {history.push('/view-ticket/' + this.state.form.ticketNumber)}, 2000);
}
}

View File

@ -2,6 +2,7 @@
&__container {
height: 361px;
width: 324px;
}
&__inputs {
@ -17,4 +18,14 @@
&__message {
margin-top: 18px;
}
}
@media screen and (min-width: 379px) {
.login-widget__container {
min-width: 324px;
}
}
@media screen and (max-width: 409px) {
.login-widget__container {
min-width: 313px;
}
}}

View File

@ -11,13 +11,20 @@ import Message from 'core-components/message';
class MainHomePage extends React.Component {
render() {
const {
config,
loginForm
} = this.props;
return (
<div className="main-home-page">
<div className="main-home-page row">
{this.renderMessage()}
{(this.props.config['user-system-enabled']) ? this.renderLoginWidget() : null}
<div className={this.getPortalClass()}>
<MainHomePagePortal type={((this.props.config['user-system-enabled']) ? 'default' : 'complete')}/>
</div>
{(config['mandatory-login']) || loginForm.loginFormShown ? this.renderLoginWidget() : null}
{!loginForm.loginFormShown ?
<div className={this.getPortalClass()}>
<MainHomePagePortal type={((config['mandatory-login']) ? 'default' : 'complete')}/>
</div>:
null
}
</div>
);
}
@ -35,7 +42,7 @@ class MainHomePage extends React.Component {
renderLoginWidget() {
return (
<div className="col-md-4 main-home-page__login-widget">
<div className={this.getLoginWidgetClass()}>
<MainHomePageLoginWidget />
</div>
);
@ -57,11 +64,23 @@ class MainHomePage extends React.Component {
);
}
getLoginWidgetClass() {
const { config } = this.props;
let classes = {
'col-md-4': config['mandatory-login'],
'main-home-page__login-widget': config['mandatory-login'],
'main-home-page__center': !config['mandatory-login']
};
return classNames(classes);
}
getPortalClass() {
const { config } = this.props;
let classes = {
'main-home-page__portal-wrapper': true,
'col-md-8': (this.props.config['user-system-enabled'] && this.props.config['layout'] === 'boxed'),
'col-md-10 col-md-offset-1' : (!this.props.config['user-system-enabled'])
'col-md-8': (this.props.config['mandatory-login'] && this.props.config['layout'] === 'boxed'),
'col-md-10 col-md-offset-1' : (!this.props.config['mandatory-login'])
};
return classNames(classes);
@ -71,6 +90,7 @@ class MainHomePage extends React.Component {
export default connect((store) => {
return {
session: store.session,
config: store.config
config: store.config,
loginForm: store.loginForm
};
})(MainHomePage);

View File

@ -1,5 +1,10 @@
.main-home-page {
&__center {
display: flex;
justify-content: center;
}
&__message {
margin-bottom: 20px;
margin-left: 20px;

View File

@ -6,33 +6,52 @@ import ConfigActions from 'actions/config-actions';
import LanguageSelector from 'app-components/language-selector';
import Button from 'core-components/button';
import loginFormActions from '../../actions/login-form-actions';
class MainLayoutHeader extends React.Component {
render() {
return (
<div className="main-layout-header">
{(this.props.config['user-system-enabled']) ? this.renderAccessLinks() : this.renderHomeLink()}
{this.renderAccessLinks()}
<LanguageSelector {...this.getLanguageSelectorProps()} />
</div>
);
}
renderAccessLinks() {
const {
session,
config
} = this.props;
let result;
if (this.props.session.logged) {
if (session.logged) {
result = (
<div className="main-layout-header__login-links">
{i18n('WELCOME')},
<span className="main-layout-header__user-name"> {this.props.session.userName}</span>
<span className="main-layout-header__user-name"> {session.userName}</span>
</div>
);
} else {
result = (
<div className="main-layout-header__login-links">
<Button type="clean" route={{to:'/'}}>{i18n('LOG_IN')}</Button>
{(!!(this.props.config['registration'] * 1)) ? <Button type="clean" route={{to:'/signup'}}>{i18n('SIGN_UP')}</Button> : null}
<Button
type="clean"
onClick={this.onHideLoginForm.bind(this)}
route={{to:'/'}}>
{i18n('HOME')}
</Button>
{(config['registration'] * 1) ?
<Button type="clean" route={{to:'/signup'}}>
{i18n('SIGN_UP')}
</Button> :
null}
{!config['mandatory-login'] ?
<Button type="clean" onClick={this.onShowLoginForm.bind(this)} route={{to:'/'}}>
{i18n('LOG_IN')}
</Button> :
null}
</div>
);
}
@ -40,14 +59,6 @@ class MainLayoutHeader extends React.Component {
return result;
}
renderHomeLink() {
return (
<div className="main-layout-header__login-links">
<Button type="clean" route={{to:'/'}}>{i18n('HOME')}</Button>
</div>
);
}
getLanguageSelectorProps() {
return {
className: 'main-layout-header__languages',
@ -60,11 +71,19 @@ class MainLayoutHeader extends React.Component {
changeLanguage(event) {
this.props.dispatch(ConfigActions.changeLanguage(event.target.value));
}
onShowLoginForm() {
this.props.dispatch(loginFormActions.showLoginForm())
}
onHideLoginForm() {
this.props.dispatch(loginFormActions.hideLoginForm())
}
}
export default connect((store) => {
return {
session: store.session,
config: store.config
config: store.config,
};
})(MainLayoutHeader);

View File

@ -9,7 +9,7 @@ class MainLayout extends React.Component {
return (
<div className="main-layout">
<MainHeader />
<div className="main-layout--content row">
<div className="main-layout--content">
{this.props.children}
</div>
<MainFooter />

View File

@ -12,16 +12,19 @@ class MainViewTicketPage extends React.Component {
return (
<div className="main-view-ticket-page">
<Widget>
<AdminPanelViewTicket {...this.props} avoidSeen assignmentAllowed={false} onRetrieveFail={this.onRetrieveFail.bind(this)} />
<AdminPanelViewTicket
{...this.props}
avoidSeen
editable={false}
assignmentAllowed={false}
onRetrieveFail={this.onRetrieveFail.bind(this)} />
</Widget>
</div>
);
}
onRetrieveFail() {
if (!this.props.config['user-system-enabled']) {
setTimeout(() => {history.push('/check-ticket')}, 2000);
}
setTimeout(() => {history.push('/check-ticket')}, 2000);
}
}

View File

@ -6,9 +6,9 @@
-webkit-perspective: 0;
-webkit-backface-visibility: hidden;
-webkit-transform: translate3d(0,0,0);
display: inline-block;
visibility: visible;
backface-visibility: hidden;
display: inline-block;
position: absolute;
left: 0;
}

View File

@ -30,7 +30,7 @@ module.exports = [
'allowedLanguages': ['en', 'es', 'de', 'fr', 'pt', 'jp', 'ru', 'cn', 'in', 'tr'],
'supportedLanguages': ['en', 'es', 'de'],
'registration': true,
'user-system-enabled': true,
'mandatory-login': true,
'tags': [{id:1,name:'bug', color:'#eb144c'},{id: 2,name:'suggestion',color:'#ff6900'}]
}
};
@ -52,7 +52,7 @@ module.exports = [
'allowedLanguages': ['en', 'es', 'de', 'fr', 'pt', 'jp', 'ru', 'cn', 'in', 'tr'],
'supportedLanguages': ['en', 'es', 'de'],
'registration': true,
'user-system-enabled': true,
'mandatory-login': true,
'tags': [{id:1,name:'bug', color:'#eb144c'},{id: 2,name:'suggestion',color:'#ff6900'}]
}
};

View File

@ -174,7 +174,7 @@ export default {
'ALL_NOTIFICATIONS': 'All notifications',
'VERIFY_SUCCESS': 'User verified',
'VERIFY_FAILED': 'Could not verify',
'ENABLE_USER_SYSTEM': 'Use user system for customers',
'ENABLE_MANDATORY_LOGIN': 'Mandatory login for customers',
'ENABLE_USER_REGISTRATION': 'Enable user registration',
'INCLUDE_USERS_VIA_CSV': 'Include users via CSV file',
'BACKUP_DATABASE': 'Backup database',
@ -324,8 +324,8 @@ export default {
'VERIFY_FAILED_DESCRIPTION': 'The verification could not be done.',
'STATISTICS_DESCRIPTION': 'Here you can view statistics related to tickets and signups.',
'ADVANCED_SETTINGS_DESCRIPTION': 'Here you can change the advanced settings of your system. Please be careful, the changes you make can not be reversed.',
'USER_SYSTEM_DISABLED': 'User system has been disabled',
'USER_SYSTEM_ENABLED': 'User system has been enabled',
'MANDATORY_LOGIN_DISABLED': 'Mandatory login has been disabled',
'MANDATORY_LOGIN_ENABLED': 'Mandatory login has been enabled',
'REGISTRATION_DISABLED': 'Registration has been disabled',
'REGISTRATION_ENABLED': 'Registration has been enabled',
'ADD_API_KEY_DESCRIPTION': 'Insert the name and a registration api key will be generated.',

View File

@ -6,9 +6,10 @@ class SessionStore {
this.storage = LocalStorage;
}
createSession(userId, token) {
createSession(userId, token, ticketNumber = '') {
this.setItem('userId', userId);
this.setItem('token', token);
this.setItem('ticketNumber', ticketNumber);
}
getSessionData() {
@ -19,12 +20,17 @@ class SessionStore {
}
isLoggedIn() {
return !!this.getItem('userId');
return !!this.getItem('userId') && !this.getItem('ticketNumber');
}
isLoggedInWithTicket() {
return !!this.getItem('userId') && this.getItem('ticketNumber');
}
closeSession() {
this.removeItem('userId');
this.removeItem('token');
this.removeItem('ticketNumber');
this.clearRememberData();
this.clearUserData();
@ -58,7 +64,7 @@ class SessionStore {
this.setItem('layout', configs.layout);
this.setItem('title', configs.title);
this.setItem('registration', configs.registration);
this.setItem('user-system-enabled', configs['user-system-enabled']);
this.setItem('mandatory-login', configs['mandatory-login']);
this.setItem('allow-attachments', configs['allow-attachments']);
this.setItem('maintenance-mode', configs['maintenance-mode']);
this.setItem('max-size', configs['max-size']);
@ -76,7 +82,7 @@ class SessionStore {
layout: this.getItem('layout'),
title: this.getItem('title'),
registration: (this.getItem('registration') * 1),
'user-system-enabled': (this.getItem('user-system-enabled') * 1),
'mandatory-login': (this.getItem('mandatory-login') * 1),
'allow-attachments': (this.getItem('allow-attachments') * 1),
'maintenance-mode': (this.getItem('maintenance-mode') * 1),
'max-size': this.getItem('max-size'),

View File

@ -6,10 +6,12 @@ import configReducer from 'reducers/config-reducer';
import modalReducer from 'reducers/modal-reducer';
import articlesReducer from 'reducers/articles-reducer';
import adminDataReducer from 'reducers/admin-data-reducer';
import loginFormReducer from './login-form-reducer';
export default combineReducers({
session: sessionReducer,
config: configReducer,
loginForm: loginFormReducer,
modal: modalReducer,
articles: articlesReducer,
adminData: adminDataReducer,

View File

@ -46,7 +46,7 @@ class ConfigReducer extends Reducer {
return _.extend({}, state, payload.data, {
language: currentLanguage || payload.data.language || 'en',
registration: !!(payload.data.registration * 1),
'user-system-enabled': !!(payload.data['user-system-enabled']* 1),
'mandatory-login': !!(payload.data['mandatory-login']* 1),
'allow-attachments': !!(payload.data['allow-attachments']* 1),
'maintenance-mode': !!(payload.data['maintenance-mode']* 1),
departments: payload.data.departments && payload.data.departments.map(department => _.extend({}, department, {private: department.private * 1})),
@ -56,7 +56,7 @@ class ConfigReducer extends Reducer {
onUserSystemSettingsChange(state, payload) {
return _.extend({}, state, {
'user-system-enabled': !!(payload['user-system-enabled'] * 1),
'mandatory-login': !!(payload['mandatory-login'] * 1),
'registration': !!(payload['registration'] * 1)
});
}

View File

@ -0,0 +1,34 @@
import _ from 'lodash';
import Reducer from 'reducers/reducer';
class loginFormReducer extends Reducer {
getInitialState() {
return {
loginFormShown: false,
};
}
getTypeHandlers() {
return {
'SHOW_LOGIN_FORM': this.showLoginForm,
'HIDE_LOGIN_FORM': this.hideLoginForm,
};
}
showLoginForm(state, payload) {
return _.extend({}, state, {
loginFormShown: payload
});
}
hideLoginForm(state, payload) {
return _.extend({}, state, {
loginFormShown: payload
});
}
}
export default loginFormReducer.getInstance();

View File

@ -25,7 +25,7 @@ class GetAllArticlesController extends Controller {
public function validations() {
return [
'permission' => (Controller::isUserSystemEnabled()) ? 'user' : 'any',
'permission' => (Controller::isLoginMandatory()) ? 'user' : 'any',
'requestData' => []
];
}

View File

@ -27,8 +27,8 @@ $systemControllerGroup->addController(new DeleteAllUsersController);
$systemControllerGroup->addController(new BackupDatabaseController);
$systemControllerGroup->addController(new DownloadController);
$systemControllerGroup->addController(new CSVImportController);
$systemControllerGroup->addController(new DisableUserSystemController);
$systemControllerGroup->addController(new EnableUserSystemController);
$systemControllerGroup->addController(new EnableMandatoryLoginController);
$systemControllerGroup->addController(new DisableMandatoryLoginController);
$systemControllerGroup->addController(new TestSMTPController);
$systemControllerGroup->addController(new TestIMAPController);
$systemControllerGroup->addController(new EmailPollingController);

View File

@ -0,0 +1,54 @@
<?php
/**
* @api {post} /system/disable-mandatory-login Disable mandatory Login
* @apiVersion 4.6.1
*
* @apiName Disable Mandatory Login
*
* @apiGroup System
*
* @apiDescription This path disables the mandatory Login.
*
* @apiPermission staff3
*
* @apiParam {String} password The password of the current staff.
*
* @apiUse NO_PERMISSION
* @apiUse INVALID_PASSWORD
* @apiUse REGISTRATION_IS_DESACTIVATED
*
* @apiSuccess {Object} data Empty object
*
*/
class DisableMandatoryLoginController extends Controller {
const PATH = '/disable-mandatory-login';
const METHOD = 'POST';
public function validations() {
return [
'permission' => 'staff_3',
'requestData' => []
];
}
public function handler() {
$password = Controller::request('password');
if(!Setting::getSetting('registration')->getValue()) {
throw new Exception(ERRORS::REGISTRATION_IS_DESACTIVATED);
}
if(!Hashing::verifyPassword($password, Controller::getLoggedUser()->password)) {
throw new RequestException(ERRORS::INVALID_PASSWORD);
}
$mandatoryLoginRow = Setting::getSetting('mandatory-login');
$mandatoryLoginRow->value = 0;
$mandatoryLoginRow->store();
Response::respondSuccess();
}
}

View File

@ -16,6 +16,7 @@
*
* @apiUse NO_PERMISSION
* @apiUse INVALID_PASSWORD
* @apiUse MANDATORY_LOGIN_IS_DESACTIVATED
*
* @apiSuccess {Object} data Empty object
*
@ -39,7 +40,9 @@ class DisableRegistrationController extends Controller {
throw new RequestException(ERRORS::INVALID_PASSWORD);
return;
}
if(!Setting::getSetting('mandatory-login')->getValue()) {
throw new Exception(ERRORS::MANDATORY_LOGIN_IS_DESACTIVATED);
}
$registrationRow = Setting::getSetting('registration');
$registrationRow->value = false;

View File

@ -1,82 +0,0 @@
<?php
/**
* @api {post} /system/disable-user-system Disable user system
* @apiVersion 4.6.1
*
* @apiName Disable user system
*
* @apiGroup System
*
* @apiDescription This path disables the user system.
*
* @apiPermission staff3
*
* @apiParam {String} password The password of the current staff.
*
* @apiUse NO_PERMISSION
* @apiUse INVALID_PASSWORD
* @apiUse SYSTEM_USER_IS_ALREADY_DISABLED
*
* @apiSuccess {Object} data Empty object
*
*/
class DisableUserSystemController extends Controller {
const PATH = '/disable-user-system';
const METHOD = 'POST';
public function validations() {
return [
'permission' => 'staff_3',
'requestData' => []
];
}
public function handler() {
$password = Controller::request('password');
if(!Hashing::verifyPassword($password, Controller::getLoggedUser()->password)) {
throw new RequestException(ERRORS::INVALID_PASSWORD);
}
if(!Controller::isUserSystemEnabled()) {
throw new RequestException(ERRORS::SYSTEM_USER_IS_ALREADY_DISABLED);
}
$userSystemEnabled = Setting::getSetting('user-system-enabled');
$userSystemEnabled->value = 0 ;
$userSystemEnabled->store();
$userList = User::getAll();
foreach($userList as $user) {
$ticketNumberList = '';
foreach($user->sharedTicketList as $ticket) {
$ticket->authorEmail = $user->email;
$ticket->authorName = $user->name;
$ticket->author = null;
$ticketNumberList .= $ticket->ticketNumber . ' - ' . $ticket->title . '<br />';
$ticket->store();
}
$mailSender = MailSender::getInstance();
$mailSender->setTemplate(MailTemplate::USER_SYSTEM_DISABLED, [
'to' => $user->email,
'name' => $user->name,
'tickets' => $ticketNumberList,
'url' => Setting::getSetting('url')->getValue()
]);
$mailSender->send();
$user->delete();
}
Response::respondSuccess();
}
}

View File

@ -57,7 +57,7 @@ class DownloadController extends Controller {
}
break;
case FileManager::PERMISSION_ARTICLE:
if(Controller::isUserSystemEnabled() && !$session->sessionExists()) {
if(!$session->sessionExists()) {
return Response::respond403();
}
break;

View File

@ -20,6 +20,8 @@ class EmailPollingController extends Controller {
}
public function handler() {
throw new RequestException(ERRORS::NO_PERMISSION);
$commentController = new CommentController();
$createController = new CreateController();
$defaultLanguage = Setting::getSetting('language')->getValue();
@ -29,8 +31,6 @@ class EmailPollingController extends Controller {
if(Controller::request('token') !== Setting::getSetting('imap-token')->getValue())
throw new RequestException(ERRORS::INVALID_TOKEN);
if(Controller::isUserSystemEnabled())
throw new RequestException(ERRORS::USER_SYSTEM_ENABLED);
$this->mailbox = new \PhpImap\Mailbox(
Setting::getSetting('imap-host')->getValue(),

View File

@ -0,0 +1,50 @@
<?php
/**
* @api {post} /system/enable-mandatory-login Enable mandatory Login
* @apiVersion 4.6.1
*
* @apiName Enable Mandatory Login
*
* @apiGroup System
*
* @apiDescription This path enable the mandatory Login.
*
* @apiPermission staff3
*
* @apiParam {String} password The password of the current staff.
*
* @apiUse NO_PERMISSION
* @apiUse INVALID_PASSWORD
*
* @apiSuccess {Object} data Empty object
*
*/
class EnableMandatoryLoginController extends Controller {
const PATH = '/enable-mandatory-login';
const METHOD = 'POST';
public function validations() {
return [
'permission' => 'staff_3',
'requestData' => []
];
}
public function handler() {
$password = Controller::request('password');
if(!Hashing::verifyPassword($password, Controller::getLoggedUser()->password)) {
throw new RequestException(ERRORS::INVALID_PASSWORD);
return;
}
$mandatoryLoginRow = Setting::getSetting('mandatory-login');
$mandatoryLoginRow->value = 1;
$mandatoryLoginRow->store();
Response::respondSuccess();
}
}

View File

@ -1,104 +0,0 @@
<?php
/**
* @api {post} /system/enable-user-system Enable user system
* @apiVersion 4.6.1
*
* @apiName Enable user system
*
* @apiGroup System
*
* @apiDescription This path enables the user system.
*
* @apiPermission staff3
*
* @apiParam {String} password The password of the current staff.
*
* @apiUse NO_PERMISSION
* @apiUse INVALID_PASSWORD
* @apiUse SYSTEM_USER_IS_ALREADY_ENABLED
*
* @apiSuccess {Object} data Empty object
*
*/
class EnableUserSystemController extends Controller {
const PATH = '/enable-user-system';
const METHOD = 'POST';
public function validations() {
return [
'permission' => 'staff_3',
'requestData' => []
];
}
public function handler() {
$password = Controller::request('password');
if(!Hashing::verifyPassword($password, Controller::getLoggedUser()->password)) {
throw new RequestException(ERRORS::INVALID_PASSWORD);
}
if(Controller::isUserSystemEnabled()) {
throw new RequestException(ERRORS::SYSTEM_USER_IS_ALREADY_ENABLED);
}
$userSystemEnabled = Setting::getSetting('user-system-enabled');
$userSystemEnabled->value = 1 ;
$userSystemEnabled->store();
$ticketList = Ticket::getAll();
foreach($ticketList as $ticket) {
if($ticket->authorStaff) {
continue;
}
$userInstance = User::getDataStore($ticket->authorEmail, 'email');
if($userInstance->isNull()) {
$userInstance = $this->createUser($ticket->authorEmail, $ticket->authorName);
}
$userInstance->tickets = $userInstance->tickets + 1;
$userInstance->sharedTicketList->add($ticket);
$userInstance->store();
$ticket->author = $userInstance;
$ticket->authorName = null;
$ticket->authorEmail = null;
$ticket->store();
}
Response::respondSuccess();
}
public function createUser($email,$name) {
$userInstance = new User();
$password = Hashing::generateRandomToken();
$userInstance->setProperties([
'name' => $name,
'signupDate' => Date::getCurrentDate(),
'tickets' => 0,
'email' => $email,
'password' => Hashing::hashPassword($password),
'verificationToken' => null
]);
$userInstance->store();
$mailSender = MailSender::getInstance();
$mailSender->setTemplate(MailTemplate::USER_SYSTEM_ENABLED, [
'to' => $email,
'name' => $name,
'password' => $password,
'url' => Setting::getSetting('url')->getValue(),
]);
$mailSender->send();
return $userInstance;
}
}

View File

@ -57,7 +57,8 @@ class GetSettingsController extends Controller {
'allowedLanguages' => Language::getAllowedLanguages(),
'session-prefix' => Setting::getSetting('session-prefix')->getValue(),
'mail-template-header-image' => Setting::getSetting('mail-template-header-image')->getValue(),
'tags' => Tag::getAll()->toArray()
'tags' => Tag::getAll()->toArray(),
'mandatory-login' => Setting::getSetting('mandatory-login')->getValue(),
];
} else {
$settingsList = [
@ -73,9 +74,9 @@ class GetSettingsController extends Controller {
'departments' => Controller::isStaffLogged() ? Department::getAllDepartmentNames() : Department::getPublicDepartmentNames(),
'supportedLanguages' => Language::getSupportedLanguages(),
'allowedLanguages' => Language::getAllowedLanguages(),
'user-system-enabled' => intval(Setting::getSetting('user-system-enabled')->getValue()),
'session-prefix' => Setting::getSetting('session-prefix')->getValue(),
'tags' => Tag::getAll()->toArray()
'tags' => Tag::getAll()->toArray(),
'mandatory-login' => Setting::getSetting('mandatory-login')->getValue(),
];
}
}

View File

@ -16,7 +16,6 @@ DataValidator::with('CustomValidations', true);
* @apiPermission any
*
* @apiParam {String} language Indicates the default language of the system.
* @apiParam {String} user-system-enabled Indicates if the user system should be enabled.
* @apiParam {String} registration Indicates if the registration should be enabled.
* @apiParam {String} server-email Email from where automated emails will be sent.
* @apiParam {String} smtp-host SMTP Server address.
@ -26,6 +25,7 @@ DataValidator::with('CustomValidations', true);
* @apiParam {String} allow-attachments Indicates if files can be attached to tickets and comments.
* @apiParam {String} title Title of the support center
* @apiParam {String} url Url of the frontend client.
* @apiParam {Boolean} mandatory-login Indicates if the login is mandatory.
*
* @apiUse INVALID_LANGUAGE
* @apiUse INIT_SETTINGS_DONE
@ -85,13 +85,13 @@ class InitSettingsController extends Controller {
'title' => Controller::request('title') ? Controller::request('title') : 'Support Center',
'url' => Controller::request('url') ? Controller::request('url') : ('http://' . $_SERVER['HTTP_HOST']),
'registration' => !!Controller::request('registration'),
'user-system-enabled' => !!Controller::request('user-system-enabled'),
'last-stat-day' => date('YmdHi', strtotime(' -12 day ')),
'ticket-gap' => Hashing::generateRandomPrime(100000, 999999),
'ticket-first-number' => Hashing::generateRandomNumber(100000, 999999),
'session-prefix' => 'opensupports-'.Hashing::generateRandomToken().'_',
'mail-template-header-image' => 'https://s3.amazonaws.com/opensupports/logo.png',
'imap-token' => '',
'mandatory-login' => !!Controller::request('mandatory-login'),
]);
}

View File

@ -54,20 +54,23 @@ class CheckTicketController extends Controller {
}
public function handler() {
if (Controller::isUserSystemEnabled() || Controller::isStaffLogged()) {
if (Controller::isLoginMandatory()) {
throw new RequestException(ERRORS::NO_PERMISSION);
}
$email = Controller::request('email');
$ticket = Ticket::getByTicketNumber(Controller::request('ticketNumber'));
$ticketNumber = Controller::request('ticketNumber');
$ticket = Ticket::getByTicketNumber($ticketNumber);
if($ticket->authorEmail === $email) {
$session = Session::getInstance();
$session->createTicketSession($ticket->ticketNumber);
$user = User::getUser($email, 'email');
$session->createSession($user->id, false, $ticketNumber);
Response::respondSuccess([
'token' => $session->getToken(),
'ticketNumber' => $ticket->ticketNumber
'userId' => $session->getUserId(),
'ticketNumber' => $session->getTicketNumber()
]);
} else {
throw new RequestException(ERRORS::NO_PERMISSION);

View File

@ -32,43 +32,22 @@ class CloseController extends Controller {
public function validations() {
$session = Session::getInstance();
if (Controller::isUserSystemEnabled() || Controller::isStaffLogged()) {
return [
'permission' => 'user',
'requestData' => [
'ticketNumber' => [
'validation' => DataValidator::validTicketNumber(),
'error' => ERRORS::INVALID_TICKET
'permission' => 'user',
'requestData' => [
'ticketNumber' => [
'validation' => DataValidator::validTicketNumber(),
'error' => ERRORS::INVALID_TICKET
]
]
]
];
} else {
return [
'permission' => 'any',
'requestData' => [
'ticketNumber' => [
'validation' => DataValidator::equals($session->getTicketNumber()),
'error' => ERRORS::INVALID_TICKET
],
'csrf_token' => [
'validation' => DataValidator::equals($session->getToken()),
'error' => ERRORS::INVALID_TOKEN
]
]
];
}
}
public function handler() {
$this->ticket = Ticket::getByTicketNumber(Controller::request('ticketNumber'));
$user = Controller::getLoggedUser();
if(!Controller::isStaffLogged() && Controller::isUserSystemEnabled() &&
!$user->canManageTicket($this->ticket)){
throw new RequestException(ERRORS::NO_PERMISSION);
}
if(Controller::isStaffLogged() && (!$user->canManageTicket($this->ticket))){
if(!$user->canManageTicket($this->ticket)){
throw new RequestException(ERRORS::NO_PERMISSION);
}

View File

@ -42,53 +42,33 @@ class CommentController extends Controller {
public function validations() {
$this->session = Session::getInstance();
if (Controller::isUserSystemEnabled() || Controller::isStaffLogged()) {
return [
'permission' => 'user',
'requestData' => [
'content' => [
'validation' => DataValidator::content(),
'error' => ERRORS::INVALID_CONTENT
],
'ticketNumber' => [
'validation' => DataValidator::validTicketNumber(),
'error' => ERRORS::INVALID_TICKET
]
return [
'permission' => 'user',
'requestData' => [
'content' => [
'validation' => DataValidator::content(),
'error' => ERRORS::INVALID_CONTENT
],
'ticketNumber' => [
'validation' => DataValidator::validTicketNumber(),
'error' => ERRORS::INVALID_TICKET
]
];
} else {
return [
'permission' => 'any',
'requestData' => [
'content' => [
'validation' => DataValidator::content(),
'error' => ERRORS::INVALID_CONTENT
],
'ticketNumber' => [
'validation' => DataValidator::equals($this->session->getTicketNumber()),
'error' => ERRORS::INVALID_TICKET
],
'csrf_token' => [
'validation' => DataValidator::equals($this->session->getToken()),
'error' => ERRORS::INVALID_TOKEN
]
]
];
}
]
];
}
public function handler() {
$this->requestData();
$ticketNumber = Controller::request('ticketNumber');
$this->ticket = Ticket::getByTicketNumber($ticketNumber);
$this->content = Controller::request('content', true);
$this->user = Controller::getLoggedUser();
$ticketAuthor = $this->ticket->authorToArray();
$isAuthor = $this->session->isTicketSession() || $this->ticket->isAuthor($this->user);
$isAuthor = $this->ticket->isAuthor($this->user);
$isOwner = $this->ticket->isOwner($this->user);
$private = Controller::request('private');
if(!Controller::isStaffLogged() && Controller::isUserSystemEnabled() && !$isAuthor){
throw new RequestException(ERRORS::NO_PERMISSION);
}
if(!$this->session->isTicketSession() && !$this->user->canManageTicket($this->ticket)) {
if(!$this->user->canManageTicket($this->ticket)) {
throw new RequestException(ERRORS::NO_PERMISSION);
}
@ -97,6 +77,7 @@ class CommentController extends Controller {
if(!$isAuthor && !$private) {
$this->sendMail($ticketAuthor);
}
if($this->ticket->owner && !$isOwner) {
$this->sendMail([
'email' => $this->ticket->owner->email,
@ -110,13 +91,6 @@ class CommentController extends Controller {
Response::respondSuccess();
}
private function requestData() {
$ticketNumber = Controller::request('ticketNumber');
$this->ticket = Ticket::getByTicketNumber($ticketNumber);
$this->content = Controller::request('content', true);
$this->user = Controller::getLoggedUser();
}
private function storeComment() {
$fileUploader = FileUploader::getInstance();
$fileUploader->setPermission(FileManager::PERMISSION_TICKET, $this->ticket->ticketNumber);
@ -134,11 +108,9 @@ class CommentController extends Controller {
$this->ticket->unread = !$this->ticket->isAuthor($this->user);
$this->ticket->unreadStaff = !$this->ticket->isOwner($this->user);
$comment->authorStaff = $this->user;
} else if(Controller::isUserSystemEnabled()) {
$this->ticket->unreadStaff = true;
$comment->authorUser = $this->user;
} else {
$this->ticket->unreadStaff = true;
$comment->authorUser = $this->user;
}
$this->ticket->addEvent($comment);
@ -154,11 +126,10 @@ class CommentController extends Controller {
$url = Setting::getSetting('url')->getValue();
if(!Controller::isUserSystemEnabled() && !$isStaff) {
$url .= '/check-ticket/' . $this->ticket->ticketNumber;
$url .= '/' . $email;
if(!Controller::isLoginMandatory() && !$isStaff){
$url .= '/check-ticket/' . $this->ticket->ticketNumber;
$url .= '/' . $email;
}
$mailSender->setTemplate(MailTemplate::TICKET_RESPONDED, [
'to' => $email,
'name' => $name,

View File

@ -71,8 +71,7 @@ class CreateController extends Controller {
]
]
];
if(!Controller::isUserSystemEnabled() && !Controller::isStaffLogged()) {
if (!Controller::isLoginMandatory() && !Controller::isStaffLogged() && !Controller::isUserLogged()) {
$validations['permission'] = 'any';
$validations['requestData']['captcha'] = [
'validation' => DataValidator::captcha(APIKey::TICKET_CREATE),
@ -83,7 +82,7 @@ class CreateController extends Controller {
'error' => ERRORS::INVALID_EMAIL
];
$validations['requestData']['name'] = [
'validation' => DataValidator::notBlank()->length(2, 40),
'validation' => DataValidator::notBlank()->length(2, 55),
'error' => ERRORS::INVALID_NAME
];
}
@ -98,13 +97,22 @@ class CreateController extends Controller {
$this->language = Controller::request('language');
$this->email = Controller::request('email');
$this->name = Controller::request('name');
if(!Controller::isStaffLogged() && Department::getDataStore($this->departmentId)->private){
throw new Exception(ERRORS::INVALID_DEPARTMENT);
}
if(!Staff::getUser($this->email,'email')->isNull() || $this->isEmailInvalid()) {
throw new Exception(ERRORS::INVALID_EMAIL);
}
if(!Controller::isLoginMandatory() && !Controller::isStaffLogged() && !Controller::isUserLogged() && !User::getUser($this->email, 'email')->email){
$this->createNewUser();
}
$this->storeTicket();
if(!Controller::isUserSystemEnabled()) {
if(!Controller::isLoginMandatory() && !Controller::isUserLogged()) {
$this->sendMail();
}
@ -114,22 +122,46 @@ class CreateController extends Controller {
$this->sendMailStaff($staff->email);
}
}
Log::createLog('CREATE_TICKET', $this->ticketNumber);
Response::respondSuccess([
'ticketNumber' => $this->ticketNumber
]);
if(!Controller::isUserSystemEnabled() && !Controller::isStaffLogged()) {
$session = Session::getInstance();
$session->createTicketSession($this->ticketNumber);
}
Log::createLog('CREATE_TICKET', $this->ticketNumber);
}
private function isEmailInvalid(){
$session = Session::getInstance();
$sessionUser = User::getUser($session->getUserId() ,'id');
return ($session->sessionExists() && $sessionUser && $this->email && !($sessionUser->email == $this->email));
}
private function createNewUser() {
$signupController = new SignUpController(true);
Controller::setDataRequester(function ($key) {
switch ($key) {
case 'email':
return $this->email;
case 'password':
return Hashing::generateRandomToken();
case 'name':
return $this->name;
case 'indirectSignUp' :
return true;
}
return null;
});
$signupController->validations();
$signupController->handler();
}
private function storeTicket() {
$department = Department::getDataStore($this->departmentId);
$author = Controller::getLoggedUser();
$author = $this->getAuthor();
$ticket = new Ticket();
$fileUploader = FileUploader::getInstance();
@ -153,12 +185,9 @@ class CreateController extends Controller {
));
$ticket->setAuthor($author);
$author->sharedTicketList->add($ticket);
if(Controller::isUserSystemEnabled() || Controller::isStaffLogged()) {
$author->sharedTicketList->add($ticket);
}
if(Controller::isUserSystemEnabled() && !Controller::isStaffLogged()) {
if(!Controller::isStaffLogged()) {
$author->tickets++;
$this->email = $author->email;
@ -171,6 +200,14 @@ class CreateController extends Controller {
$this->ticketNumber = $ticket->ticketNumber;
}
private function getAuthor() {
if(Controller::getLoggedUser()->email) {
return Controller::getLoggedUser();
}else{
return User::getUser($this->email, 'email');
}
}
private function sendMail() {
$mailSender = MailSender::getInstance();

View File

@ -31,39 +31,19 @@ class EditCommentController extends Controller {
const METHOD = 'POST';
public function validations() {
if(Controller::isUserSystemEnabled()){
return [
'permission' => 'user',
'requestData' => [
'content' => [
'validation' => DataValidator::content(),
'error' => ERRORS::INVALID_CONTENT
],
'ticketNumber' => [
'validation' => DataValidator::oneOf(DataValidator::validTicketNumber(),DataValidator::nullType()),
'error' => ERRORS::INVALID_TICKET
]
return [
'permission' => 'user',
'requestData' => [
'content' => [
'validation' => DataValidator::content(),
'error' => ERRORS::INVALID_CONTENT
],
'ticketNumber' => [
'validation' => DataValidator::oneOf(DataValidator::validTicketNumber(),DataValidator::nullType()),
'error' => ERRORS::INVALID_TICKET
]
];
} else {
return [
'permission' => 'any',
'requestData' => [
'content' => [
'validation' => DataValidator::content(),
'error' => ERRORS::INVALID_CONTENT
],
'ticketNumber' => [
'validation' => DataValidator::oneOf(DataValidator::validTicketNumber(),DataValidator::nullType()),
'error' => ERRORS::INVALID_TICKET
],
'csrf_token' => [
'validation' => DataValidator::equals(Session::getInstance()->getToken()),
'error' => ERRORS::INVALID_TOKEN
]
]
];
}
]
];
}
public function handler() {
@ -74,7 +54,7 @@ class EditCommentController extends Controller {
$ticketevent = Ticketevent::getTicketEvent(Controller::request('ticketEventId'));
$ticket = Ticket::getByTicketNumber(Controller::request('ticketNumber'));
if(Controller::isUserSystemEnabled() && !Controller::isStaffLogged() && ($user->id !== $ticketevent->authorUserId && $user->id !== $ticket->authorId ) ){
if(!Controller::isStaffLogged() && ($user->id !== $ticketevent->authorUserId && $user->id !== $ticket->authorId ) ){
throw new RequestException(ERRORS::NO_PERMISSION);
}

View File

@ -30,39 +30,19 @@ class EditTitleController extends Controller {
const METHOD = 'POST';
public function validations() {
if(Controller::isUserSystemEnabled()){
return [
'permission' => 'user',
'requestData' => [
'title' => [
'validation' => DataValidator::notBlank()->length(1, 200),
'error' => ERRORS::INVALID_TITLE
],
'ticketNumber' => [
'validation' => DataValidator::validTicketNumber(),
'error' => ERRORS::INVALID_TICKET
]
return [
'permission' => 'user',
'requestData' => [
'title' => [
'validation' => DataValidator::notBlank()->length(1, 200),
'error' => ERRORS::INVALID_TITLE
],
'ticketNumber' => [
'validation' => DataValidator::validTicketNumber(),
'error' => ERRORS::INVALID_TICKET
]
];
} else {
return [
'permission' => 'any',
'requestData' => [
'title' => [
'validation' => DataValidator::notBlank()->length(1, 200),
'error' => ERRORS::INVALID_TITLE
],
'ticketNumber' => [
'validation' => DataValidator::validTicketNumber(),
'error' => ERRORS::INVALID_TICKET
],
'csrf_token' => [
'validation' => DataValidator::equals(Session::getInstance()->getToken()),
'error' => ERRORS::INVALID_TOKEN
]
]
];
}
]
];
}
public function handler() {
@ -70,7 +50,7 @@ class EditTitleController extends Controller {
$newtitle = Controller::request('title');
$ticket = Ticket::getByTicketNumber(Controller::request('ticketNumber'));
if(Controller::isUserSystemEnabled() && !$user->canManageTicket($ticket)) {
if(!$user->canManageTicket($ticket)) {
throw new RequestException(ERRORS::NO_PERMISSION);
}

View File

@ -32,43 +32,22 @@ class TicketGetController extends Controller {
public function validations() {
$session = Session::getInstance();
if (Controller::isUserSystemEnabled() || Controller::isStaffLogged()) {
return [
'permission' => 'user',
'requestData' => [
'ticketNumber' => [
'validation' => DataValidator::validTicketNumber(),
'error' => ERRORS::INVALID_TICKET
]
return [
'permission' => 'any',
'requestData' => [
'ticketNumber' => [
'validation' => DataValidator::validTicketNumber(),
'error' => ERRORS::INVALID_TICKET
]
];
} else {
return [
'permission' => 'any',
'requestData' => [
'ticketNumber' => [
'validation' => DataValidator::equals($session->getTicketNumber()),
'error' => ERRORS::INVALID_TICKET
],
'csrf_token' => [
'validation' => DataValidator::equals($session->getToken()),
'error' => ERRORS::INVALID_TOKEN
]
]
];
}
]
];
}
public function handler() {
$this->ticket = Ticket::getByTicketNumber(Controller::request('ticketNumber'));
if(Controller::isUserSystemEnabled() || Controller::isStaffLogged()) {
if ($this->shouldDenyPermission()) {
throw new RequestException(ERRORS::NO_PERMISSION);
} else {
Response::respondSuccess($this->ticket->toArray());
}
if ($this->shouldDenyPermission()) {
throw new RequestException(ERRORS::NO_PERMISSION);
} else {
Response::respondSuccess($this->ticket->toArray());
}
@ -76,8 +55,6 @@ class TicketGetController extends Controller {
private function shouldDenyPermission() {
$user = Controller::getLoggedUser();
return (!Controller::isStaffLogged() && (Controller::isUserSystemEnabled() && !$user->canManageTicket($this->ticket))) ||
(Controller::isStaffLogged() && !$user->canManageTicket($this->ticket));
return !$user->canManageTicket($this->ticket);
}
}

View File

@ -18,7 +18,6 @@ use RedBeanPHP\Facade as RedBean;
*
* @apiUse NO_PERMISSION
* @apiUse INVALID_USER
* @apiUse USER_SYSTEM_DISABLED
*
* @apiSuccess {Object} data Empty object
*
@ -43,9 +42,6 @@ class DeleteUserController extends Controller {
}
public function handler() {
if(!Controller::isUserSystemEnabled()) {
throw new RequestException(ERRORS::USER_SYSTEM_DISABLED);
}
$userId = Controller::request('userId');
$user = User::getDataStore($userId);
@ -56,7 +52,7 @@ class DeleteUserController extends Controller {
foreach($user->sharedTicketList as $ticket) {
$ticket->delete();
}
$user->delete();
Response::respondSuccess();

View File

@ -18,7 +18,6 @@ DataValidator::with('CustomValidations', true);
*
* @apiUse NO_PERMISSION
* @apiUse INVALID_USER
* @apiUse USER_SYSTEM_DISABLED
*
* @apiSuccess {Object} data Information about an user
* @apiSuccess {String} data.name Name of the user
@ -46,10 +45,7 @@ class GetUserByIdController extends Controller {
}
public function handler() {
if(!Controller::isUserSystemEnabled()) {
throw new RequestException(ERRORS::USER_SYSTEM_DISABLED);
}
$userId = Controller::request('userId');
$user = User::getDataStore($userId);
$staff = Controller::getLoggedUser();

View File

@ -21,7 +21,6 @@ use Respect\Validation\Validator as DataValidator;
* @apiUse NO_PERMISSION
* @apiUse INVALID_PAGE
* @apiUse INVALID_ORDER
* @apiUse USER_SYSTEM_DISABLED
*
* @apiSuccess {Object} data
* @apiSuccess {[User](#api-Data_Structures-ObjectUser)[]} data.users Array of users found
@ -53,9 +52,6 @@ class GetUsersController extends Controller {
}
public function handler() {
if(!Controller::isUserSystemEnabled()) {
throw new RequestException(ERRORS::USER_SYSTEM_DISABLED);
}
$userList = $this->getUserList();
$userListArray = [];

View File

@ -64,9 +64,6 @@ class InviteUserController extends Controller {
}
public function handler() {
if (!Controller::isUserSystemEnabled()) {
throw new RequestException(ERRORS::USER_SYSTEM_DISABLED);
}
$this->storeRequestData();

View File

@ -20,8 +20,6 @@ use RedBeanPHP\Facade as RedBean;
* @apiParam {Number} userId The id of the user to login.
* @apiParam {String} rememberToken Token to login automatically. It replaces the password.
*
* @apiUse USER_SYSTEM_DISABLED
* @apiUse SESSION_EXISTS
* @apiUse UNVERIFIED_USER
* @apiUse INVALID_CREDENTIALS
*
@ -50,16 +48,8 @@ class LoginController extends Controller {
}
public function handler() {
if(!Controller::isUserSystemEnabled() && !Controller::request('staff')) {
throw new RequestException(ERRORS::USER_SYSTEM_DISABLED);
}
if ($this->isAlreadyLoggedIn()) {
throw new RequestException(ERRORS::SESSION_EXISTS);
}
$this->clearOldRememberTokens();
if ($this->checkInputCredentials() || $this->checkRememberToken()) {
if($this->userInstance->verificationToken !== null) {
throw new RequestException(ERRORS::UNVERIFIED_USER);
@ -82,10 +72,6 @@ class LoginController extends Controller {
}
}
private function isAlreadyLoggedIn() {
return Session::getInstance()->sessionExists();
}
private function checkInputCredentials() {
$this->userInstance = $this->getUserByInputCredentials();

View File

@ -20,7 +20,6 @@ DataValidator::with('CustomValidations', true);
*
* @apiUse INVALID_EMAIL
* @apiUse INVALID_PASSWORD
* @apiUse USER_SYSTEM_DISABLED
* @apiUse NO_PERMISSION
*
* @apiSuccess {Object} data Empty object
@ -73,10 +72,6 @@ class RecoverPasswordController extends Controller {
throw new RequestException(ERRORS::NO_PERMISSION);
}
if(!Controller::isUserSystemEnabled() && !$recoverPassword->staff) {
throw new RequestException(ERRORS::USER_SYSTEM_DISABLED);
}
if($recoverPassword->staff) {
$this->user = Staff::getDataStore($this->email, 'email');
} else {
@ -88,7 +83,8 @@ class RecoverPasswordController extends Controller {
$recoverPassword->delete();
$this->user->setProperties([
'password' => Hashing::hashPassword($this->password)
'password' => Hashing::hashPassword($this->password),
'notRegistered' => null
]);
$this->user->store();

View File

@ -51,10 +51,6 @@ class SendRecoverPasswordController extends Controller {
public function handler() {
$this->staff = Controller::request('staff');
if(!Controller::isUserSystemEnabled() && !$this->staff) {
throw new RequestException(ERRORS::USER_SYSTEM_DISABLED);
}
$email = Controller::request('email');
if($this->staff){

View File

@ -20,12 +20,12 @@ DataValidator::with('CustomValidations', true);
* @apiParam {String} password The password of the new user.
* @apiParam {String} apiKey APIKey to sign up an user if the registration system is disabled.
* @apiParam {String} customfield_ Custom field values for this user.
* @apiParam {Boolean} indirectSignUp Indicates if the new User has been created by ticket/create
*
* @apiUse INVALID_NAME
* @apiUse INVALID_EMAIL
* @apiUse INVALID_PASSWORD
* @apiUse INVALID_CAPTCHA
* @apiUse USER_SYSTEM_DISABLED
* @apiUse USER_EXISTS
* @apiUse ALREADY_BANNED
* @apiUse NO_PERMISSION
@ -81,16 +81,13 @@ class SignUpController extends Controller {
}
public function handler() {
if(!Controller::isUserSystemEnabled()) {
throw new RequestException(ERRORS::USER_SYSTEM_DISABLED);
}
$this->storeRequestData();
$apiKey = APIKey::getDataStore(Controller::request('apiKey'), 'token');
$existentUser = User::getUser($this->userEmail, 'email');
if (!$existentUser->isNull()) {
$user = User::getUser($this->userEmail, 'email');
if (!$user->isNull() && !$user->notRegistered) {
throw new RequestException(ERRORS::USER_EXISTS);
}
$banRow = Ban::getDataStore($this->userEmail,'email');
@ -106,7 +103,7 @@ class SignUpController extends Controller {
if(!$apiKey->isNull() && $apiKey->type !== APIKey::REGISTRATION) {
throw new RequestException(ERRORS::INVALID_API_KEY_TYPE);
}
$userId = $this->createNewUserAndRetrieveId();
if(MailSender::getInstance()->isConnected()) {
@ -129,15 +126,19 @@ class SignUpController extends Controller {
}
public function createNewUserAndRetrieveId() {
$userInstance = new User();
$user = User::getUser($this->userEmail,'email');
$userInstance = ($user->isNull() ? new User() : $user );
$UserTickets = ($user->isNull() ? 0 : $user->tickets);
$userInstance->setProperties([
'name' => $this->userName,
'signupDate' => Date::getCurrentDate(),
'tickets' => 0,
'tickets' => $UserTickets,
'email' => $this->userEmail,
'password' => Hashing::hashPassword($this->userPassword),
'verificationToken' => (MailSender::getInstance()->isConnected()) ? $this->verificationToken : null,
'notRegistered' => Controller::request('indirectSignUp') ? true : null,
'xownCustomfieldvalueList' => $this->getCustomFieldValues()
]);
@ -154,6 +155,6 @@ class SignUpController extends Controller {
'verificationToken' => $this->verificationToken
]);
$mailSender->send();
if(!Controller::request('indirectSignUp')) $mailSender->send();
}
}

View File

@ -17,7 +17,6 @@ use Respect\Validation\Validator as DataValidator;
* @apiParam {String} token The validation token sent by email to the user.
*
* @apiUse INVALID_EMAIL
* @apiUse USER_SYSTEM_DISABLED
* @apiUse INVALID_TOKEN
*
* @apiSuccess {Object} data Empty object
@ -41,15 +40,11 @@ class VerifyController extends Controller{
}
public function handler() {
if(!Controller::isUserSystemEnabled()) {
throw new RequestException(ERRORS::USER_SYSTEM_DISABLED);
}
$email = Controller::request('email');
$token = Controller::request('token');
$userRow = User::getDataStore($email, 'email');
if(!$userRow) {
throw new RequestException(ERRORS::INVALID_EMAIL);
}

View File

@ -199,14 +199,6 @@
* @apiDefine INVALID_BODY
* @apiError {String} INVALID_BODY The body is invalid.
*/
/**
* @apiDefine USER_SYSTEM_ENABLED
* @apiError {String} USER_SYSTEM_ENABLED The user system is enabled.
*/
/**
* @apiDefine USER_SYSTEM_DISABLED
* @apiError {String} USER_SYSTEM_DISABLED The user system is disabled.
*/
/**
* @apiDefine SYSTEM_USER_IS_ALREADY_DISABLED
* @apiError {String} SYSTEM_USER_IS_ALREADY_DISABLED The system user is already disabled.
@ -307,6 +299,14 @@
* @apiDefine INVALID_API_KEY_TYPE
* @apiError {String} INVALID_API_KEY_TYPE Api key type is not one of the availables
*/
/**
* @apiDefine MANDATORY_LOGIN_IS_DESACTIVATED
* @apiError {String} MANDATORY_LOGIN_IS_DESACTIVATED Mandatory login is disactivated
*/
/**
* @apiDefine REGISTRATION_IS_DESACTIVATED
* @apiError {String} REGISTRATION_IS_DESACTIVATED Registration is disactivated
*/
class ERRORS {
const INVALID_CREDENTIALS = 'INVALID_CREDENTIALS';
@ -360,8 +360,6 @@ class ERRORS {
const INVALID_TEMPLATE = 'INVALID_TEMPLATE';
const INVALID_SUBJECT = 'INVALID_SUBJECT';
const INVALID_BODY = 'INVALID_BODY';
const USER_SYSTEM_ENABLED = 'USER_SYSTEM_ENABLED';
const USER_SYSTEM_DISABLED = 'USER_SYSTEM_DISABLED';
const SYSTEM_USER_IS_ALREADY_DISABLED = 'SYSTEM_USER_IS_ALREADY_DISABLED';
const SYSTEM_USER_IS_ALREADY_ENABLED = 'SYSTEM_USER_IS_ALREADY_ENABLED';
const INVALID_PERIOD = 'INVALID_PERIOD';
@ -387,4 +385,6 @@ class ERRORS {
const UNAVAILABLE_STATS = 'UNAVAILABLE_STATS';
const INVALID_COLOR = 'INVALID_COLOR';
const INVALID_API_KEY_TYPE = 'INVALID_API_KEY_TYPE';
const MANDATORY_LOGIN_IS_DESACTIVATED = 'MANDATORY_LOGIN_IS_DESACTIVATED';
const REGISTRATION_IS_DESACTIVATED = 'REGISTRATION_IS_DESACTIVATED';
}

View File

@ -71,7 +71,10 @@ abstract class Controller {
if ($session->isStaffLogged()) {
return Staff::getUser($session->getUserId());
} else {
return User::getUser($session->getUserId());
$user = User::getUser($session->getUserId());
if($session->getTicketNumber()) $user->ticketNumber = $session->getTicketNumber();
return $user;
}
}
@ -145,8 +148,8 @@ abstract class Controller {
return str_replace(array_map(function($index) { return "IMAGE_PATH_$index"; }, array_keys($imagePaths)), $imagePaths, $content);
}
public static function isUserSystemEnabled() {
return Setting::getSetting('user-system-enabled')->getValue();
public static function isLoginMandatory() {
return Setting::getSetting('mandatory-login')->getValue();
}
public static function getCustomFieldValues() {

View File

@ -96,6 +96,8 @@ abstract class DataStore {
public function __set($prop, $value) {
if (in_array($prop, static::getProps())) {
$this->properties[$prop] = $value;
} else if(property_exists($this, $prop)){
$this->{$prop} = $value;
} else {
throw new Exception("Invalid prop: $prop");
}

View File

@ -34,8 +34,6 @@ class MailTemplate extends DataStore {
'USER_EMAIL' => 'data/mail-templates/user-edit-email.html',
'PASSWORD_FORGOT' => 'data/mail-templates/user-password-forgot.html',
'USER_INVITE' => 'data/mail-templates/user-invite.html',
'USER_SYSTEM_DISABLED' => 'data/mail-templates/user-system-disabled.html',
'USER_SYSTEM_ENABLED' => 'data/mail-templates/user-system-enabled.html',
'TICKET_CREATED' => 'data/mail-templates/ticket-created.html',
'TICKET_RESPONDED' => 'data/mail-templates/ticket-responded.html',
'TICKET_CLOSED' => 'data/mail-templates/ticket-closed.html',

View File

@ -30,13 +30,9 @@ class Session {
$this->store($key, $value);
}
public function createSession($userId, $staff = false) {
public function createSession($userId, $staff = false, $ticketNumber = null) {
$this->store('userId', $userId);
$this->store('staff', $staff);
$this->store('token', Hashing::generateRandomToken());
}
public function createTicketSession($ticketNumber) {
$this->store('ticketNumber', $ticketNumber);
$this->store('token', Hashing::generateRandomToken());
}

View File

@ -9,16 +9,17 @@ use RedBeanPHP\Facade as RedBean;
* @apiParam {Number} id The id of the user.
* @apiParam {String} name The name of the user.
* @apiParam {Boolean} verified Indicates if the user has verified the email.
* @apiParam {Boolean} notRegistered Indicates if the user had logged at least one time.
* @apiParam {[CustomField](#api-Data_Structures-ObjectCustomfield)[]} customfields Indicates the values for custom fields.
*/
class User extends DataStore {
const TABLE = 'user';
public $ticketNumber = null;
public static function authenticate($userEmail, $userPassword) {
$user = User::getUser($userEmail, 'email');
return ($user && Hashing::verifyPassword($userPassword, $user->password)) ? $user : new NullDataStore();
return ($user && Hashing::verifyPassword($userPassword, $user->password) && !$user->notRegistered) ? $user : new NullDataStore();
}
public static function getProps() {
@ -31,7 +32,8 @@ class User extends DataStore {
'sharedTicketList',
'verificationToken',
'disabled',
'xownCustomfieldvalueList'
'xownCustomfieldvalueList',
'notRegistered'
];
}
@ -44,7 +46,13 @@ class User extends DataStore {
}
public function canManageTicket(Ticket $ticket){
return $ticket->isAuthor($this);
$ticketNumberInstanceValidation = true;
if($this->ticketNumber) {
$ticketNumberInstanceValidation = $this->ticketNumber == $ticket->ticketNumber;
}
return ($ticket->isAuthor($this) && $ticketNumberInstanceValidation);
}
public function toArray() {
@ -55,6 +63,7 @@ class User extends DataStore {
'verified' => !$this->verificationToken,
'disabled' => $this->disabled,
'customfields' => $this->xownCustomfieldvalueList->toArray(),
'notRegistered' => $this->notRegistered
];
}
}

View File

@ -23,6 +23,7 @@ require './user/recover-password.rb'
require './user/edit-password.rb'
require './user/edit-email.rb'
require './user/get.rb'
require './user/enable-disable.rb'
require './ticket/create.rb'
require './ticket/seen.rb'
@ -73,6 +74,6 @@ require './ticket/edit-comment.rb'
require './ticket/edit-title.rb'
require './system/custom-fields.rb'
require './ticket/get-authors.rb'
require './system/disable-user-system.rb'
require './ticket/search.rb'
require './system/mandatory-login.rb'
# require './system/get-stats.rb'

View File

@ -1,236 +0,0 @@
describe'system/disable-user-system' do
request('/user/logout')
Scripts.login($staff[:email], $staff[:password], true)
it 'should disable the user system' do
result = request('/system/disable-user-system', {
csrf_userid: $csrf_userid,
csrf_token: $csrf_token,
password:$staff[:password]
})
(result['status']).should.equal('success')
row = $database.getRow('setting', 'user-system-enabled', 'name')
(row['value']).should.equal('0')
row = $database.getRow('user', 1, 'id')
(row).should.equal(nil)
numberOftickets = $database.query("SELECT * FROM ticket WHERE author_id IS NULL AND author_email IS NOT NULL AND author_name IS NOT NULL")
(numberOftickets.num_rows).should.equal(53)
request('/user/logout')
result = request('/user/signup', {
:name => 'test name',
:email => 'steve@mail.com',
:password => 'customm'
})
(result['status']).should.equal('fail')
(result['message']).should.equal('USER_SYSTEM_DISABLED')
result = request('/user/login', {
email: @loginEmail,
password: @loginPass
})
(result['status']).should.equal('fail')
(result['message']).should.equal('USER_SYSTEM_DISABLED')
end
it 'should create a ticket without user' do
request('/user/logout')
result = request('/ticket/create', {
title: 'test ticket without user',
content: 'The north remembers',
departmentId: 1,
language: 'en',
})
(result['status']).should.equal('fail')
(result['message']).should.equal('INVALID_EMAIL')
result = request('/ticket/create', {
title: 'test ticket without user',
content: 'The north remembers',
departmentId: 1,
language: 'en',
name: 'Test Subject',
email: 'emailtest@opensupports.com'
})
(result['status']).should.equal('success')
end
it 'should be able to comment on ticket as a non-logged user' do
result = request('/ticket/create', {
title: 'Doubt about Russian language',
content: 'Stariy means old in Russian?',
departmentId: 1,
language: 'en',
name: 'Abraham Einstein',
email: 'abrahameinstein@opensupports.com'
})
(result['status']).should.equal('success')
ticketNumber = result['data']['ticketNumber']
result = request('/ticket/check', {
ticketNumber: ticketNumber,
email: 'abrahameinstein@opensupports.com',
captcha: 'valid'
})
token = result['data']['token']
(result['status']).should.equal('success');
result = request('/ticket/comment', {
content: 'I actually think it is not like that, but anyways, thanks',
ticketNumber: ticketNumber,
csrf_token: token
})
(result['status']).should.equal('success')
end
it 'should be able to assign and respond tickets' do
Scripts.login($staff[:email], $staff[:password], true);
ticket = $database.getLastRow('ticket');
result = request('/staff/assign-ticket', {
ticketNumber: ticket['ticket_number'],
csrf_userid: $csrf_userid,
csrf_token: $csrf_token,
})
(result['status']).should.equal('success')
result = request('/ticket/comment', {
ticketNumber: ticket['ticket_number'],
content: 'This is a staff response for a ticket without an user',
csrf_userid: $csrf_userid,
csrf_token: $csrf_token,
})
(result['status']).should.equal('success')
end
it 'should be able to get the latest events as admin' do
result = request('/staff/last-events', {
page: 1,
csrf_userid: $csrf_userid,
csrf_token: $csrf_token
})
(result['status']).should.equal('success')
(result['data'].size).should.equal(10)
end
it 'should be able to get system logs as admin' do
result = request('/system/get-logs', {
page: 1,
csrf_userid: $csrf_userid,
csrf_token: $csrf_token
})
(result['status']).should.equal('success')
(result['data'].size).should.equal(10)
end
it 'should be be able to create a ticket as an admin' do
result = request('/ticket/create', {
title: 'created by staff with user system disabled',
content: 'an staff created this ticket while user system disabled',
departmentId: 1,
language: 'en',
csrf_userid: $csrf_userid,
csrf_token: $csrf_token
})
(result['status']).should.equal('success')
ticket = $database.getRow('ticket', result['data']['ticketNumber'], 'ticket_number')
(ticket['author_id']).should.equal(nil)
(ticket['author_staff_id']).should.equal('1')
end
it 'should be able to create a ticket using api' do
api_key = Scripts.createAPIKey('ticketCreateKey', 'TICKET_CREATE')['data']
request('/user/logout')
result = request('/ticket/create', {
email: 'fromapi@testemail.com',
name: 'Random user',
title: 'created by api',
content: 'this ticket was created using anapi key while user system is disabled',
departmentId: 1,
language: 'en',
apiKey: api_key
})
(result['status']).should.equal('success')
end
it 'should not disable the user system if it is already disabled 'do
request('/user/logout')
Scripts.login($staff[:email], $staff[:password], true)
result = request('/system/disable-user-system', {
csrf_userid: $csrf_userid,
csrf_token: $csrf_token,
password:$staff[:password]
})
(result['status']).should.equal('fail')
(result['message']).should.equal('SYSTEM_USER_IS_ALREADY_DISABLED')
end
it 'should allow staff members to recover their passwords' do
request('/user/logout')
result = request('/user/send-recover-password', {
email: 'jorah@opensupports.com',
staff: true
})
(result['status']).should.equal('success')
token = $database.getLastRow('recoverpassword')['token'];
result = request('/user/recover-password', {
email: 'jorah@opensupports.com',
password: 's3cur3p455w0rd',
token: token
})
(result['status']).should.equal('success')
(result['data']['staff']).should.equal('1')
result = request('/user/login', {
email: 'jorah@opensupports.com',
password: 's3cur3p455w0rd',
staff: true
})
(result['status']).should.equal('success')
(result['data']['userEmail']).should.equal('jorah@opensupports.com')
end
it 'should enable the user system' do
request('/user/logout')
Scripts.login($staff[:email], $staff[:password], true)
result = request('/system/enable-user-system', {
csrf_userid: $csrf_userid,
csrf_token: $csrf_token,
password:$staff[:password]
})
(result['status']).should.equal('success')
row = $database.getRow('setting', 'user-system-enabled', 'name')
(row['value']).should.equal('1')
numberOftickets= $database.query("SELECT * FROM ticket WHERE author_email IS NULL AND author_name IS NULL AND author_id IS NOT NULL" )
(numberOftickets.num_rows).should.equal(56)
end
it 'should not enable the user system' do
result = request('/system/enable-user-system', {
csrf_userid: $csrf_userid,
csrf_token: $csrf_token,
password:$staff[:password]
})
(result['status']).should.equal('fail')
(result['message']).should.equal('SYSTEM_USER_IS_ALREADY_ENABLED')
end
end

View File

@ -5,6 +5,7 @@ describe '/system/get-settings' do
(result['status']).should.equal('success')
(result['data']['language']).should.equal('en')
(result['data']['departments'][0]['name']).should.equal('Help and Support')
(result['data']['mandatory-login']).should.equal('1')
(result['data']['allowedLanguages'][0]).should.equal('en')
(result['data']['allowedLanguages'][1]).should.equal('es')
(result['data']['allowedLanguages'][2]).should.equal('de')

View File

@ -14,14 +14,14 @@ describe '/system/init-settings' do
it 'should initialize correctly' do
result = request('/system/init-settings', {
'user-system-enabled' => true,
'registration' => true,
'title' => 'Support Center',
'smtp-host' => 'localhost:7070',
'smtp-user' => 'testemail@opensupports.com',
'smtp-pass' => 'password',
'server-email' => 'testemail@opensupports.com',
'language' => 'en'
'language' => 'en',
'mandatory-login' => true
})
(result['status']).should.equal('success')

View File

@ -0,0 +1,386 @@
describe'system/mandatory-login' do
it 'should fail if a creator tries to create a ticket without login' do
result = request('/ticket/create', {
email: 'nonuser@os4.com',
language: 'en',
name: 'nonuser',
title: 'ticket created without login',
content: 'THis is the first content created without login',
departmentId: 1
})
(result['status']).should.equal('fail')
(result['message']).should.equal('NO_PERMISSION')
end
request('/user/logout')
Scripts.login($staff[:email], $staff[:password], true)
it 'should fail trying to disable mandatory login when registration is off' do
request('/system/disable-registration', {
"csrf_userid" => $csrf_userid,
"csrf_token" => $csrf_token,
"password" => "staff"
})
result = request('/system/disable-mandatory-login', {
"csrf_userid" => $csrf_userid,
"csrf_token" => $csrf_token,
"password" => "staff"
})
(result['status']).should.equal('fail')
(result['message']).should.equal('REGISTRATION_IS_DESACTIVATED')
row = $database.getRow('setting', 'mandatory-login', 'name')
(row['value']).should.equal('1')
request('/system/enable-registration', {
"csrf_userid" => $csrf_userid,
"csrf_token" => $csrf_token,
"password" => "staff"
})
end
it 'should disable the mandatory login' do
result = request('/system/disable-mandatory-login', {
"csrf_userid" => $csrf_userid,
"csrf_token" => $csrf_token,
"password" => "invalidPassword"
})
(result['status']).should.equal('fail')
(result['message']).should.equal('INVALID_PASSWORD')
row = $database.getRow('setting', 'mandatory-login', 'name')
(row['value']).should.equal('1')
result = request('/system/disable-mandatory-login', {
"csrf_userid" => $csrf_userid,
"csrf_token" => $csrf_token,
"password" => "staff"
})
(result['status']).should.equal('success')
row = $database.getRow('setting', 'mandatory-login', 'name')
(row['value']).should.equal('0')
end
it 'should fail trying to disable registration if mandatory login is false' do
result = request('/system/disable-registration', {
"csrf_userid" => $csrf_userid,
"csrf_token" => $csrf_token,
"password" => "staff"
})
(result['status']).should.equal('fail')
(result['message']).should.equal('MANDATORY_LOGIN_IS_DESACTIVATED')
row = $database.getRow('setting', 'registration', 'name')
(row['value']).should.equal('1')
end
it 'should allow Staff invite Users when Mandatory-login is off' do
result = request('/user/invite', {
"csrf_userid" => $csrf_userid,
"csrf_token" => $csrf_token,
email: 'inviteduser@opensupports.com',
name: 'inviteduser'
})
(result['status']).should.equal('success')
(result['data']['userEmail']).should.equal('inviteduser@opensupports.com')
$row = $database.getRow('recoverpassword','inviteduser@opensupports.com','email')
($row['email']).should.equal('inviteduser@opensupports.com')
end
it 'should allow a creator creates a ticket and create him a user' do
request('/user/logout')
result = request('/ticket/create', {
email: 'nonuser@os4.com',
language: 'en',
name: 'nonuser',
title: 'ticket created without login',
content: 'THis is a content created without login',
departmentId: 1
})
$ticketRow = $database.getRow('ticket','ticket created without login','title')
$userRow = $database.getRow('user','nonuser@os4.com','email')
(result['status']).should.equal('success')
(result['data']['ticketNumber']).should.equal($ticketRow['ticket_number'].to_i)
($userRow['email']).should.equal('nonuser@os4.com')
($userRow['not_registered']).should.equal('1')
($userRow['tickets']).should.equal('1')
end
it 'should allow the creator creates another ticket and not create another user' do
result = request('/ticket/create', {
email: 'nonuser@os4.com',
language: 'en',
name: 'nonuser',
title: 'ticket2 created without login',
content: 'THis is the second content created without login',
departmentId: 1
})
$ticketRow = $database.getRow('ticket','ticket2 created without login','title')
$userRow = $database.getRow('user','nonuser@os4.com','email')
(result['status']).should.equal('success')
(result['data']['ticketNumber']).should.equal($ticketRow['ticket_number'].to_i)
($userRow['email']).should.equal('nonuser@os4.com')
($userRow['tickets']).should.equal('2')
end
it 'should fail if a creator check others ticket' do
$ticketRow = $database.getRow('ticket',1,'id')
result = request('/ticket/check', {
email: 'nonuser@os4.com',
ticketNumber: $ticketRow['ticket_number']
})
(result['status']).should.equal('fail')
(result['message']).should.equal('NO_PERMISSION')
end
it 'should fail if a creator tries to check an inexistant ticket' do
result = request('/ticket/check', {
email: 'nonuser@os4.com',
ticketNumber: 111111
})
(result['status']).should.equal('fail')
(result['message']).should.equal('INVALID_TICKET')
end
it 'should allow a creator check a own ticket' do
$ticketRow = $database.getRow('ticket','ticket created without login','title')
$userRow = $database.getRow('user','nonuser@os4.com','email')
result = request('/ticket/check', {
email: 'nonuser@os4.com',
ticketNumber: $ticketRow['ticket_number']
})
(result['status']).should.equal('success')
(result['data']['userId']).should.equal($userRow['id'])
(result['data']['ticketNumber']).should.equal($ticketRow['ticket_number'])
$sessionToken = result['data']['token']
$sessionId = result['data']['userId']
$sessionTicketNumber = result['data']['ticketNumber']
end
it 'should fail if the creator creates a ticket using a diferent email of the session' do
result = request('/ticket/create', {
email: 'nonuser2@os4.com',
language: 'en',
name: 'nonuser2',
title: 'ticket3 created without login',
content: 'THis is the third content created without login',
departmentId: 1
})
(result['status']).should.equal('fail')
(result['message']).should.equal('INVALID_EMAIL')
end
it 'should allow the creator get the ticket checked' do
result = request('/ticket/get', {
csrf_token: $sessionToken,
csrf_userid: $sessionId,
ticketNumber: $sessionTicketNumber
})
(result['status']).should.equal('success')
(result['data']['title']).should.equal($ticketRow['title'])
(result['data']['content']).should.equal($ticketRow['content'])
end
it 'should allow the creator handle the ticket checked' do
result = request('/ticket/edit-title', {
csrf_token: $sessionToken,
csrf_userid: $sessionId,
ticketNumber: $sessionTicketNumber,
title: 'new title of ticket created without login'
})
(result['status']).should.equal('success')
result = request('/ticket/edit-comment', {
csrf_token: $sessionToken,
csrf_userid: $sessionId,
ticketNumber: $sessionTicketNumber,
content: 'this is the new content of the ticket created without login'
})
(result['status']).should.equal('success')
result = request('/ticket/comment', {
csrf_token: $sessionToken,
csrf_userid: $sessionId,
ticketNumber: $sessionTicketNumber,
content: 'this is the first comment without login'
})
(result['status']).should.equal('success')
result = request('/ticket/comment', {
csrf_token: $sessionToken,
csrf_userid: $sessionId,
ticketNumber: $sessionTicketNumber,
content: 'this is the second comment without login'
})
(result['status']).should.equal('success')
result = request('/ticket/edit-comment', {
csrf_token: $sessionToken,
csrf_userid: $sessionId,
ticketNumber: $sessionTicketNumber,
ticketEventId: 0,
content: 'this is the first edited-comment without login'
})
(result['status']).should.equal('success')
result = request('/ticket/close', {
csrf_token: $sessionToken,
csrf_userid: $sessionId,
ticketNumber: $sessionTicketNumber,
})
(result['status']).should.equal('success')
$ticketRow = $database.getRow('ticket','new title of ticket created without login','title')
($ticketRow['title']).should.equal('new title of ticket created without login')
($ticketRow['content']).should.equal('this is the first edited-comment without login')
($ticketRow['closed']).should.equal('1')
result = request('/ticket/delete', {
csrf_token: $sessionToken,
csrf_userid: $sessionId,
ticketNumber: $sessionTicketNumber,
})
(result['status']).should.equal('success')
end
it 'should fail if the creator tries to get a own ticket not checked' do
$ticketRow = $database.getRow('ticket','ticket2 created without login','title')
result = request('/ticket/get', {
csrf_token: $sessionToken,
csrf_userid: $sessionId,
ticketNumber: $ticketRow['ticket_number'],
})
(result['status']).should.equal('fail')
(result['message']).should.equal('NO_PERMISSION')
end
it 'should re-login if the creator tries to check another ticket with a existent session' do
$ticketRow = $database.getRow('ticket','ticket2 created without login','title')
result = request('/ticket/check', {
email: 'nonuser@os4.com',
ticketNumber: $ticketRow['ticket_number']
})
(result['status']).should.equal('success')
end
it 'should fail if the creator tries to login with email used to create tickets' do
result = request('/user/login', {
email: 'nonuser@os4.com'
})
(result['status']).should.equal('fail')
(result['message']).should.equal('INVALID_CREDENTIALS')
request('/user/logout')
result = request('/user/login', {
email: 'nonuser@os4.com'
})
(result['status']).should.equal('fail')
(result['message']).should.equal('INVALID_CREDENTIALS')
end
it 'should allow the creator sign up' do
request('/user/logout')
Scripts.createUser('nonuser@os4.com', 'customPassword', 'nonuser')
$userRow = $database.getRow('user','nonuser@os4.com','email')
($userRow['never_logged']).should.equal(nil)
($userRow['verification_token']).should.equal(nil)
end
it 'should allow the creator login and get more than 1 own ticket' do
request('/user/logout')
result = request('/user/login', {
email: 'nonuser@os4.com',
password: 'customPassword'
})
(result['status']).should.equal('success')
$sessionToken = result['data']['token']
$sessionUserId = result['data']['userId']
result = request('/ticket/create', {
title: 'Valid titlee',
content: 'ticket created to see ifcreator can handle 2 tickets',
departmentId: 1,
language: 'en',
csrf_userid: $sessionUserId,
csrf_token: $sessionToken
})
$ticket2 = $database.getRow('ticket', 'ticket2 created without login', 'title')
$ticket3 = $database.getRow('ticket', 'ticket created to see ifcreator can handle 2 tickets', 'content')
result = request('/ticket/get', {
csrf_userid: $sessionUserId,
csrf_token: $sessionToken,
ticketNumber: $ticket3['ticket_number'],
})
(result['status']).should.equal('success')
result = request('/ticket/get', {
csrf_userid: $sessionUserId,
csrf_token: $sessionToken,
ticketNumber: $ticket2['ticket_number'],
})
(result['status']).should.equal('success')
end
request('/user/logout')
Scripts.login($staff[:email], $staff[:password], true)
it 'should allow staff enable the mandatory login' do
result = request('/system/enable-mandatory-login', {
"csrf_userid" => $csrf_userid,
"csrf_token" => $csrf_token,
"password" => "invalidPassword"
})
(result['status']).should.equal('fail')
(result['message']).should.equal('INVALID_PASSWORD')
row = $database.getRow('setting', 'mandatory-login', 'name')
(row['value']).should.equal('0')
result = request('/system/enable-mandatory-login', {
"csrf_userid" => $csrf_userid,
"csrf_token" => $csrf_token,
"password" => "staff"
})
(result['status']).should.equal('success')
row = $database.getRow('setting', 'mandatory-login', 'name')
(row['value']).should.equal('1')
end
end

View File

@ -1,6 +1,6 @@
describe '/ticket/get-authors/' do
it 'should fail if a user is loged' do
it 'should fail if a user is logged' do
request('/user/logout')
Scripts.login('tyrion@opensupports.com', 'tyrionl')

View File

@ -22,14 +22,13 @@ describe '/user/login' do
(result['status']).should.equal('success')
end
it 'should fail if already logged in' do
it 'should not fail if already logged in' do
result = request('/user/login', {
email: @loginEmail,
password: @loginPass
})
(result['status']).should.equal('fail')
(result['message']).should.equal('SESSION_EXISTS')
(result['status']).should.equal('success')
end
it 'should login staff member' do