aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSeb Bacon <seb.bacon@gmail.com>2012-05-15 07:46:50 +0100
committerSeb Bacon <seb.bacon@gmail.com>2012-05-15 07:47:16 +0100
commit8d78cb8844549dc901cd901371be6ec604fb7f68 (patch)
tree0169ee73dd9164ad6d95001dccdf98cfc8e655f3
parent835fb0a3e87701316120b5c4625213dd41c4e762 (diff)
parent70daa37c704dfae813641d31b8c51261343bab46 (diff)
Merge branch 'feature/superusers_as_admin_interface_users' into develop
Fixes #288
-rw-r--r--app/controllers/admin_controller.rb45
-rw-r--r--app/views/user/sign.rhtml5
-rw-r--r--config/general.yml-example12
-rw-r--r--doc/CHANGES.md8
-rw-r--r--doc/INSTALL.md37
-rw-r--r--public/stylesheets/main.css6
-rw-r--r--spec/controllers/admin_public_body_controller_spec.rb41
7 files changed, 103 insertions, 51 deletions
diff --git a/app/controllers/admin_controller.rb b/app/controllers/admin_controller.rb
index 1612e5179..884d7e540 100644
--- a/app/controllers/admin_controller.rb
+++ b/app/controllers/admin_controller.rb
@@ -46,23 +46,40 @@ class AdminController < ApplicationController
expire_for_request(info_request)
end
end
- private
- def authenticate
- config_username = MySociety::Config.get('ADMIN_USERNAME', '')
- config_password = MySociety::Config.get('ADMIN_PASSWORD', '')
- if !config_username.empty? && !config_password.empty?
- authenticate_or_request_with_http_basic do |user_name, password|
- if user_name == config_username && password == config_password
- session[:using_admin] = 1
- request.env['REMOTE_USER'] = user_name
- else
- request_http_basic_authentication
+ private
+
+ def authenticate
+ if MySociety::Config.get('SKIP_ADMIN_AUTH', false)
+ session[:using_admin] = 1
+ return
+ else
+ if session[:using_admin].nil?
+ if params[:emergency].nil?
+ if authenticated?(
+ :web => _("To log into the administrative interface"),
+ :email => _("Then you can log into the administrative interface"),
+ :email_subject => _("Log into the admin interface"),
+ :user_name => "a superuser")
+ if !@user.nil? && @user.admin_level == "super"
+ session[:using_admin] = 1
+ request.env['REMOTE_USER'] = @user.url_name
+ end
+ end
+ else
+ config_username = MySociety::Config.get('ADMIN_USERNAME', '')
+ config_password = MySociety::Config.get('ADMIN_PASSWORD', '')
+ authenticate_or_request_with_http_basic do |user_name, password|
+ if user_name == config_username && password == config_password
+ session[:using_admin] = 1
+ request.env['REMOTE_USER'] = user_name
+ else
+ request_http_basic_authentication
+ end
end
end
- else
- session[:using_admin] = 1
end
- end
+ end
+ end
end
diff --git a/app/views/user/sign.rhtml b/app/views/user/sign.rhtml
index bfd0fa63e..4704ea95a 100644
--- a/app/views/user/sign.rhtml
+++ b/app/views/user/sign.rhtml
@@ -10,7 +10,10 @@
<%= @post_redirect.reason_params[:web] %>,
<%= _('please sign in as ')%><%= link_to h(@post_redirect.reason_params[:user_name]), @post_redirect.reason_params[:user_url] %>.
<% end %>
- </p>
+ </p>
+ <% if @post_redirect.post_params["controller"] == "admin_general" %>
+ <p id="superuser_message">Don't have a superuser account yet? <%= link_to "Sign in as the emergency user", @post_redirect.uri + "?emergency=1" %></p>
+ <% end %>
<%= render :partial => 'signin', :locals => { :sign_in_as_existing_user => true } %>
diff --git a/config/general.yml-example b/config/general.yml-example
index 84980c353..7325202c1 100644
--- a/config/general.yml-example
+++ b/config/general.yml-example
@@ -37,7 +37,7 @@ FRONTPAGE_PUBLICBODY_EXAMPLES: 'tgq'
# URL of theme to install (when running rails-post-deploy script)
THEME_URL: 'git://github.com/sebbacon/alavetelitheme.git'
-# Whether a user needs to sign in to start the New Request process
+# Whether a user neis eds to sign in to start the New Request process
FORCE_REGISTRATION_ON_NEW_REQUEST: false
@@ -56,10 +56,13 @@ BLACKHOLE_PREFIX: 'do-not-reply-to-this-address'
## Administration
-# Leave these two blank to skip admin authorisation
+# The emergency user
ADMIN_USERNAME: 'adminxxxx'
ADMIN_PASSWORD: 'passwordx'
+# Set this to true, and the admin interface will be available to anonymous users
+SKIP_ADMIN_AUTH: false
+
# Email "from" details
CONTACT_EMAIL: 'postmaster@localhost'
CONTACT_NAME: 'Alaveteli Webmaster'
@@ -146,3 +149,8 @@ MAX_REQUESTS_PER_USER_PER_DAY: 6
# This is used to work out where to send purge requests. Should be
# unset if you aren't running behind varnish
VARNISH_HOST: localhost
+
+# Set this to true if you want to use secure the admin interface with
+# HTTP Basic auth
+
+# config_username = MySociety::Config.get('ADMIN_USERNAME', '')
diff --git a/doc/CHANGES.md b/doc/CHANGES.md
index 968c34eff..4796b726f 100644
--- a/doc/CHANGES.md
+++ b/doc/CHANGES.md
@@ -7,7 +7,6 @@
less likely, when using Varnish, that users will be presented with
stale content. Fixes
[issue #436](https://github.com/sebbacon/alaveteli/issues/436)
-
## Upgrade notes
* Existing installations will need to install the Bundler gem. See
@@ -36,6 +35,13 @@
in `ugly` format at `config/purge-varnish-debian.ugly` to ensure the
purge queue is emptied regularly.
+* Administrators are now assumed to log in using standard user accounts
+ with superuser privileges (see 'Administrator Privileges' in
+ `INSTALL.md`). The old-style admin account (using credentials from
+ `general.yml`) is now known as the "emergency user". Deployments
+ that previously bypassed admin authentication should set the new
+ `SKIP_ADMIN_AUTH` config variable to `true`.
+
# Version 0.5.1
## Highlighted features
diff --git a/doc/INSTALL.md b/doc/INSTALL.md
index 1e2f3d134..cc72bf6b8 100644
--- a/doc/INSTALL.md
+++ b/doc/INSTALL.md
@@ -282,23 +282,26 @@ the site in action.
# Administrator privileges
-By default, anyone can access the administrator pages without authentication.
-They are under the URL `/admin`.
-
-At mySociety (originators of the Alaveteli software), they use a
-separate layer of HTTP basic authentication, proxied over HTTPS, to
-check who is allowed to use the administrator pages. You might like to
-do something similar.
-
-Alternatively, update the code so that:
-
-* By default, admin pages use normal site authentication (checking user admin
-level 'super').
-* Create an option in `config/general` which lets us override that
-behaviour.
-
-And send us the patch!
-
+The administrative interface is at the URL `/admin`.
+
+Only users with the `super` admin level can access the admin
+interface. Users create their own accounts in the usual way, and then
+administrators can give them `super` privileges.
+
+There is an emergency user account which can be accessed via
+`/admin?emergency=1`, using the credentials `ADMIN_USERNAME` and
+`ADMIN_PASSWORD`, which are set in `general.yml`. To bootstrap the
+first `super` level accounts, you will need to log in as the emergency
+user.
+
+Users with the superuser role also have extra privileges in the
+website frontend, such as being able to categorise any request, being
+able to view items that have been hidden from the search, and being
+presented with "admin" links next to individual requests and comments
+in the front end.
+
+It is possible completely to override the administrator authentication
+by setting `SKIP_ADMIN_AUTH` to `true` in `general.yml`.
# Cron jobs
diff --git a/public/stylesheets/main.css b/public/stylesheets/main.css
index 87d3a3eed..a92b6b382 100644
--- a/public/stylesheets/main.css
+++ b/public/stylesheets/main.css
@@ -636,13 +636,15 @@ margin-top:-1em;
margin:0 0 0 9em;
}
-p#sign_in_reason {
+p#sign_in_reason, p#superuser_message {
text-align:center;
font-size:1.4em;
font-weight:700;
line-height:1em;
}
-
+p#superuser_message {
+ font-size:1.2em;
+}
#signup,#signin {
clear:none;
margin-bottom:1em;
diff --git a/spec/controllers/admin_public_body_controller_spec.rb b/spec/controllers/admin_public_body_controller_spec.rb
index 1e82a0ba4..2fa893a93 100644
--- a/spec/controllers/admin_public_body_controller_spec.rb
+++ b/spec/controllers/admin_public_body_controller_spec.rb
@@ -84,15 +84,14 @@ describe AdminPublicBodyController, "when administering public bodies and paying
@request.env["HTTP_AUTHORIZATION"] = ""
n = PublicBody.count
post :destroy, { :id => 3 }
- response.code.should == "401"
+ response.should redirect_to(:controller=>'user', :action=>'signin', :token=>PostRedirect.get_last_post_redirect.token)
PublicBody.count.should == n
session[:using_admin].should == nil
end
- it "skips admin authorisation when no username/password set" do
+ it "skips admin authorisation when SKIP_ADMIN_AUTH set" do
config = MySociety::Config.load_default()
- config['ADMIN_USERNAME'] = ''
- config['ADMIN_PASSWORD'] = ''
+ config['SKIP_ADMIN_AUTH'] = true
@request.env["HTTP_AUTHORIZATION"] = ""
n = PublicBody.count
@@ -101,30 +100,44 @@ describe AdminPublicBodyController, "when administering public bodies and paying
session[:using_admin].should == 1
end
- it "skips admin authorisation when no username set" do
+ it "doesn't let people with bad credentials log in" do
config = MySociety::Config.load_default()
- config['ADMIN_USERNAME'] = ''
+ config['SKIP_ADMIN_AUTH'] = false
+ config['ADMIN_USERNAME'] = 'biz'
config['ADMIN_PASSWORD'] = 'fuz'
@request.env["HTTP_AUTHORIZATION"] = ""
-
n = PublicBody.count
+ basic_auth_login(@request, "baduser", "badpassword")
post :destroy, { :id => public_bodies(:forlorn_public_body).id }
- PublicBody.count.should == n - 1
- session[:using_admin].should == 1
+ response.should redirect_to(:controller=>'user', :action=>'signin', :token=>PostRedirect.get_last_post_redirect.token)
+ PublicBody.count.should == n
+ session[:using_admin].should == nil
end
- it "forces authorisation when password and username set" do
+
+ it "allows people with good credentials log in using HTTP Basic Auth" do
config = MySociety::Config.load_default()
+ config['SKIP_ADMIN_AUTH'] = false
config['ADMIN_USERNAME'] = 'biz'
config['ADMIN_PASSWORD'] = 'fuz'
@request.env["HTTP_AUTHORIZATION"] = ""
n = PublicBody.count
- basic_auth_login(@request, "baduser", "badpassword")
+ basic_auth_login(@request, "biz", "fuz")
+ post :show, { :id => public_bodies(:humpadink_public_body).id, :emergency => 1}
+ session[:using_admin].should == 1
+ n = PublicBody.count
post :destroy, { :id => public_bodies(:forlorn_public_body).id }
- response.code.should == "401"
- PublicBody.count.should == n
- session[:using_admin].should == nil
+ session[:using_admin].should == 1
+ PublicBody.count.should == n - 1
end
+ it "allows superusers to do stuff" do
+ session[:user_id] = users(:admin_user).id
+ @request.env["HTTP_AUTHORIZATION"] = ""
+ n = PublicBody.count
+ post :destroy, { :id => public_bodies(:forlorn_public_body).id }
+ PublicBody.count.should == n - 1
+ session[:using_admin].should == 1
+ end
end