Project

General

Profile

« Previous | Next » 

Revision ba69b49b

Added by Martin Bacovsky almost 12 years ago

  • ID ba69b49bedf7df0e00bb434852e21e62e0630c6e

api v1 - Users controller and tests

- split api routes to separate routes file
- better detection of permission failure in model
- fix ApiConstraints
- catch bad routes in api and return json
- render home#index links from restapi
- fixed resource params recognition

View differences:

app/controllers/api/base_controller.rb
rescue_from StandardError, :with => lambda { |error|
Rails.logger.error "#{error.message} (#{error.class})\n#{error.backtrace.join("\n")}"
render_error 'standard_error', :status => 500, :locals => { :error => error }
render_error 'standard_error', :status => 500, :locals => { :exception => error }
}
def get_resource
......
end
end
def process_response(condition, response = get_resource)
if condition
def process_success(response = nil)
response ||= get_resource
respond_with response
end
def process_response(condition, response = nil)
if condition
process_success response
else
process_resource_error
end
......
User.current.allowed_to?(:controller => ctrl.gsub(/::/, "_").underscore, :action => action) or deny_access
end
def deny_access
render_error 'access_denied', :status => :forbidden
def deny_access(details = nil)
render_error 'access_denied', :status => :forbidden, :locals => { :details => details }
false
end
......
# store params[:id] under correct predicable key
def set_resource_params
if (id_or_name = params.delete(:id))
suffix = id_or_name =~ /^\d+$/ ? 'id' : 'name'
suffix = (id_or_name.is_a?(Fixnum) || id_or_name =~ /^\d+$/) ? 'id' : 'name'
params[:"#{resource_name}_#{suffix}"] = id_or_name
end
end
app/controllers/api/v1/users_controller.rb
module Api
module V1
class UsersController < BaseController
include Foreman::Controller::AutoCompleteSearch
before_filter :find_resource, :only => %w{show update destroy}
api :GET, "/users/", "List all users."
def index
@users = User.search_for(params[:search], :order => params[:order]).
paginate :page => params[:page]
end
api :GET, "/users/:id/", "Show an user."
def show
end
api :POST, "/users/", "Create an user."
description <<-DOC
Adds role 'Anonymous' to the user by default
DOC
param :user, Hash, :required => true do
param :login, String, :required => true
param :firstname, String, :required => false
param :lastname, String, :required => false
param :mail, String, :required => true
param :admin, :bool, :required => false, :desc => "Is an admin account?"
end
def create
@user = User.new(params[:user])
@user.admin = params[:user][:admin]
if @user.save
@user.roles << Role.find_by_name("Anonymous") unless @user.roles.map(&:name).include? "Anonymous"
process_success
else
process_resource_error
end
end
api :PUT, "/users/:id/", "Update an user."
description <<-DOC
Adds role 'Anonymous' to the user if it is not already present.
Only admin can set admin account.
DOC
param :user, Hash, :required => true do
param :login, String, :required => false
param :firstname, String, :required => false
param :lastname, String, :required => false
param :mail, String, :required => false
param :admin, :bool, :required => false, :desc => "Is an admin account?"
end
def update
admin = params[:user].has_key?(:admin) ? params[:user].delete(:admin) : nil
# Remove keys for restricted variables when the user is editing their own account
if @user == User.current
for key in params[:user].keys
params[:user].delete key unless %w{password_confirmation password mail firstname lastname}.include? key
end
end
if @user.update_attributes(params[:user])
# Only an admin can update admin attribute of another use
# this is required, as the admin field is blacklisted above
@user.update_attribute(:admin, admin) if User.current.admin and !admin.nil?
@user.roles << Role.find_by_name("Anonymous") unless @user.roles.map(&:name).include? "Anonymous"
process_success
else
process_resource_error
end
end
api :DELETE, "/users/:id/", "Delete an user."
def destroy
if @user == User.current
deny_access "You are trying to delete your own account"
else
process_response @user.destroy
end
end
end
end
end
app/models/usergroup.rb
require 'user'
class Usergroup < ActiveRecord::Base
include Authorization
has_many_polymorphs :members, :from => [:usergroups, :users ], :as => :member,
app/views/api/v1/errors/access_denied.json.rabl
object false
object false => :error
node(:message) { "Access denied" }
node(:message) {'Access denied'}
node(:details) {locals[:details]}
app/views/api/v1/errors/standard_error.json.rabl
error = locals[:error]
exception = locals[:exception]
object error => :error
object exception => :error
attributes :message, :backtrace
node(:class) { error.class.to_s }
node(:class) { exception.class.to_s }
app/views/api/v1/users/index.json.rabl
collection @users
extends "api/v1/users/show"
app/views/api/v1/users/show.json.rabl
object @user
attributes :id, :login, :firstname, :lastname, :mail, :admin, :auth_source, :roles
config/routes.rb
resources :tasks, :only => [:show]
#Keep this line the last route
match '*a', :to => 'errors#routing'
end
config/routes/api/v1.rb
scope :module => :v1, :constraints => ApiConstraints.new(:version => 1, :default => true) do
resources :bookmarks, :except => [:new, :edit]
resources :architectures, :except => [:new, :edit]
resources :users, :except => [:new, :edit]
resources :operatingsystems, :except => [:new, :edit] do
member do
get 'bootfiles'
lib/foreman/access_permissions.rb
end
map.security_block :users do |map|
map.permission :view_users, {:users => [:index, :show]}
map.permission :create_users, {:users => [:new, :create]}
map.permission :edit_users, {:users => [:edit, :update]}
map.permission :destroy_users, {:users => [:destroy]}
map.permission :view_users,
:users => [:index, :show], :"api/v1/users" => [:index, :show]
map.permission :create_users,
:users => [:new, :create], :"api/v1/users" => [:new, :create]
map.permission :edit_users,
:users => [:edit, :update], :"api/v1/users" => [:edit, :update]
map.permission :destroy_users,
:users => [:destroy], :"api/v1/users" => [:destroy]
end
map.security_block :settings_menu do |map|
test/functional/api/v1/users_controller_test.rb
require 'test_helper'
class Api::V1::UsersControllerTest < ActionController::TestCase
test "should get index" do
as_user :admin do
get :index, {}
end
assert_response :success
end
test "should update user" do
as_user :admin do
user = User.create :login => "foo", :mail => "foo@bar.com", :auth_source => auth_sources(:one)
put :update, { :id => user.id, :user => {:login => "johnsmith"} }
assert_response :success
mod_user = User.find_by_id(user.id)
assert mod_user.login == "johnsmith"
end
end
test "should not remove the anonymous role" do
as_user :admin do
user = User.create :login => "foo", :mail => "foo@bar.com", :auth_source => auth_sources(:one)
assert user.roles =([roles :anonymous])
put :update, { :id => user.id, :user => {:login => "johnsmith"} }
assert_response :success
mod_user = User.find_by_id(user.id)
assert mod_user.roles =([roles :anonymous])
end
end
test "should set password" do
as_user :admin do
user = User.new :login => "foo", :mail => "foo@bar.com", :firstname => "john", :lastname => "smith", :auth_source => auth_sources(:internal)
user.password = "changeme"
assert user.save
put :update, { :id => user.id, :user => { :login => "johnsmith", :password => "dummy", :password_confirmation => "dummy" }}
assert_response :success
mod_user = User.find_by_id(user.id)
assert mod_user.matching_password?("dummy")
end
end
test "should detect password validation mismatches" do
as_user :admin do
user = User.new :login => "foo", :mail => "foo@bar.com", :firstname => "john", :lastname => "smith", :auth_source => auth_sources(:internal)
user.password = "changeme"
assert user.save
put :update, { :id => user.id, :user => { :login => "johnsmith", :password => "dummy", :password_confirmation => "DUMMY" }}
assert_response :unprocessable_entity
mod_user = User.find_by_id(user.id)
assert mod_user.matching_password?("changeme")
end
end
test "should delete different user" do
as_user :admin do
user = users(:one)
delete :destroy, { :id => user.id }
assert_response :success
assert !User.exists?(user.id)
end
end
test "should not delete same user" do
user = users(:one)
user.update_attribute :admin, true
as_user :one do
delete :destroy, { :id => user.id }
assert_response :forbidden
response = ActiveSupport::JSON.decode(@response.body)
assert response['error']['details'] == "You are trying to delete your own account"
assert User.exists?(user)
end
end
def user_one_as_anonymous_viewer
users(:one).roles = [Role.find_by_name('Anonymous'), Role.find_by_name('Viewer')]
end
test 'user with viewer rights should fail to edit a user' do
user_one_as_anonymous_viewer
user = nil
as_user :admin do
user = User.create :login => "foo", :mail => "foo@bar.com", :auth_source => auth_sources(:one)
user.save
end
as_user :one do
put :update, { :id => user.id, :user => {:login => "johnsmith"} }
assert_response :forbidden
end
end
test 'user with viewer rights should succeed in viewing users' do
user_one_as_anonymous_viewer
as_user :one do
get :index
assert_response :success
end
end
# do we support this?
=begin
test "should recreate the admin account" do
user = users(:one)
user.update_attribute :admin, true
User.find_by_login("admin").delete # Of course we only use destroy in the codebase
assert User.find_by_login("admin").nil?
as_user :one do
get :index, {}
assert_response :success
end
assert !User.find_by_login("admin").nil?
end
=end
end
test/test_helper.rb
ENV["RAILS_ENV"] = "test"
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
require 'ap'
class ActiveSupport::TestCase
# Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.

Also available in: Unified diff