Merging upstream version 2.7.4.

parent 7f088830
#
# openSUSE Build Service 2.7.1
#
Updaters from OBS 2.7.0 release can just ugrade the packages
and restart all services. Updaters from former releases should
read the README.UPDATERS file.
This is in first place a bugfix release focusing on security issues
Feature backports:
==================
* none
Changes:
========
* none
Bugfixes:
=========
* [webui][api] Update rails to version 4.2.7.1 to fix CVE-2016-6316 and CVE-2016-6317
* [webui] Users in not 'confirmed' state were allowed to login
* [api] Users in not 'confirmed' state were allowed to run services via former created token
* [backend] Fixing project copy which includes binaries
* [backend] worker supports jobs from OBS 2.8 scheduler
* [backend] support publishing of .vdi (VirtualBox image) files
#
# openSUSE Build Service 2.7.2
#
Updaters from OBS 2.7.0 release can just ugrade the packages
and restart all services. Updaters from former releases should
read the README.UPDATERS file.
This is in first place a bugfix release focusing on security issues
Feature backports:
==================
* none
Changes:
========
* none
Bugfixes:
=========
* [webui][api] Sets bs_request_counter correctly
* [backend] bs_publish: unpublished hook added
#
# openSUSE Build Service 2.7.3
#
Updaters from OBS 2.7.0 release can just ugrade the packages
and restart all services. Updaters from former releases should
read the README.UPDATERS file.
This is in first place a bugfix release focusing on security issues
Feature backports:
==================
* none
Changes:
========
* Compability with OBS 2.8 remote instances
Bugfixes:
=========
* [api] Project meta data was corrupted after undelete
* [api] Raising access and sourceaccess permissions as admin is working again
* [backend] Download on demand sync fixes
* [webui] Fixed revert to a specified source revision
#
# openSUSE Build Service 2.7.4
#
Updaters from OBS 2.7.0 release can just ugrade the packages
and restart all services. Updaters from former releases should
read the README.UPDATERS file.
This release fixes a few bugs and a security issue caused by to loose API
attribute permission checks.
Feature backports:
==================
* none
Changes:
========
* none
Bugfixes:
=========
* [api] Fix API permission check for creating and changing (POST) attributes
* [api] Fix API permission check for deleting (DELETE) attributes
* [webui] Invalidate cached session in LDAP mode
* [api][webui] Fail ldap authentification with empty password
* [webui] Fix repository removal when updating project meta fails with an error
GEM
remote: https://rubygems.org/
specs:
actionmailer (4.2.5.2)
actionpack (= 4.2.5.2)
actionview (= 4.2.5.2)
activejob (= 4.2.5.2)
actionmailer (4.2.7.1)
actionpack (= 4.2.7.1)
actionview (= 4.2.7.1)
activejob (= 4.2.7.1)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 1.0, >= 1.0.5)
actionpack (4.2.5.2)
actionview (= 4.2.5.2)
activesupport (= 4.2.5.2)
actionpack (4.2.7.1)
actionview (= 4.2.7.1)
activesupport (= 4.2.7.1)
rack (~> 1.6)
rack-test (~> 0.6.2)
rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.3)
actionview (4.2.5.2)
activesupport (= 4.2.5.2)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (4.2.7.1)
activesupport (= 4.2.7.1)
builder (~> 3.1)
erubis (~> 2.7.0)
rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.3)
activejob (4.2.5.2)
activesupport (= 4.2.5.2)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
activejob (4.2.7.1)
activesupport (= 4.2.7.1)
globalid (>= 0.3.0)
activemodel (4.2.5.2)
activesupport (= 4.2.5.2)
activemodel (4.2.7.1)
activesupport (= 4.2.7.1)
builder (~> 3.1)
activerecord (4.2.5.2)
activemodel (= 4.2.5.2)
activesupport (= 4.2.5.2)
activerecord (4.2.7.1)
activemodel (= 4.2.7.1)
activesupport (= 4.2.7.1)
arel (~> 6.0)
activesupport (4.2.5.2)
activesupport (4.2.7.1)
i18n (~> 0.7)
json (~> 1.7, >= 1.7.7)
minitest (~> 5.1)
......@@ -184,16 +184,16 @@ GEM
rack (1.6.4)
rack-test (0.6.3)
rack (>= 1.0)
rails (4.2.5.2)
actionmailer (= 4.2.5.2)
actionpack (= 4.2.5.2)
actionview (= 4.2.5.2)
activejob (= 4.2.5.2)
activemodel (= 4.2.5.2)
activerecord (= 4.2.5.2)
activesupport (= 4.2.5.2)
rails (4.2.7.1)
actionmailer (= 4.2.7.1)
actionpack (= 4.2.7.1)
actionview (= 4.2.7.1)
activejob (= 4.2.7.1)
activemodel (= 4.2.7.1)
activerecord (= 4.2.7.1)
activesupport (= 4.2.7.1)
bundler (>= 1.3.0, < 2.0)
railties (= 4.2.5.2)
railties (= 4.2.7.1)
sprockets-rails
rails-deprecated_sanitizer (1.0.3)
activesupport (>= 4.2.0.alpha)
......@@ -205,9 +205,9 @@ GEM
loofah (~> 2.0)
rails_tokeninput (1.7.0)
railties (>= 3.1.0)
railties (4.2.5.2)
actionpack (= 4.2.5.2)
activesupport (= 4.2.5.2)
railties (4.2.7.1)
actionpack (= 4.2.7.1)
activesupport (= 4.2.7.1)
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
rainbow (2.0.0)
......
......@@ -209,12 +209,10 @@ class AttributeController < ApplicationController
render_error :status => 404, :errorcode => "not_found",
:message => "Attribute #{params[:attribute]} does not exist" and return
end
if params[:attribute]
unless User.current.can_create_attribute_in? @attribute_container, namespace: params[:namespace], name: params[:name]
render_error :status => 403, :errorcode => "change_attribute_no_permission",
:message => "user #{user.login} has no permission to change attribute"
return
end
unless User.current.can_create_attribute_in? @attribute_container, namespace: params[:namespace], name: params[:name]
render_error status: 403, errorcode: "change_attribute_no_permission",
message: "user #{user.login} has no permission to change attribute"
return
end
# exec
......@@ -235,26 +233,18 @@ class AttributeController < ApplicationController
req = ActiveXML::Node.new(request.raw_post)
# checks
if params[:attribute]
unless User.current.can_create_attribute_in? @attribute_container, namespace: params[:namespace], name: params[:name]
render_error :status => 403, :errorcode => "change_attribute_no_permission",
:message => "user #{user.login} has no permission to change attribute"
req.each('attribute') do |attr|
begin
can_create = User.current.can_create_attribute_in? @attribute_container, namespace: attr.value('namespace'), name: attr.value('name')
rescue ArgumentError => e
render_error status: 400, errorcode: "change_attribute_attribute_error",
message: e.message
return
end
else
req.each('attribute') do |attr|
begin
can_create = User.current.can_create_attribute_in? @attribute_container, namespace: attr.value('namespace'), name: attr.value('name')
rescue ArgumentError => e
render_error :status => 400, :errorcode => "change_attribute_attribute_error",
:message => e.message
return
end
unless can_create
render_error :status => 403, :errorcode => "change_attribute_no_permission",
:message => "user #{user.login} has no permission to change attribute"
return
end
unless can_create
render_error status: 403, errorcode: "change_attribute_no_permission",
message: "user #{user.login} has no permission to change attribute"
return
end
end
......
......@@ -59,7 +59,7 @@ class RequestController < ApplicationController
# POST /request?cmd=create
def global_command
unless %w(create).include? params[:cmd]
raise UnknownCommandError.new "Unknown command '#{params[opt[:cmd_param]]}' for path #{request.path}"
raise UnknownCommandError.new "Unknown command '#{params[:cmd]}' for path #{request.path}"
end
# refuse request creation for anonymous users
......
......@@ -150,7 +150,11 @@ class SourceController < ApplicationController
return
end
project.check_weak_dependencies!
check_and_remove_repositories!(project.repositories, !params[:remove_linking_repositories].blank?, !params[:force].blank?)
opts = { no_write_to_backend: true,
force: params[:force].present?,
recursive_remove: params[:remove_linking_repositories].present?
}
check_and_remove_repositories!(project.repositories, opts)
logger.info "destroying project object #{project.name}"
project.commit_opts = { comment: params[:comment] }
......@@ -504,7 +508,11 @@ class SourceController < ApplicationController
if project
remove_repositories = project.get_removed_repositories(request_data)
check_and_remove_repositories!(remove_repositories, !params[:remove_linking_repositories].blank?, !params[:force].blank?)
opts = { no_write_to_backend: true,
force: params[:force].present?,
recursive_remove: params[:remove_linking_repositories].present?
}
check_and_remove_repositories!(remove_repositories, opts)
end
Project.transaction do
......@@ -522,13 +530,14 @@ class SourceController < ApplicationController
render_ok
end
def check_and_remove_repositories!(repositories, full_remove, force = false)
error = Project.check_repositories(repositories) unless force
if !force && error[:error]
def check_and_remove_repositories!(repositories, opts)
error = Project.check_repositories(repositories) unless opts[:force]
if !opts[:force] && error[:error]
raise RepoDependency, error[:error]
else
error = Project.remove_repositories(repositories, full_remove)
if !force && error[:error]
error = Project.remove_repositories(repositories, opts)
if !opts[:force] && error[:error]
raise ChangeProjectNoPermission, error[:error]
end
end
......@@ -689,10 +698,10 @@ class SourceController < ApplicationController
return
end
if pkg and not pkg.disabled_for?('sourceaccess', nil, nil)
if FlagHelper.xml_disabled_for?(rdata, 'sourceaccess')
render_error :status => 403, :errorcode => 'change_package_protection_level',
:message => 'admin rights are required to raise the protection level of a package'
if pkg && !pkg.disabled_for?('sourceaccess', nil, nil)
if FlagHelper.xml_disabled_for?(rdata, 'sourceaccess') && !User.current.is_admin?
render_error status: 403, errorcode: 'change_package_protection_level',
message: 'admin rights are required to raise the protection level of a package'
return
end
end
......
......@@ -27,10 +27,17 @@ class TriggerController < ApplicationController
end
pkg = token.package
if pkg
# check if user has still access
unless token.user.is_active? && token.user.can_modify_package?(pkg)
raise NoPermission.new "no permission for package #{pkg.name} in project #{pkg.project.name}"
end
end
unless pkg
# token is not bound to a package, but event may have specified it
pkg = Package.get_by_project_and_name(params[:project].to_s, params[:package].to_s, use_source: true)
unless token.user.can_modify_package? pkg
unless token.user.is_active? && token.user.can_modify_package?(pkg)
raise NoPermission.new "no permission for package #{pkg.name} in project #{pkg.project.name}"
end
end
......
......@@ -278,6 +278,7 @@ class Webui::PackageController < Webui::WebuiController
elsif params[:project].include?(':branches:')
opts[:sourceupdate] = 'update' # Avoid auto-removal of branch
end
opts[:source_rev] = params[:rev] if params[:rev]
action = BsRequestActionSubmit.new(opts)
req.bs_request_actions << action
action.bs_request = req
......
......@@ -708,12 +708,13 @@ class Webui::ProjectController < Webui::WebuiController
Suse::Validator.validate('project', params[:meta])
request_data = Xmlhash.parse(params[:meta])
remove_repositories = @project.get_removed_repositories(request_data)
errors << Project.check_repositories(remove_repositories)[:error]
errors << Project.validate_remote_permissions(request_data)[:error]
errors << Project.validate_link_xml_attribute(request_data, @project.name)[:error]
errors << Project.validate_maintenance_xml_attribute(request_data)[:error]
errors << Project.validate_repository_xml_attribute(request_data, @project.name)[:error]
errors << @project.check_and_remove_repositories(request_data, !params[:remove_linking_repositories].blank?)[:error]
errors = errors.compact
if errors.empty?
......@@ -724,7 +725,7 @@ class Webui::ProjectController < Webui::WebuiController
end
end
rescue Suse::ValidationError => exception
rescue Suse::ValidationError => exception
errors << exception.message
rescue Project::UnknownObjectError => exception
errors << "Project with name '#{exception.message}' not found"
......
......@@ -14,6 +14,7 @@ class Webui::UserController < Webui::WebuiController
def logout
logger.info "Logging out: #{session[:login]}"
Rails.cache.delete("ldap_cache_userpasswd:#{session[:login]}")
reset_session
User.current = nil
if CONFIG['proxy_auth_mode'] == :on
......@@ -27,12 +28,22 @@ class Webui::UserController < Webui::WebuiController
end
def do_login
unless User.authenticate(params[:username], params[:password])
user = User.find_with_credentials(params[:username], params[:password])
if user && !user.is_active?
redirect_to(root_path, error: "Your account is disabled. Please contact the adminsitrator for details.")
return
end
unless user
redirect_to(user_login_path, error: 'Authentication failed')
return
end
session[:login] = User.current.login
Rails.logger.debug "Authentificated user '#{user.try(:login)}'"
session[:login] = user.login
User.current = user
if request.referer.end_with?("/user/login")
redirect_to home_path
......
......@@ -211,7 +211,7 @@ class Webui::WebuiController < ActionController::Base
return
end
# If the user logs in for the first time (before we have a User with that login)
# The user does not exist in our database, create her.
unless User.where(login: user_login).exists?
logger.debug "Creating user #{user_login}"
chars = ["A".."Z", "a".."z", "0".."9"].collect { |r| r.to_a }.join
......@@ -224,9 +224,15 @@ class Webui::WebuiController < ActionController::Base
password_confirmation: fakepw)
end
User.current = User.find_by_login(user_login)
# The user exists, check if shes active and update the info
User.current = User.find_by(login: user_login)
unless User.current.is_active?
session[:login] = nil
User.current = User.find_nobody!
redirect_to(CONFIG['proxy_auth_logout_page'], error: "Your account is disabled. Please contact the adminsitrator for details.")
return
end
User.current.update_user_info_from_proxy_env(request.env)
return
end
User.current = User.find_by_login(session[:login]) if session[:login]
......
......@@ -58,7 +58,7 @@ class BsRequestActionMaintenanceIncident < BsRequestAction
super(opts)
end
def _merge_pkg_into_maintenance_incident(incidentProject, source_project, source_package, releaseproject = nil, request = nil)
def _merge_pkg_into_maintenance_incident(incidentProject)
# recreate package based on link target and throw everything away, except source changes
# silently as maintenance teams requests ...
new_pkg = nil
......@@ -86,17 +86,17 @@ class BsRequestActionMaintenanceIncident < BsRequestAction
end
# use specified release project if defined
elsif releaseproject
elsif target_releaseproject
package_name = source_package
package_name = linkinfo['package'] if linkinfo
branch_params = {:target_project => incidentProject.name,
:olinkrev => 'base',
:maintenance => 1,
:force => 1,
:comment => 'Initial new branch',
:project => releaseproject, :package => package_name}
branch_params[:requestid] = request.id if request
branch_params = {target_project: incidentProject.name,
olinkrev: 'base',
requestid: bs_request.number,
maintenance: 1,
force: 1,
comment: 'Initial new branch',
project: target_releaseproject, package: package_name}
# accept branching from former update incidents or GM (for kgraft case)
linkprj = Project.find_by_name(linkinfo['project']) if linkinfo
if defined?(linkprj) && linkprj
......@@ -117,13 +117,12 @@ class BsRequestActionMaintenanceIncident < BsRequestAction
# linked to an existing package in an external project
linked_project = linkinfo['project']
linked_package = linkinfo['package']
branch_params = {:target_project => incidentProject.name,
:olinkrev => 'base',
:maintenance => 1,
:force => 1,
:project => linked_project, :package => linked_package}
branch_params[:requestid] = request.id if request
branch_params = {target_project: incidentProject.name,
olinkrev: 'base',
requestid: bs_request.number,
maintenance: 1,
force: 1,
project: linked_project, package: linked_package}
ret = BranchPackage.new(branch_params).branch
new_pkg = Package.get_by_project_and_name(ret[:data][:targetproject], ret[:data][:targetpackage])
else
......@@ -142,21 +141,22 @@ class BsRequestActionMaintenanceIncident < BsRequestAction
end
end
# backend copy of current sources, but keep link
# backend copy of submitted sources, but keep link
cp_params = {
cmd: "copy",
user: User.current.login,
oproject: source_project,
opackage: source_package,
requestid: bs_request.number,
keeplink: 1,
expand: 1,
withacceptinfo: 1,
comment: "Maintenance incident copy from project #{source_project}"
}
cp_params[:requestid] = request.number if request
cp_params[:orev] = source_rev if source_rev
cp_path = "/source/#{CGI.escape(incidentProject.name)}/#{CGI.escape(new_pkg.name)}"
cp_path << Suse::Backend.build_query_from_hash(cp_params, [:cmd, :user, :oproject, :opackage,
:keeplink, :expand, :comment,
:orev, :keeplink, :expand, :comment,
:requestid, :withacceptinfo])
result = Suse::Backend.post cp_path, nil
result = Xmlhash.parse(result.body)
......@@ -166,10 +166,10 @@ class BsRequestActionMaintenanceIncident < BsRequestAction
new_pkg
end
def merge_into_maintenance_incident(incidentProject, source_project, source_package, releaseproject = nil, request = nil)
def merge_into_maintenance_incident(incidentProject)
# copy all or selected packages and project source files from base project
# we don't branch from it to keep the link target.
pkg = _merge_pkg_into_maintenance_incident(incidentProject, source_project, source_package, releaseproject, request)
pkg = _merge_pkg_into_maintenance_incident(incidentProject)
incidentProject.save!
incidentProject.store(comment: "maintenance_incident request #{self.bs_request.number}", request: self.bs_request)
......@@ -181,9 +181,7 @@ class BsRequestActionMaintenanceIncident < BsRequestAction
incident_project = Project.get_by_name(self.target_project)
# the incident got created before
self.target_package = merge_into_maintenance_incident(incident_project,
self.source_project, self.source_package,
self.target_releaseproject, self.bs_request)
self.target_package = merge_into_maintenance_incident(incident_project)
# update action with real target project
self.target_project = incident_project.name
......
......@@ -171,7 +171,7 @@ class BsRequestPermissionCheck
end
end
def set_permissions_for_action(action)
def set_permissions_for_action(action, new_state = nil)
# general write permission check on the target on accept
@write_permission_in_this_action = false
......@@ -192,6 +192,12 @@ class BsRequestPermissionCheck
end
end
if action.action_type == :maintenance_incident
# this action type is always branching using extended names
target_package_name = Package.extended_name(action.source_project, action.source_package)
@target_package = @target_project.packages.find_by_name(target_package_name)
end
# general source write permission check (for revoke)
if (@source_package and User.current.can_modify_package?(@source_package, true)) or
(not @source_package and @source_project and User.current.can_modify_project?(@source_project, true))
......@@ -201,7 +207,8 @@ class BsRequestPermissionCheck
# general write permission check on the target on accept
@write_permission_in_this_action = false
# meta data change shall also be allowed after freezing a project using force:
ignoreLock = opts[:force] and [:set_bugowner].include? action.action_type
ignoreLock = (new_state == "declined") ||
(opts[:force] && action.action_type == :set_bugowner)
if @target_package
if User.current.can_modify_package?(@target_package, ignoreLock)
@write_permission_in_target = true
......@@ -349,7 +356,7 @@ class BsRequestPermissionCheck
# permission and validation check for each action inside
req.bs_request_actions.each do |action|
set_permissions_for_action(action)
set_permissions_for_action(action, opts[:newstate])
check_newstate_action! action, opts
......
......@@ -839,6 +839,14 @@ class Package < ActiveRecord::Base
return BsRequest.where(id: rel.pluck('bs_requests.id'))
end
def self.extended_name(project, package)
# the package name which will be used on a branch with extended or maintenance option
directory_hash = Directory.hashed(project: project, package: package)
linkinfo = directory_hash["linkinfo"] || {}
"#{linkinfo['package'] || package}.#{linkinfo['project'] || project}".gsub(/:/, '_')
end
def linkinfo
dir_hash['linkinfo']
end
......
<
......@@ -527,18 +527,18 @@ class Project < ActiveRecord::Base
# check for raising read access permissions, which can't get ensured atm
unless self.new_record? || self.disabled_for?('access', nil, nil)
if FlagHelper.xml_disabled_for?(xmlhash, 'access')
if FlagHelper.xml_disabled_for?(xmlhash, 'access') && !User.current.is_admin?
raise ForbiddenError.new
end
end
unless self.new_record? || self.disabled_for?('sourceaccess', nil, nil)
if FlagHelper.xml_disabled_for?(xmlhash, 'sourceaccess')
if FlagHelper.xml_disabled_for?(xmlhash, 'sourceaccess') && !User.current.is_admin?
raise ForbiddenError.new
end
end
new_record = self.new_record?
if ::Configuration.default_access_disabled == true and not new_record
if self.disabled_for?('access', nil, nil) and not FlagHelper.xml_disabled_for?(xmlhash, 'access')
if ::Configuration.default_access_disabled == true && !new_record
if self.disabled_for?('access', nil, nil) && !FlagHelper.xml_disabled_for?(xmlhash, 'access') && !User.current.is_admin?
raise ForbiddenError.new
end
end
......@@ -1728,16 +1728,6 @@ class Project < ActiveRecord::Base
{}
end
def check_and_remove_repositories(request_data, full_remove = false)
remove_repositories = get_removed_repositories(request_data)
error = Project.check_repositories(remove_repositories)
return error if error[:error]
error = Project.remove_repositories(remove_repositories, full_remove)
error[:error] ? error : {}
end
def get_removed_repositories(request_data)
new_repositories = request_data.elements('repository').map(&:values).flatten
old_repositories = repositories.all.map(&:name)
......@@ -1773,7 +1763,8 @@ class Project < ActiveRecord::Base
{}
end
def self.remove_repositories(repositories, full_remove = false)
# opts: recursive_remove no_write_to_backend
def self.remove_repositories(repositories, opts = {})
deleted_repository = Repository.deleted_instance