WIP. having fun with authorisation.

pull/1065/head
Richard 11 years ago
parent d7306ad571
commit 1b3a367077
  1. 38
      app/assets/javascripts/angular/helpers/components/path-helper.js
  2. 2
      app/assets/javascripts/angular/services/query-service.js
  3. 2
      app/assets/javascripts/angular/services/status-service.js
  4. 2
      app/assets/javascripts/angular/services/user-service.js
  5. 6
      app/assets/javascripts/angular/services/work-package-service.js
  6. 3
      app/controllers/api/v2/api_controller.rb
  7. 3
      app/controllers/api/v3/api_controller.rb
  8. 74
      app/controllers/api/v3/work_packages_controller.rb
  9. 73
      app/controllers/work_packages_controller.rb
  10. 1
      app/models/user.rb
  11. 31
      app/views/api/v3/work_packages/column_data.api.rabl
  12. 31
      app/views/api/v3/work_packages/column_sums.api.rabl
  13. 7
      config/routes.rb
  14. 5
      lib/redmine.rb

@ -4,9 +4,10 @@ angular.module('openproject.helpers')
.service('PathHelper', [function() {
PathHelper = {
apiPrefix: '/api/v2',
apiPrefixV3: '/api/v3',
projectPath: function(projectIdentifier) {
return '/api/v3/projects/' + projectIdentifier;
return '/projects/' + projectIdentifier;
},
workPackagesPath: function() {
return '/work_packages';
@ -14,29 +15,38 @@ angular.module('openproject.helpers')
workPackagePath: function(id) {
return '/work_packages/' + id;
},
projectWorkPackagesPath: function(projectIdentifier) {
return PathHelper.projectPath(projectIdentifier) + PathHelper.workPackagesPath() + ".json";
},
usersPath: function() {
return '/users';
},
userPath: function(id) {
return PathHelper.usersPath() + id;
},
workPackagesColumnDataPath: function() {
return PathHelper.workPackagesPath() + '/column_data';
},
workPackagesSumsPath: function(projectIdentifier) {
return PathHelper.projectPath(projectIdentifier) + '/column_sums';
},
versionPath: function(versionId) {
return '/versions/' + versionId;
},
statusesPath: function() {
return '/statuses'
apiProjectPath: function(projectIdentifier) {
return PathHelper.apiPrefixV3 + PathHelper.projectPath(projectIdentifier);
},
apiWorkPackagesPath: function() {
return PathHelper.apiPrefixV3 + '/work_packages';
},
apiProjectWorkPackagesPath: function(projectIdentifier) {
return PathHelper.apiProjectPath(projectIdentifier) + PathHelper.workPackagesPath();
},
apiAvailableColumnsPath: function(projectIdentifier) {
return PathHelper.apiProjectPath(projectIdentifier) + '/queries/available_columns';
},
apiWorkPackagesColumnDataPath: function() {
return PathHelper.apiWorkPackagesPath() + '/column_data';
},
apiStatusesPath: function() {
return PathHelper.apiPrefix + '/statuses'
},
apiUsersPath: function() {
return PathHelper.apiPrefix + PathHelper.usersPath();
},
availableColumnsPath: function(projectIdentifier) {
return PathHelper.projectPath(projectIdentifier) + '/queries/available_columns';
apiWorkPackagesSumsPath: function(projectIdentifier) {
return PathHelper.apiProjectPath(projectIdentifier) + '/column_sums';
},
};

@ -4,7 +4,7 @@ angular.module('openproject.services')
var QueryService = {
getAvailableColumns: function(projectId) {
var url = PathHelper.availableColumnsPath(projectId);
var url = PathHelper.apiAvailableColumnsPath(projectId);
return QueryService.doQuery(url);
},

@ -4,7 +4,7 @@ angular.module('openproject.services')
var StatusService = {
getStatuses: function() {
var url = PathHelper.statusesPath();
var url = PathHelper.apiStatusesPath();
return WorkPackageService.doQuery(url);
},

@ -19,7 +19,7 @@ angular.module('openproject.services')
loadRegisteredUsers: function() {
if (registeredUserIds.length > 0) {
return $http.get(PathHelper.apiPrefix + PathHelper.usersPath(), {
return $http.get(PathHelper.apiUsersPath(), {
params: { 'ids[]': registeredUserIds }
}).then(function(response){
UserService.storeUsers(response.data.users);

@ -4,7 +4,7 @@ angular.module('openproject.services')
var WorkPackageService = {
getWorkPackages: function(projectId, query, paginationOptions) {
var url = projectId ? PathHelper.projectWorkPackagesPath(projectId) : PathHelper.workPackagesPath();
var url = projectId ? PathHelper.apiProjectWorkPackagesPath(projectId) : PathHelper.apiWorkPackagesPath();
var params = angular.extend(query.toParams(), {
page: paginationOptions.page,
per_page: paginationOptions.perPage
@ -14,7 +14,7 @@ angular.module('openproject.services')
},
loadWorkPackageColumnsData: function(workPackages, columnNames) {
var url = PathHelper.workPackagesColumnDataPath();
var url = PathHelper.apiWorkPackagesColumnDataPath();
var params = {
'ids[]': workPackages.map(function(workPackage){
@ -32,7 +32,7 @@ angular.module('openproject.services')
return column.name;
});
var url = PathHelper.workPackagesSumsPath(projectId);
var url = PathHelper.apiWorkPackagesSumsPath(projectId);
var params = {
'column_names[]': columnNames

@ -39,7 +39,8 @@ module Api
/api\/v2\//
end
permeate_permissions :apply_at_timestamp,
permeate_permissions :authorize,
:apply_at_timestamp,
:determine_base,
:find_all_projects_by_project_id,
:find_project_by_project_id,

@ -38,7 +38,8 @@ module Api
/api\/v3\//
end
permeate_permissions :apply_at_timestamp,
permeate_permissions :authorize,
:apply_at_timestamp,
:determine_base,
:find_all_projects_by_project_id,
:find_project_by_project_id,

@ -11,8 +11,9 @@ module Api
include ::Api::V3::ApiController
include ExtendedHTTP
before_filter :authorize_and_setup_project
before_filter :assign_planning_elements
before_filter :authorize_and_setup_project, only: [:index]
before_filter :authorize_request, only: [:column_data]
before_filter :assign_planning_elements, only: [:index]
def index
# the data for the index is already produced in the assign_planning_elements
@ -21,6 +22,29 @@ module Api
end
end
def column_data
raise 'API Error: No IDs' unless params[:ids]
raise 'API Error: No column names' unless params[:column_names]
column_names = params[:column_names]
ids = params[:ids].map(&:to_i)
work_packages = Array.wrap(WorkPackage.visible.find(*ids)).sort {|a,b| ids.index(a.id) <=> ids.index(b.id)}
@columns_data = fetch_columns_data(column_names, work_packages)
end
def column_sums
raise 'API Error' unless params[:column_names]
column_names = params[:column_names]
project = Project.find_visible(current_user, params[:id])
work_packages = project.work_packages
@column_sums = column_names.map do |column_name|
fetch_column_data(column_name, work_packages).map{|c| c.nil? ? 0 : c}.compact.sum if column_should_be_summed_up?(column_name)
end
end
private
def authorize_and_setup_project
@ -28,6 +52,10 @@ module Api
authorize unless performed?
end
def authorize_request
authorize_global unless performed?
end
def assign_planning_elements
@work_packages = current_work_packages(@project) unless performed?
end
@ -89,6 +117,48 @@ module Api
super
end
end
def fetch_columns_data(column_names, work_packages)
column_names.map do |column_name|
fetch_column_data(column_name, work_packages)
end
end
def fetch_column_data(column_name, work_packages)
if column_name =~ /cf_(.*)/
custom_field = CustomField.find($1)
work_packages.map do |work_package|
custom_value = work_package.custom_values.find_by_custom_field_id($1)
custom_field.cast_value custom_value.try(:value)
end
else
work_packages.map do |work_package|
# Note: Doing as_json here because if we just take the value.attributes then we can't get any methods later.
# Name and subject are the default properties that the front end currently looks for to summarize an object.
value = work_package.send(column_name)
value.is_a?(ActiveRecord::Base) ? value.as_json( only: "id", methods: [:name, :subject] ) : value
end
end
end
def column_should_be_summed_up?(column_name)
# see ::Query::Sums mix in
column_is_numeric?(column_name) && Setting.work_package_list_summable_columns.include?(column_name.to_s)
end
def column_is_numeric?(column_name)
# TODO RS: We want to leave out ids even though they are numeric
[:int, :float].include? column_type(column_name)
end
def column_type(column_name)
if column_name =~ /cf_(.*)/
CustomField.find($1).field_format.to_sym
else
column = WorkPackage.columns_hash[column_name]
column.nil? ? :none : column.type
end
end
end
end
end

@ -266,79 +266,6 @@ class WorkPackagesController < ApplicationController
render_404
end
# ------------------- Custom API method -------------------
# TODO Move to API
def column_data
raise 'API Error: No IDs' unless params[:ids]
raise 'API Error: No column names' unless params[:column_names]
column_names = params[:column_names]
ids = params[:ids].map(&:to_i)
work_packages = Array.wrap(WorkPackage.visible.find(*ids)).sort {|a,b| ids.index(a.id) <=> ids.index(b.id)}
render json: fetch_columns_data(column_names, work_packages)
end
def column_sums
# TODO RS: Needs to work for groups, what's the deal?
raise 'API Error' unless params[:column_names]
column_names = params[:column_names]
project = Project.find_visible(current_user, params[:id])
work_packages = project.work_packages
sums = column_names.map do |column_name|
fetch_column_data(column_name, work_packages).map{|c| c.nil? ? 0 : c}.compact.sum if column_should_be_summed_up?(column_name)
end
render json: sums
end
def fetch_columns_data(column_names, work_packages)
column_names.map do |column_name|
fetch_column_data(column_name, work_packages)
end
end
def fetch_column_data(column_name, work_packages)
if column_name =~ /cf_(.*)/
custom_field = CustomField.find($1)
work_packages.map do |work_package|
custom_value = work_package.custom_values.find_by_custom_field_id($1)
custom_field.cast_value custom_value.try(:value)
end
else
work_packages.map do |work_package|
# Note: Doing as_json here because if we just take the value.attributes then we can't get any methods later.
# Name and subject are the default properties that the front end currently looks for to summarize an object.
value = work_package.send(column_name)
value.is_a?(ActiveRecord::Base) ? value.as_json( only: "id", methods: [:name, :subject] ) : value
end
end
end
def column_should_be_summed_up?(column_name)
# see ::Query::Sums mix in
column_is_numeric?(column_name) && Setting.work_package_list_summable_columns.include?(column_name.to_s)
end
def column_is_numeric?(column_name)
# TODO RS: We want to leave out ids even though they are numeric
[:int, :float].include? column_type(column_name)
end
def column_type(column_name)
if column_name =~ /cf_(.*)/
CustomField.find($1).field_format.to_sym
else
column = WorkPackage.columns_hash[column_name]
column.nil? ? :none : column.type
end
end
# ---------------------------------------------------------
def quoted
text, author = if params[:journal_id]
journal = work_package.journals.find(params[:journal_id])

@ -641,7 +641,6 @@ class User < Principal
return true if admin?
initialize_allowance_evaluators
# authorize if user has at least one membership granting this permission
candidates_for_global_allowance.any? do |candidate|
denied = @registered_allowance_evaluators.any? do |evaluator|

@ -0,0 +1,31 @@
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2013 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
object false
node(:columns_data) { @columns_data }

@ -0,0 +1,31 @@
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2013 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
object false
node(:column_sums) { @column_sums }

@ -117,8 +117,13 @@ OpenProject::Application.routes.draw do
end
namespace :v3 do
resources :work_packages, only: [:index] do
get :column_data, on: :collection
end
resources :projects, only: [:show] do
resources :work_packages, only: [:index]
resources :work_packages, only: [:index] do
get :column_sums, on: :collection
end
resources :queries, only: [:show] do
get :available_columns, on: :collection
end

@ -91,6 +91,9 @@ Redmine::AccessControl.map do |map|
:copy_projects => [:copy, :copy_project],
:members => [:paginate_users]
}, :require => :member
map.permission :load_column_data, {
:work_packages => [ :column_data ]
}
map.project_module :work_package_tracking do |map|
# Issue categories
@ -102,7 +105,7 @@ Redmine::AccessControl.map do |map|
:versions => [:index, :show, :status_by],
:journals => [:index, :diff],
:queries => :index,
:work_packages => [:show, :index, :column_data], # TODO move column_data to API
:work_packages => [:show, :index],
:'work_packages/reports' => [:report, :report_details],
:planning_elements => [:index, :all, :show, :recycle_bin],
:planning_element_journals => [:index]}

Loading…
Cancel
Save