Revision ba69b49b
Added by Martin Bacovsky almost 12 years ago
- ID ba69b49bedf7df0e00bb434852e21e62e0630c6e
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
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