added password protection

closes #4993
- brings password protection to the frontend of blogs
- adds testing for password protection
- upgrades bcrypt-js to 2.1.0
This commit is contained in:
Austin Burdine 2015-03-26 02:01:39 -05:00
parent 5e5879a2f0
commit 8913d36864
11 changed files with 150 additions and 1 deletions

View File

@ -24,6 +24,10 @@ var FeatureController = Ember.Controller.extend(Ember.PromiseProxyMixin, {
}
return value;
}),
passProtectUI: Ember.computed('config.passProtectUI', 'labs.passProtectUI', function () {
return this.get('config.passProtectUI') || this.get('labs.passProtectUI');
})
});

View File

@ -1,6 +1,8 @@
import Ember from 'ember';
var SettingsController = Ember.Controller.extend({
needs: ['feature'],
showGeneral: Ember.computed('session.user.name', function () {
return this.get('session.user.isAuthor') || this.get('session.user.isEditor') ? false : true;
}),
@ -21,6 +23,9 @@ var SettingsController = Ember.Controller.extend({
}),
showAbout: Ember.computed('session.user.name', function () {
return this.get('session.user.isAuthor') ? false : true;
}),
showPassProtection: Ember.computed('session.user.name', 'controllers.feature.passProtectUI', function () {
return this.get('session.user.isAuthor') || this.get('session.user.isEditor') || !this.get('controllers.feature.passProtectUI') ? false : true;
})
});

View File

@ -23,6 +23,16 @@ var LabsController = Ember.Controller.extend(Ember.Evented, {
});
},
usePassProtectUI: Ember.computed('controllers.feature.passProtectUI', function (key, value) {
// setter
if (arguments.length > 1) {
this.saveLabs('passProtectUI', value);
}
// getter
return this.get('controllers.feature.passProtectUI');
}),
actions: {
onUpload: function (file) {
var self = this,

View File

@ -0,0 +1,27 @@
import Ember from 'ember';
var SettingsPassProtectController = Ember.Controller.extend({
actions: {
save: function () {
var self = this;
if (this.get('model.isPrivate') && this.get('model.password') === '') {
self.notifications.closePassive();
self.notifications.showError('Password must have a value.');
return;
}
return this.get('model').save().then(function (model) {
self.notifications.closePassive();
self.notifications.showSuccess('Settings successfully saved.');
return model;
}).catch(function (errors) {
self.notifications.closePassive();
self.notifications.showErrors(errors);
});
}
}
});
export default SettingsPassProtectController;

View File

@ -19,7 +19,9 @@ var Setting = DS.Model.extend(NProgressSaveMixin, ValidationEngine, {
ghost_head: DS.attr('string'),
ghost_foot: DS.attr('string'),
labs: DS.attr('string'),
navigation: DS.attr('string')
navigation: DS.attr('string'),
isPrivate: DS.attr('boolean'),
password: DS.attr('string')
});
export default Setting;

View File

@ -43,6 +43,7 @@ Router.map(function () {
this.route('labs');
this.route('code-injection');
this.route('navigation');
this.route('pass-protect');
});
// Redirect debug to settings labs

View File

@ -0,0 +1,45 @@
import AuthenticatedRoute from 'ghost/routes/authenticated';
import loadingIndicator from 'ghost/mixins/loading-indicator';
import CurrentUserSettings from 'ghost/mixins/current-user-settings';
import styleBody from 'ghost/mixins/style-body';
var SettingsPassProtectRoute = AuthenticatedRoute.extend(styleBody, loadingIndicator, CurrentUserSettings, {
classNames: ['settings-view-pass'],
beforeModel: function () {
var feature = this.controllerFor('feature'),
self = this;
if (!feature) {
this.generateController('feature');
feature = this.controllerFor('feature');
}
return this.get('session.user')
.then(this.transitionAuthor())
.then(this.transitionEditor())
.then(function () {
return feature.then(function () {
if (!feature.get('passProtectUI')) {
return self.transitionTo('settings.general');
}
});
});
},
model: function () {
return this.store.find('setting', {type: 'blog,theme'}).then(function (records) {
return records.get('firstObject');
});
},
actions: {
save: function () {
this.get('controller').send('save');
}
}
});
export default SettingsPassProtectRoute;

View File

@ -27,6 +27,10 @@
{{gh-activating-list-item route="settings.code-injection" title="Code Injection" classNames="settings-nav-code icon-code"}}
{{/if}}
{{#if showPassProtection}}
{{gh-activating-list-item route="settings.pass-protect" title="Password Protection" classNames="settings-nav-pass icon-lock"}}
{{/if}}
{{#if showLabs}}
{{gh-activating-list-item route="settings.labs" title="Labs" classNames="settings-nav-labs icon-atom"}}
{{/if}}

View File

@ -44,4 +44,21 @@
</fieldset>
</form>
<hr>
<form>
<fieldset>
<div class="form-group for-checkbox">
<label for="labs-passProtectUI">Password Protection</label>
<label class="checkbox" for="labs-passProtectUI">
{{input id="labs-passProtectUI" name="labs[passProtectUI]" type="checkbox"
checked=usePassProtectUI}}
<span class="input-toggle-component"></span>
<p>Enable the password protection interface</p>
</label>
<p>A settings screen which enables you to add password protection to the front of your blog (work in progress)</p>
</div>
</fieldset>
</form>
</section>

View File

@ -0,0 +1,29 @@
<header class="settings-view-header">
{{#link-to "settings" class="btn btn-default btn-back"}}Back{{/link-to}}
<h2 class="page-title">Password protect your blog</h2>
<section class="page-actions">
<button type="button" class="btn btn-blue" {{action "save"}}>Save</button>
</section>
</header>
<section class="content settings-pass">
<form id="settings-pass" novalidate="novalidate">
<fieldset>
<div class="form-group for-checkbox">
<label for="blog-isPrivate">Make this blog private</label>
<label class="checkbox" for="blog-isPrivate">
{{input id="blog-isPrivate" name="labs[passProtectUI]" type="checkbox"
checked=model.isPrivate}}
<span class="input-toggle-component"></span>
<p>Enable password protection</p>
</label>
</div>
{{#if model.isPrivate}}
<div class="form-group">
{{input name="private[password]" type="text" value=model.password}}
<p>This password will be needed to access your blog. All search engine optimization and social features are now disabled.</p>
</div>
{{/if}}
</fieldset>
</form>
</section>

View File

@ -0,0 +1,5 @@
import BaseView from 'ghost/views/settings/content-base';
var SettingsGeneralView = BaseView.extend();
export default SettingsGeneralView;