diff options
author | Seb Bacon <seb.bacon@gmail.com> | 2012-05-15 07:46:50 +0100 |
---|---|---|
committer | Seb Bacon <seb.bacon@gmail.com> | 2012-05-15 07:47:16 +0100 |
commit | 8d78cb8844549dc901cd901371be6ec604fb7f68 (patch) | |
tree | 0169ee73dd9164ad6d95001dccdf98cfc8e655f3 | |
parent | 835fb0a3e87701316120b5c4625213dd41c4e762 (diff) | |
parent | 70daa37c704dfae813641d31b8c51261343bab46 (diff) |
Merge branch 'feature/superusers_as_admin_interface_users' into develop
Fixes #288
-rw-r--r-- | app/controllers/admin_controller.rb | 45 | ||||
-rw-r--r-- | app/views/user/sign.rhtml | 5 | ||||
-rw-r--r-- | config/general.yml-example | 12 | ||||
-rw-r--r-- | doc/CHANGES.md | 8 | ||||
-rw-r--r-- | doc/INSTALL.md | 37 | ||||
-rw-r--r-- | public/stylesheets/main.css | 6 | ||||
-rw-r--r-- | spec/controllers/admin_public_body_controller_spec.rb | 41 |
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 |