1 require "rexml/document"
5 class SourceController < ApplicationController
6 validate_action :index => :directory, :packagelist => :directory, :filelist => :directory
7 validate_action :project_meta => :project, :package_meta => :package, :pattern_meta => :pattern
9 skip_before_filter :extract_user, :only => [:file, :project_meta]
12 # ACL(index): projects with flag 'access' are not listed
20 if params.has_key? :deleted
21 if @http_user.is_admin?
24 render_error :status => 403, :errorcode => 'no_permission_for_deleted',
25 :message => "only admins can see deleted projects"
28 dir = Project.find :all
29 # ACL(projectlist): projects with flag 'access' are not listed
30 accessprjs = DbProject.find( :all, :joins => "LEFT OUTER JOIN flags f ON f.db_project_id = db_projects.id", :conditions => [ "f.flag = 'access'", "ISNULL(f.repo)", "ISNULL(f.architecture_id)"] )
31 accessprjs.each do |prj|
32 dir.delete_element("//entry[@name='#{prj.name}']") if prj.disabled_for?('access', nil, nil) and not @http_user.can_access?(prj)
34 render :text => dir.dump_xml, :content_type => "text/xml"
40 project_name = params[:project]
41 pro = DbProject.find_by_name project_name
42 # ACL(index_project): in case of access, project is really hidden, e.g. does not get listed, accessing says project is not existing
43 if pro and pro.disabled_for?('access', nil, nil) and not @http_user.can_access?(pro)
44 render_error :status => 404, :errorcode => 'unknown_project',
45 :message => "Unknown project '#{project_name}'"
49 unless params[:cmd] == "undelete" or request.get?
50 render_error :status => 404, :errorcode => 'unknown_project',
51 :message => "Unknown project '#{project_name}'"
54 elsif params[:cmd] == "undelete"
55 render_error :status => 403, :errorcode => 'create_project_no_permission',
56 :message => "Can not undelete, project exists already '#{project_name}'"
61 if params.has_key? :deleted
64 # ACL(index_project): private projects appear as empty
65 if pro and pro.enabled_for?('privacy', nil, nil) and not @http_user.can_private_view?(pro)
66 render :text => '<directory count="0"></directory>', :content_type => "text/xml"
69 @dir = Package.find :all, :project => project_name
70 render :text => @dir.dump_xml, :content_type => "text/xml"
78 unless @http_user.can_modify_project?(pro)
79 logger.debug "No permission to delete project #{project_name}"
80 render_error :status => 403, :errorcode => 'delete_project_no_permission',
81 :message => "Permission denied (delete project #{project_name})"
85 #deny deleting if other packages use this as develproject
86 unless pro.develpackages.empty?
87 msg = "Unable to delete project #{pro.name}; following packages use this project as develproject: "
88 msg += pro.develpackages.map {|pkg| pkg.db_project.name+"/"+pkg.name}.join(", ")
89 render_error :status => 400, :errorcode => 'develproject_dependency',
93 #check all packages, if any get refered as develpackage
94 pro.db_packages.each do |pkg|
96 pkg.develpackages do |dpkg|
97 if pro != dpkg.db_project
98 msg += dpkg.db_project.name + "/" + dkg.name + ", "
102 render_error :status => 400, :errorcode => 'develpackage_dependency',
103 :message => "Unable to delete package #{pkg.name}; following packages use this package as devel package: #{msg}"
110 pro.repositories.each do |repo|
111 repo.linking_repositories.each do |lrep|
117 if params[:force] and not params[:force].empty?
118 #replace links to this projects with links to the "deleted" project
119 del_repo = DbProject.find_by_name("deleted").repositories[0]
120 lreps.each do |link_rep|
121 link_rep.path_elements.find(:all).each { |pe| pe.destroy }
122 link_rep.path_elements.create(:link => del_repo, :position => 1)
125 link_rep.db_project.store
128 lrepstr = lreps.map{|l| l.db_project.name+'/'+l.name}.join "\n"
129 render_error :status => 403, :errorcode => "repo_dependency",
130 :message => "Unable to delete project #{project_name}; following repositories depend on this project:\n#{lrepstr}\n"
135 #destroy all packages
136 pro.db_packages.each do |pack|
137 DbPackage.transaction do
138 logger.info "destroying package #{pack.name}"
143 logger.info "destroying project #{pro.name}"
146 logger.debug "delete request to backend: /source/#{pro.name}"
147 Suse::Backend.delete "/source/#{pro.name}"
155 unless @http_user.can_create_project?(project_name)
156 render_error :status => 403, :errorcode => "cmd_execution_no_permission",
157 :message => "no permission to execute command '#{cmd}'"
162 # read meta data from backend to restore database object
163 path = request.path + "/_meta"
164 Project.new(backend_get(path)).save
166 # restore all package meta data objects in DB
167 backend_pkgs = Collection.find :package, :match => "@project='#{params[:project]}'"
168 backend_pkgs.each_package do |package|
169 path = request.path + "/" + package.name + "/_meta"
170 Package.new(backend_get(path), :project => params[:project]).save
175 if @http_user.can_modify_project?(pro) or cmd == "showlinked"
178 render_error :status => 403, :errorcode => "cmd_execution_no_permission",
179 :message => "no permission to execute command '#{cmd}'"
183 render_error :status => 400, :errorcode => "illegal_request",
184 :message => "illegal POST request to #{request.request_uri}"
188 # FIXME: for OBS 3, api of branch and copy calls have target and source in the opossite place
190 valid_http_methods :get, :delete, :post
191 required_parameters :project, :package
193 deleted = params.has_key? :deleted
195 # list of commands which are allowed even when the project has the package only via a project link
196 read_commands = ['diff', 'branch', 'linkdiff', 'showlinked']
198 # find out about source and target dependening on command
200 origin_project_name = params[:project]
201 target_package_name = origin_package_name = params[:package]
202 target_project_name = params[:target_project] if params[:target_project]
203 target_package_name = params[:target_package] if params[:target_package]
205 origin_project_name = target_project_name = params[:project]
206 origin_package_name = target_package_name = params[:package]
207 origin_project_name = params[:oproject] if params[:oproject]
208 origin_package_name = params[:opackage] if params[:opackage]
210 if origin_package_name and not origin_project_name
211 render_error :status => 404, :errorcode => "missing_argument",
212 :message => "origin package name is specified, but no origin project"
215 # FIXME: not found error messages needs to be defined in one place to avoid the risk that a typo can
216 # be used to find out about existens
217 unknownTargetPackageError = "Unknown package '#{target_package_name}' in project '#{target_project_name}'"
218 unknownTargetProjectError = "Unknown project '#{target_project_name}'"
220 # test read access from origin package if specified, ignore it when it does not exist, assuming a remote package
221 if (origin_package_name)
222 sprj = DbProject.find_by_name(origin_project_name)
223 spkg = sprj.find_package(origin_package_name) if sprj
225 # ACL(index_package): access behaves like package / project not existing
226 if spkg and spkg.disabled_for?('access', nil, nil) and not @http_user.can_access?(spkg)
227 render_error :status => 404, :errorcode => 'unknown_package',
228 :message => "Unknown package '#{origin_package_name}' in project '#{origin_project_name}'"
232 # ACL(index_package): source access gives permisson denied
233 if spkg and spkg.disabled_for?('sourceaccess', nil, nil) and not @http_user.can_source_access?(spkg)
234 render_error :status => 403, :errorcode => "source_access_no_permission",
235 :message => "no read access to package #{origin_package_name} in project #{origin_project_name}"
241 prj = DbProject.find_by_name(target_project_name)
243 # FIXME3.0: disallow the "rebuild" via /source/
244 if request.get? or ( request.post? and read_commands.include?(cmd) ) or ( request.post? and cmd == "rebuild" )
245 # include project links on get or for diff and branch command
246 pkg = prj.find_package(target_package_name)
248 # allow operations only for local packages
249 pkg = prj.db_packages.find_by_name(target_package_name)
252 if target_project_name and not (request.post? and [ "branch", "showlinked" ].include?(cmd) )
253 # Check if this is a package via project link to a remote OBS instance
254 answer = Suse::Backend.get("/source/#{CGI.escape(target_project_name)}")
259 path << build_query_from_hash(params, [:rev, :linkrev, :emptylink, :expand, :view, :extension, :lastworking, :withlinked, :meta, :deleted])
264 render_error :status => 404, :errorcode => "unknown_project",
265 :message => unknownTargetProjectError
270 # ACL(index_package): in case of access, package is really hidden and shown as non existing to users without access
271 if pkg and pkg.disabled_for?('access', nil, nil) and not @http_user.can_access?(pkg)
272 render_error :status => 404, :errorcode => 'unknown_package',
273 :message => unknownTargetPackageError
277 # ACL(index_package): if private view is on behave like pkg without any src files
278 if pkg and pkg.enabled_for?('privacy', nil, nil) and not @http_user.can_private_view?(pkg)
283 # ACL(index_package): source access gives permisson denied
284 if pkg and pkg.disabled_for?('sourceaccess', nil, nil) and not @http_user.can_source_access?(pkg)
285 render_error :status => 403, :errorcode => "source_access_no_permission",
286 :message => "no read access to package #{pkg.name} in project #{pkg.db_project.name}"
290 if request.post? and cmd == "showlinked"
295 # package may not exist currently here, but can we work anyway with it ?
297 if deleted and request.get? and prj and not prj.disabled_for?('sourceaccess', nil, nil) and not pkg
298 # load last package meta file and just check if sourceaccess flag was used at all, no per user checking atm
300 r = Suse::Backend.get("/source/#{CGI.escape(target_project_name)}/#{target_package_name}/_history?deleted=1&meta=1")
305 data = ActiveXML::XMLNode.new(r.body.to_s)
307 data.each_revision {|rev| lastrev = rev}
308 srcmd5 = lastrev.value("srcmd5")
309 metapath = "/source/#{CGI.escape(target_project_name)}/#{target_package_name}/_meta?rev=#{srcmd5}"
310 r = Suse::Backend.get(metapath)
312 dpkg = Package.new(r.body)
313 if dpkg and dpkg.disabled_for? 'sourceaccess'
319 # validate if package exists in db, except when working on deleted package sources
320 unless deleted.blank? and not request.delete? and not dpkg
321 unless target_package_name == "_project" or pkg or DbProject.find_remote_project(target_project_name)
322 render_error :status => 404, :errorcode => "unknown_package",
323 :message => unknownTargetPackageError
330 path << build_query_from_hash(params, [:rev, :linkrev, :emptylink, :expand, :view, :extension, :lastworking, :withlinked, :meta, :deleted])
333 elsif request.delete?
334 if target_package_name == "_project"
335 render_error :status => 403, :errorcode => "delete_package_no_permission",
336 :message => "_project package can not be deleted."
340 # ACL: check if user is allowed to delete package
341 if not @http_user.can_modify_package?(pkg)
342 render_error :status => 403, :errorcode => "delete_package_no_permission",
343 :message => "no permission to delete package #{target_package_name}"
347 #deny deleting if other packages use this as develpackage
348 # Shall we offer a --force option here as well ?
349 # Shall we ask the other package owner accepting to be a devel package ?
350 unless pkg.develpackages.empty?
351 msg = "Unable to delete package #{pkg.name}; following packages use this package as devel package: "
352 msg += pkg.develpackages.map {|dp| dp.db_project.name+"/"+dp.name}.join(", ")
353 render_error :status => 400, :errorcode => 'develpackage_dependency',
358 DbPackage.transaction do
360 Suse::Backend.delete "/source/#{target_project_name}/#{target_package_name}"
361 if target_package_name == "_product"
362 update_product_autopackages params[:project]
367 if ['undelete'].include?(cmd)
368 # ACL: check if user is allowed to undelete package
369 unless @http_user.can_modify_project?(prj)
370 render_error :status => 403, :errorcode => "cmd_execution_no_permission",
371 :message => "no permission to execute command '#{cmd}'"
376 # read meta data from backend to restore database object
377 path = request.path + "/_meta"
378 Package.new(backend_get(path), :project => params[:project]).save
382 package_creating_commands = [ "branch", "copy" ]
383 if pkg.nil? and not package_creating_commands.include?(cmd)
384 render_error :status => 404, :errorcode => 'unknown_package',
385 :message => unknownTargetPackageError
389 # ACL: check if user is allowed to modify project, if package is not existing yet
390 if prj and pkg.nil? and package_creating_commands.include?(cmd)
391 unless @http_user.can_create_package_in?(prj)
392 render_error :status => 403, :errorcode => "cmd_execution_no_permission",
393 :message => "no permission to execute command '#{cmd}' creating not existing package"
396 elsif pkg and not read_commands.include?(cmd) and not @http_user.can_modify_package?(pkg)
397 # ACL: check if user is allowed to modify existing package if command is changing source
398 render_error :status => 403, :errorcode => "cmd_execution_no_permission",
399 :message => "no permission to execute command '#{cmd}'"
407 # /source/:project/_attribute/:attribute
408 # /source/:project/:package/_attribute/:attribute
409 # /source/:project/:package/:binary/_attribute/:attribute
411 valid_http_methods :get, :post, :delete
412 params[:user] = @http_user.login if @http_user
415 binary=params[:binary] if params[:binary]
418 @attribute_container = DbPackage.find_by_project_and_name(params[:project], params[:package])
419 unless @attribute_container
420 render_error :message => "Unknown package '#{params[:project]}/#{params[:package]}'",
421 :status => 404, :errorcode => "unknown_package"
425 @attribute_container = DbProject.find_by_name(params[:project])
426 unless @attribute_container
427 render_error :message => "Unknown project '#{params[:project]}'",
428 :status => 404, :errorcode => "unknown_project"
433 # ACL(attribute_meta): in case of access, project or package is really hidden
434 if @attribute_container and (@attribute_container.disabled_for?('access', nil, nil) and not @http_user.can_access?(@attribute_container))
436 render_error :message => "Unknown package '#{params[:project]}/#{params[:package]}'",
437 :status => 404, :errorcode => "unknown_package"
440 render_error :message => "Unknown project '#{params[:project]}'",
441 :status => 404, :errorcode => "unknown_project"
446 # is the attribute type defined at all ?
447 if params[:attribute]
448 at = AttribType.find_by_name(params[:attribute])
450 render_error :status => 403, :errorcode => "not_existing_attribute",
451 :message => "Attribute is not defined in system"
457 params[:binary]=binary if binary
458 render :text => @attribute_container.render_attribute_axml(params), :content_type => 'text/xml'
464 req = BsRequest.new(request.body.read)
465 req.data # trigger XML parsing
466 rescue ActiveXML::ParseError => e
467 render_error :message => "Invalid XML",
468 :status => 400, :errorcode => "invalid_xml"
473 # permission checking
474 if params[:attribute]
475 aname = params[:attribute]
476 name_parts = aname.split(/:/)
477 if name_parts.length != 2
478 raise ArgumentError, "attribute '#{aname}' must be in the $NAMESPACE:$NAME style"
480 unless @http_user.can_create_attribute_in? @attribute_container, :namespace => name_parts[0], :name => name_parts[1]
481 render_error :status => 403, :errorcode => "change_attribute_no_permission",
482 :message => "user #{user.login} has no permission to change attribute"
487 req.each_attribute do |attr|
489 can_create = @http_user.can_create_attribute_in? @attribute_container, :namespace => attr.namespace, :name => attr.name
490 rescue ActiveRecord::RecordNotFound => e
491 render_error :status => 404, :errorcode => "not_found",
492 :message => e.message
494 rescue ArgumentError => e
495 render_error :status => 400, :errorcode => "change_attribute_attribute_error",
496 :message => e.message
500 render_error :status => 403, :errorcode => "change_attribute_no_permission",
501 :message => "user #{user.login} has no permission to change attribute"
506 render_error :status => 403, :errorcode => "internal_error",
507 :message => "INTERNAL ERROR: unhandled request"
514 req.each_attribute do |attr|
516 @attribute_container.store_attribute_axml(attr, binary)
517 rescue DbProject::SaveError => e
518 render_error :status => 403, :errorcode => "save_error", :message => e.message
520 rescue DbPackage::SaveError => e
521 render_error :status => 403, :errorcode => "save_error", :message => e.message
525 @attribute_container.store
527 elsif request.delete?
528 ac = @attribute_container.find_attribute(name_parts[0], name_parts[1],binary)
530 render_error :status => 404, :errorcode => "not_found",
531 :message => "Attribute #{aname} does not exist" and return
534 @attribute_container.store
537 render_error :message => "INTERNAL ERROR: Unhandled operation",
538 :status => 404, :errorcode => "unknown_operation"
542 # /source/:project/_pattern/:pattern
544 valid_http_methods :get, :put, :delete
546 params[:user] = @http_user.login if @http_user
548 @project = DbProject.find_by_name params[:project]
550 render_error :message => "Unknown project '#{params[:project]}'",
551 :status => 404, :errorcode => "unknown_project"
555 # ACL(pattern_meta): in case of access, project or package is really hidden
556 if @project.disabled_for?('access', nil, nil) and not @http_user.can_access?(@project)
557 render_error :message => "Unknown project '#{params[:project]}'",
558 :status => 404, :errorcode => "unknown_project"
568 permerrormsg = "no permission to store pattern"
569 elsif request.delete?
570 permerrormsg = "no permission to delete pattern"
573 unless @http_user.can_modify_project? @project
574 logger.debug "user #{user.login} has no permission to modify project #{@project}"
575 render_error :status => 403, :errorcode => "change_project_no_permission",
576 :message => permerrormsg
580 path = request.path + build_query_from_hash(params, [:rev, :user, :comment])
585 # GET /source/:project/_pattern
587 valid_http_methods :get
589 @project = DbProject.find_by_name(params[:project])
591 render_error :message => "Unknown project '#{params[:project]}'",
592 :status => 404, :errorcode => "unknown_project"
596 # ACL(index_pattern): in case of access, project or package is really hidden
597 if @project.disabled_for?('access', nil, nil) and not @http_user.can_access?(@project)
598 render_error :message => "Unknown project '#{params[:project]}'",
599 :status => 404, :errorcode => "unknown_project"
607 valid_http_methods :get, :put
608 required_parameters :project
610 project_name = params[:project]
612 return unless extract_user
613 params[:user] = @http_user.login
615 unless project_name and valid_project_name? project_name
616 render_error :status => 400, :errorcode => "invalid_project_name",
617 :message => "invalid project name '#{project_name}'"
621 # ACL(project_meta): if access is set, this behaves like project non existing
623 flag_sourceaccess = false
624 @project = DbProject.find_by_name( project_name )
626 flag_access = @project.disabled_for?('access', nil, nil)
627 flag_sourceaccess = @project.disabled_for?('sourceaccess', nil, nil)
628 if flag_access and not @http_user.can_access?(@project)
629 render_error :message => "Unknown project '#{project_name}'",
630 :status => 404, :errorcode => "unknown_project"
637 render :text => @project.to_axml(params[:view]), :content_type => 'text/xml'
638 elsif DbProject.find_remote_project(project_name)
639 # project from remote buildservice, get metadata from backend
642 render_error :message => "Unknown project '#{project_name}'",
643 :status => 404, :errorcode => "unknown_project"
648 #assemble path for backend
650 path += build_query_from_hash(params, [:user, :comment, :rev])
655 logger.debug "Checking permission for the put"
657 request_data = request.raw_post
659 @project = DbProject.find_by_name( project_name )
661 #project exists, change it
662 unless @http_user.can_modify_project? @project
663 logger.debug "user #{user.login} has no permission to modify project #{@project.name}"
664 render_error :status => 403, :errorcode => "change_project_no_permission",
665 :message => "no permission to change project"
670 unless @http_user.can_create_project? project_name
671 logger.debug "Not allowed to create new project"
672 render_error :status => 403, :errorcode => 'create_project_no_permission',
673 :message => "not allowed to create new project '#{project_name}'"
678 # ACL(project_meta): the following code checks if the target project of a linked project exists or is ACL protected, skip remote projects
679 rdata = REXML::Document.new(request.raw_post.to_s)
680 rdata.elements.each("project/link") do |e|
681 # ACL(project_meta) TODO: check if project linking check cannot be circumvented
682 tproject_name = e.attributes["project"]
683 tprj = DbProject.find_by_name(tproject_name)
686 if not DbProject.find_remote_project(tproject_name)
687 render_error :status => 404, :errorcode => 'not_found',
688 :message => "The link target project #{tproject_name} does not exist"
692 # ACL(project_meta): project link to project with access behaves like target project not existing
693 if tprj.disabled_for?('access', nil, nil) and not @http_user.can_access?(tprj)
694 render_error :status => 404, :errorcode => 'not_found',
695 :message => "The link target project #{tproject_name} does not exist"
700 logger.debug "project #{project_name} link checked against #{tproject_name} projects permission"
703 # ACL(project_meta): the following code checks if a repository path is to protected project, skip remote projects
704 rdata.elements.each("project/repository/path") do |e|
705 # ACL(project_meta) TODO: check if repository check cannot be circumvented
706 tproject_name = e.attributes["project"]
707 tprj = DbProject.find_by_name(tproject_name)
709 if not DbProject.find_remote_project(tproject_name)
710 render_error :status => 404, :errorcode => 'not_found',
711 :message => "The link target project #{tproject_name} does not exist"
715 # ACL(project_meta): project link to project with access behaves like target project not existing
716 if tprj.disabled_for?('access', nil, nil) and not @http_user.can_access?(tprj)
717 render_error :status => 404, :errorcode => 'not_found',
718 :message => "The project #{tproject_name} does not exist"
722 # FIXME2.1: or to be discussed. This is currently a regression. It was wanted so far that it is still
723 # possible to build against a path, where binary download was not possible.
724 # ACL(project_meta): project link to project with binarydownload gives permisson denied
725 # if tprj.disabled_for?('binarydownload', nil, nil) and not @http_user.can_download_binaries?(tprj)
726 # render_error :status => 403, :errorcode => "binary_download_no_permission",
727 # :message => "No permission for a repository path to project #{tproject_name}"
731 # ACL(project_meta): check that user does not link an unprotected project to a protected project
733 if (tprj.disabled_for?('access', nil, nil) and @project.enabled_for?('access', nil, nil)) or
734 (tprj.disabled_for?('binarydownload', nil, nil) and @project.enabled_for?('access', nil, nil) and
735 @project.enabled_for?('binarydownload', nil, nil))
736 render_error :status => 403, :errorcode => "binary_download_no_permission" ,
737 :message => "repository path from a insufficiently protected project #{project_name} to a protected project #{tproject_name}"
740 elsif @project.nil? and (tprj.disabled_for?('access', nil, nil) or tprj.disabled_for?('binarydownload', nil, nil))
741 render_error :status => 403, :errorcode => "binary_download_no_permission" ,
742 :message => "cannot check permission of project #{project_name} which must preexist"
747 logger.debug "project #{project_name} repository path checked against #{tproject_name} projects permission"
750 p = Project.new(request_data, :name => project_name)
751 if @project and not @project.disabled_for?('sourceaccess', nil, nil) and not @http_user.is_admin?
752 if p.disabled_for? :sourceaccess
753 render_error :status => 403, :errorcode => "change_project_protection_level",
754 :message => "admin rights are required to raise the source protection level of a project"
759 if @project and not @project.disabled_for?('access', nil, nil) and not @http_user.is_admin?
760 if p.disabled_for? :access
761 render_error :status => 403, :errorcode => "change_project_protection_level",
762 :message => "admin rights are required to raise the protection level of a project"
767 if p.name != project_name
768 render_error :status => 400, :errorcode => 'project_name_mismatch',
769 :message => "project name in xml data does not match resource path component"
773 if (p.has_element? :remoteurl or p.has_element? :remoteproject) and not @http_user.is_admin?
774 render_error :status => 403, :errorcode => "change_project_no_permission",
775 :message => "admin rights are required to change remoteurl or remoteproject"
779 p.add_person(:userid => @http_user.login) unless @project
784 render_error :status => 400, :errorcode => 'illegal_request',
785 :message => "Illegal request: POST #{request.path}"
790 valid_http_methods :get, :put
792 #check if project exists
793 unless (prj = DbProject.find_by_name(params[:project]))
794 render_error :status => 404, :errorcode => 'project_not_found',
795 :message => "Unknown project #{params[:project]}"
799 #assemble path for backend
800 params[:user] = @http_user.login
802 # ACL(project_config): in case of access, project is really hidden, accessing says project is not existing
803 if prj.disabled_for?('access', nil, nil) and not @http_user.can_access?(prj)
804 render_error :status => 404, :errorcode => 'project_not_found',
805 :message => "Unknown project #{params[:project]}"
811 path += build_query_from_hash(params, [:rev])
816 #assemble path for backend
818 path += build_query_from_hash(params, [:user, :comment])
821 unless @http_user.can_modify_project?(prj)
822 render_error :status => 403, :errorcode => 'put_project_config_no_permission',
823 :message => "No permission to write build configuration for project '#{params[:project]}'"
830 render_error :status => 400, :errorcode => 'illegal_request',
831 :message => "Illegal request: #{request.path}"
835 valid_http_methods :get, :delete
837 #assemble path for backend
838 params[:user] = @http_user.login if request.delete?
840 path += build_query_from_hash(params, [:user, :comment, :rev])
842 #check if project exists
843 unless prj = DbProject.find_by_name(params[:project])
844 prj, pro_name = DbProject.find_remote_project(params[:project])
845 unless request.get? and prj
846 render_error :status => 404, :errorcode => "project_not_found",
847 :message => "Unknown project '#{params[:project]}'"
852 # ACL(project_pubkey): in case of access, project or package is really hidden
853 if prj and prj.disabled_for?('access', nil, nil) and not @http_user.can_access?(prj)
854 render_error :message => "Unknown project '#{params[:project]}'",
855 :status => 404, :errorcode => "project_not_found"
861 elsif request.delete?
862 #check for permissions
863 unless @http_user.can_modify_project?(prj)
864 render_error :status => 403, :errorcode => 'delete_project_pubkey_no_permission',
865 :message => "No permission to delete public key for project '#{params[:project]}'"
875 valid_http_methods :put, :get
876 required_parameters :project, :package
878 project_name = params[:project]
879 package_name = params[:package]
881 unless pro = DbProject.find_by_name(project_name)
882 pro, pro_name = DbProject.find_remote_project(project_name)
883 unless request.get? and pro
884 render_error :status => 404, :errorcode => "unknown_project",
885 :message => "Unknown project '#{project_name}'"
890 unless valid_package_name? package_name
891 render_error :status => 400, :errorcode => "invalid_package_name",
892 :message => "invalid package name '#{package_name}'"
896 pack = pro.find_package( package_name )
898 # ACL(package_meta): in case of access, project is really hidden, accessing says project is not existing
899 if pack and (pack.disabled_for?('access', nil, nil) and not @http_user.can_access?(pack))
900 render_error :message => "Unknown package '#{project_name}/#{package_name}'",
901 :status => 404, :errorcode => "unknown_package"
906 if pack.nil? or params.has_key?(:rev)
907 # check if this comes from a remote project, also true for _project package
908 # or if rev it specified we need to fetch the meta from the backend
909 answer = Suse::Backend.get(request.path)
911 render :text => answer.body.to_s, :content_type => 'text/xml'
913 render_error :status => 404, :errorcode => "unknown_package",
914 :message => "Unknown package '#{package_name}'"
919 render :text => pack.to_axml(params[:view]), :content_type => 'text/xml'
921 update_package_meta(project_name, package_name, request.raw_post, @http_user.login, params[:comment])
926 valid_http_methods :get, :delete, :put
927 project_name = params[:project]
928 package_name = params[:package]
930 path = "/source/#{CGI.escape(project_name)}/#{CGI.escape(package_name)}/#{CGI.escape(file)}"
933 return unless extract_user
934 params[:user] = @http_user.login
936 prj = DbProject.find_by_name(project_name)
937 pack = DbPackage.find_by_project_and_name(project_name, package_name)
938 if package_name == "_project"
940 render_error :status => 403, :errorcode => 'not_found',
941 :message => "The given project #{project_name} does not exist"
944 allowed = permissions.project_change? prj
946 if prj and pack.nil? and request.get?
947 # find package instance of linked local project
948 pack = prj.find_package(package_name)
950 if pack.nil? and request.get?
951 # Check if this is a package on a remote OBS instance
952 answer = Suse::Backend.get(request.path)
958 if pack.nil? and package_name != "_project"
959 render_error :status => 403, :errorcode => 'not_found',
960 :message => "The given package #{package_name} does not exist in project #{project_name}"
963 allowed = permissions.package_change? pack
965 # ACL(file): access behaves like project not existing
966 if pack.disabled_for?('access', nil, nil) and not @http_user.can_access?(pack)
967 render_error :status => 404, :errorcode => 'not_found',
968 :message => "The given package #{package_name} does not exist in project #{project_name}"
972 # ACL(file): source access gives permisson denied
973 if pack.disabled_for?('sourceaccess', nil, nil) and not @http_user.can_source_access?(pack)
974 render_error :status => 403, :errorcode => "source_access_no_permission",
975 :message => "no read access to package #{package_name}, project #{project_name}"
981 path += build_query_from_hash(params, [:rev, :meta])
986 # ACL(file): lookup via :rev / :linkrev is prevented now by backend if $nosharedtrees is set
988 path += build_query_from_hash(params, [:user, :comment, :rev, :linkrev, :keeplink, :meta])
991 # file validation where possible
992 if params[:file] == "_link"
993 validator = Suse::Validator.new( "link" )
994 validator.validate(request)
995 elsif params[:file] == "_aggregate"
996 validator = Suse::Validator.new( "aggregate" )
997 validator.validate(request)
1000 # ACL(file): the following code checks if link or aggregate
1001 if params[:file] == "_aggregate"
1002 data = REXML::Document.new(request.raw_post.to_s)
1003 data.elements.each("aggregatelist/aggregate") do |e|
1004 # ACL(file) TODO: check if the _aggregate check cannot be circumvented somehow
1005 tproject_name = e.attributes["project"]
1006 tprj = DbProject.find_by_name(tproject_name)
1008 if not DbProject.find_remote_project(tproject_name)
1009 render_error :status => 404, :errorcode => 'not_found',
1010 :message => "The given #{tproject_name} does not exist"
1014 # ACL(file): _aggregate access behaves like project not existing
1015 if tprj.disabled_for?('access', nil, nil) and not @http_user.can_access?(tprj)
1016 render_error :status => 404, :errorcode => 'not_found',
1017 :message => "The project #{tproject_name} does not exist"
1021 # ACL(file): _aggregate binarydownload denies access to repositories
1022 if tprj.disabled_for?('binarydownload', nil, nil) and not @http_user.can_download_binaries?(tprj)
1023 render_error :status => 403, :errorcode => "download_binary_no_permission",
1024 :message => "No permission to _aggregate binaries from project #{params[:project]}"
1028 # ACL(file): check that user does not aggregate an unprotected project to a protected project
1030 if (tprj.disabled_for?('access', nil, nil) and prj.enabled_for?('access', nil, nil)) or
1031 (tprj.disabled_for?('binarydownload', nil, nil) and prj.enabled_for?('access', nil, nil) and
1032 prj.enabled_for?('binarydownload', nil, nil))
1033 render_error :status => 403, :errorcode => "binary_download_no_permission" ,
1034 :message => "aggregate with an unprotected project #{project_name} to a protected project #{tproject_name}"
1040 logger.debug "_aggregate checked for #{tproject_name} project permission"
1042 elsif params[:file] == "_link"
1043 data = REXML::Document.new(request.raw_post.to_s)
1044 data.elements.each("link") do |e|
1045 tproject_name = e.attributes["project"]
1046 tpackage_name = e.attributes["package"]
1047 tproject_name = project_name if tproject_name.blank?
1048 tpackage_name = package_name if tpackage_name.blank?
1049 tprj = DbProject.find_by_name(tproject_name)
1051 # link to remote project ?
1052 unless tprj = DbProject.find_remote_project(tproject_name)
1053 render_error :status => 404, :errorcode => 'not_found',
1054 :message => "The given project #{tproject_name} does not exist"
1058 tpkg = tprj.find_package(tpackage_name)
1060 # check if this is a package on a remote OBS instance
1062 answer = Suse::Backend.get("/source/#{URI.escape tproject_name}/#{URI.escape tpackage_name}/_meta")
1064 render_error :status => 404, :errorcode => 'not_found',
1065 :message => "The given package #{tpackage_name} does not exist in project #{tproject_name}"
1070 # ACL(file): _link access behaves like project not existing
1071 if tpkg and tpkg.disabled_for?('access', nil, nil) and not @http_user.can_access?(tpkg)
1072 render_error :status => 404, :errorcode => 'not_found',
1073 :message => "The given package #{tpackage_name} does not exist in project #{tproject_name}"
1077 # ACL(file): _link sourceaccess gives permisson denied
1078 if tpkg and tpkg.disabled_for?('sourceaccess', nil, nil) and not @http_user.can_source_access?(tpkg)
1079 render_error :status => 403, :errorcode => "source_access_no_permission",
1080 :message => "No permission to _link to package #{tpackage_name} at project #{tproject_name}"
1084 # ACL(file): check that user does not link an unprotected package to a protected package
1086 if (tpkg.disabled_for?('access', nil, nil) and pack.enabled_for?('access', nil, nil))
1087 render_error :status => 403, :errorcode => "access_no_permission" ,
1088 :message => "linking an unprotected package #{package_name}/#{project_name} to a protected package #{tpackage_name}/#{tproject_name}"
1091 if (tpkg.disabled_for?('sourceaccess', nil, nil) and pack.enabled_for?('sourceaccess', nil, nil))
1092 render_error :status => 403, :errorcode => "source_access_no_permission" ,
1093 :message => "linking an unprotected package #{package_name}/#{project_name} to a protected package #{tpackage_name}/#{tproject_name}"
1098 logger.debug "_link checked against #{tpackage_name} in #{tproject_name} package permission"
1103 pass_to_backend path
1104 pack.update_timestamp
1105 if package_name == "_product"
1106 update_product_autopackages params[:project]
1109 render_error :status => 403, :errorcode => 'put_file_no_permission',
1110 :message => "Insufficient permissions to store file in package #{package_name}, project #{project_name}"
1112 elsif request.delete?
1113 path += build_query_from_hash(params, [:user, :comment, :rev, :linkrev, :keeplink])
1116 Suse::Backend.delete path
1117 pack = DbPackage.find_by_project_and_name(project_name, package_name)
1118 pack.update_timestamp
1119 if package_name == "_product"
1120 update_product_autopackages params[:project]
1124 render_error :status => 403, :errorcode => 'delete_file_no_permission',
1125 :message => "Insufficient permissions to delete file"
1132 def update_package_meta(project_name, package_name, request_data, user=nil, comment=nil)
1133 pkg = DbPackage.find_by_project_and_name(project_name, package_name)
1136 # Being here means that the package already exists
1137 unless permissions.package_change? pkg
1138 logger.debug "user #{user} has no permission to change package #{package_name}"
1139 render_error :status => 403, :errorcode => "change_package_no_permission",
1140 :message => "no permission to change package"
1144 # Ok, the package is new
1145 unless permissions.package_create?( project_name )
1146 # User is not allowed by global permission.
1147 logger.debug "Not allowed to create new packages"
1148 render_error :status => 403, :errorcode => "create_package_no_permission",
1149 :message => "no permission to create package for project #{project_name}"
1154 @package = Package.new( request_data, :project => project_name, :name => package_name )
1156 if pkg and not pkg.disabled_for?('sourceaccess', nil, nil)
1157 if @package.disabled_for? :sourceaccess
1158 render_error :status => 403, :errorcode => "change_package_protection_level",
1159 :message => "admin rights are required to raise the protection level of a package"
1164 if pkg and not pkg.disabled_for?('access', nil, nil)
1165 if @package.disabled_for? :access
1166 render_error :status => 403, :errorcode => "change_package_protection_level",
1167 :message => "admin rights are required to raise the protection level of a package"
1172 if( @package.name != package_name )
1173 render_error :status => 400, :errorcode => 'package_name_mismatch',
1174 :message => "package name in xml data does not match resource path component"
1180 rescue DbPackage::CycleError => e
1181 render_error :status => 400, :errorcode => 'devel_cycle', :message => e.message
1188 # POST /source?cmd=branch
1191 unless params[:attribute]
1192 params[:attribute] = "OBS:Maintained"
1194 unless params[:update_project_attribute]
1195 params[:update_project_attribute] = "OBS:UpdateProject"
1197 unless params[:target_project]
1199 params[:target_project] = "home:#{@http_user.login}:branches:REQUEST_#{params[:request]}"
1201 params[:target_project] = "home:#{@http_user.login}:branches:#{params[:attribute].gsub(':', '_')}"
1202 params[:target_project] += ":#{params[:package]}" if params[:package]
1206 # find packages to be branched
1209 # find packages from request
1210 data = Suse::Backend.get("/request/#{params[:request]}").body
1211 req = BsRequest.new(data)
1213 req.each_action do |action|
1216 if action.has_element? 'source'
1217 if action.source.has_attribute? 'project'
1218 prj = DbProject.find_by_name action.source.project
1220 render_error :status => 404, :errorcode => 'unknown_project',
1221 :message => "Unknown source project #{action.source.project} in request #{params[:request]}"
1225 if action.source.has_attribute? 'package'
1226 pkg = prj.db_packages.find_by_name action.source.package
1228 render_error :status => 404, :errorcode => 'unknown_package',
1229 :message => "Unknown source package #{action.source.package} in project #{action.source.project} in request #{params[:request]}"
1236 @packages.push({ :target_project => pkg.db_project, :package => pkg })
1239 # find packages via attributes
1240 at = AttribType.find_by_name(params[:attribute])
1242 render_error :status => 403, :errorcode => 'not_found',
1243 :message => "The given attribute #{params[:attribute]} does not exist"
1247 DbPackage.find_by_attribute_type_and_value( at, params[:value], params[:package] ) do |pkg|
1248 @packages.push({ :target_project => pkg.db_project, :package => pkg })
1250 # FIXME: how to handle linked projects here ? shall we do at all or has the tagger (who creates the attribute) to create the package instance ?
1252 # Find all direct instances of a package
1253 DbPackage.find_by_attribute_type( at, params[:package] ).each do |pkg|
1254 @packages.push({ :target_project => pkg.db_project, :package => pkg })
1256 # Find all indirect instance via project links, a new package will get created on submit accept
1258 projects = DbProject.find_by_attribute_type( at )
1259 projects.each do |prj|
1260 prj.linkedprojects.each do |lprj|
1261 if lprj.linked_db_project
1262 if pkg = lprj.linked_db_project.db_packages.find_by_name( params[:package] )
1263 @packages.push({ :target_project => prj, :package => pkg })
1265 # FIXME: add support for branching from remote projects
1274 #ACL permission check of source packages
1275 @packages.each do |p|
1276 # ACL(index_branch): source access gives permisson denied
1277 if p[:package].disabled_for?('sourceaccess', nil, nil) and not @http_user.can_source_access?(p[:package])
1278 render_error :status => 403, :errorcode => "source_access_no_permission",
1279 :message => "user #{@http_user.login} has no read access to package #{action.source.package}, project #{action.source.project}"
1283 # ACL(index_branch): skip access protected packages
1284 @packages.delete(p) if p[:package].disabled_for?('access', nil, nil) and not @http_user.can_access?(p[:package])
1287 unless @packages.length > 0
1288 render_error :status => 403, :errorcode => "not_found",
1289 :message => "no packages found by search criteria"
1293 #create branch project
1294 tprj = DbProject.find_by_name params[:target_project]
1297 unless @http_user.can_create_project?(params[:target_project])
1298 render_error :status => 403, :errorcode => "create_project_no_permission",
1299 :message => "no permission to create project '#{params[:target_project]}' while executing branch command"
1303 title = "Branch project for package #{params[:package]}"
1304 description = "This project was created for package #{params[:package]} via attribute #{params[:attribute]}"
1306 title = "Branch project based on request #{params[:request]}"
1307 description = "This project was created as a clone of request #{params[:request]}"
1309 DbProject.transaction do
1310 tprj = DbProject.new :name => params[:target_project], :title => title, :description => description
1311 tprj.add_user @http_user, "maintainer"
1312 tprj.flags.create( :position => 1, :flag => 'build', :status => "disable" )
1316 ans = AttribNamespace.find_by_name "OBS"
1317 at = AttribType.find( :first, :joins => ans, :conditions=>{:name=>"RequestCloned"} )
1319 a = Attrib.new(:db_project => tprj, :attrib_type => at)
1320 a.values << AttribValue.new(:value => params[:request], :position => 1)
1324 # ACL(index_branch): in case of access, project is really hidden and shown as non existing to users without access
1325 if not @http_user.can_modify_project?(tprj) or (tprj.disabled_for?('access', nil, nil) and not @http_user.can_access?(tprj))
1326 render_error :status => 403, :errorcode => "modify_project_no_permission",
1327 :message => "no permission to modify project '#{params[:target_project]}' while executing branch project command"
1332 # create package branches
1333 # collect also the needed repositories here
1334 @packages.each do |p|
1335 # is a update project defined and a package there ?
1337 aname = params[:update_project_attribute]
1338 name_parts = aname.split(/:/)
1339 if name_parts.length != 2
1340 raise ArgumentError, "attribute '#{aname}' must be in the $NAMESPACE:$NAME style"
1343 # find origin package to be branched
1344 branch_target_project = p[:target_project].name
1345 branch_target_package = pac.name
1346 proj_name = branch_target_project.gsub(':', '_')
1347 pack_name = branch_target_package.gsub(':', '_') + "." + proj_name
1349 # check for update project
1350 if not params[:request] and a = p[:target_project].find_attribute(name_parts[0], name_parts[1]) and a.values[0]
1351 if pa = DbPackage.find_by_project_and_name( a.values[0].value, p[:package].name )
1352 branch_target_project = pa.db_project.name
1353 branch_target_package = pa.name
1355 # package exists not yet in update project, but it may have a project link ?
1356 uprj = DbProject.find_by_name(a.values[0].value)
1357 if uprj and uprj.find_package( pac.name )
1358 branch_target_project = a.values[0].value
1363 # create branch package
1364 # no find_package call here to check really this project only
1365 if tpkg = tprj.db_packages.find_by_name(pack_name)
1366 render_error :status => 400, :errorcode => "double_branch_package",
1367 :message => "branch target package already exists: #{tprj.name}/#{tpkg.name}"
1370 tpkg = tprj.db_packages.new(:name => pack_name, :title => pac.title, :description => pac.description)
1371 tprj.db_packages << tpkg
1374 # create repositories, if missing
1375 pac.db_project.repositories.each do |repo|
1376 repoName = proj_name+"_"+repo.name
1377 unless tprj.repositories.find_by_name(repoName)
1378 trepo = tprj.repositories.create :name => repoName
1379 trepo.architectures = repo.architectures
1380 trepo.path_elements.create(:link => repo, :position => 1)
1382 tpkg.flags.create( :position => 1, :flag => 'build', :status => "enable", :repo => repoName )
1386 # branch sources in backend
1387 Suse::Backend.post "/source/#{tpkg.db_project.name}/#{tpkg.name}?cmd=branch&oproject=#{CGI.escape(branch_target_project)}&opackage=#{CGI.escape(branch_target_package)}", nil
1390 # store project data in DB and XML
1393 # all that worked ? :)
1394 render_ok :data => {:targetproject => params[:target_project]}
1397 # create a id collection of all projects doing a project link to this one
1398 # POST /source/<project>?cmd=showlinked
1399 def index_project_showlinked
1400 valid_http_methods :post
1401 project_name = params[:project]
1403 # ACL(index_project_showlinked): check project itself for access, project is really hidden and shown as non existing to users without access
1404 tprj = DbProject.find_by_name(params[:project])
1405 if tprj and tprj.disabled_for?('access', nil, nil) and not @http_user.can_access?(tprj)
1406 render_error :status => 404, :errorcode => 'unknown_project',
1407 :message => "Unknown project #{params[:project]}"
1411 builder = FasterBuilder::XmlMarkup.new( :indent => 2 )
1412 pro = DbProject.find_by_name project_name
1413 xml = builder.collection() do |c|
1414 pro.find_linking_projects.each do |l|
1415 # ACL(index_project_showlinked): sort out access proteced projects as hidden
1416 unless pro.disabled_for?('access', nil, nil) and not @http_user.can_access?(pro)
1423 render :text => xml.target!, :content_type => "text/xml"
1426 # POST /source/<project>?cmd=extendkey
1427 def index_project_extendkey
1428 valid_http_methods :post
1429 params[:user] = @http_user.login
1430 project_name = params[:project]
1432 pro = DbProject.find_by_name project_name
1433 # ACL(index_project_extendkey): in case of access, project is really hidden, e.g. does not get listed, accessing says project is not existing
1434 if pro and pro.disabled_for?('access', nil, nil) and not @http_user.can_access?(pro)
1435 render_error :status => 404, :errorcode => 'unknown_project',
1436 :message => "Unknown project '#{project_name}'"
1441 path << build_query_from_hash(params, [:cmd, :user, :comment])
1442 pass_to_backend path
1445 # POST /source/<project>?cmd=createkey
1446 def index_project_createkey
1447 valid_http_methods :post
1448 params[:user] = @http_user.login
1449 project_name = params[:project]
1451 pro = DbProject.find_by_name project_name
1452 # ACL(index_project_createkey): in case of access, project is really hidden, e.g. does not get listed, accessing says project is not existing
1453 # adrian: this should be checked anyway in index_project already
1454 if pro and pro.disabled_for?('access', nil, nil) and not @http_user.can_access?(pro)
1455 render_error :status => 404, :errorcode => 'unknown_project',
1456 :message => "Unknown project '#{project_name}'"
1461 path << build_query_from_hash(params, [:cmd, :user, :comment])
1462 pass_to_backend path
1465 # POST /source/<project>?cmd=undelete
1466 def index_project_undelete
1467 valid_http_methods :post
1468 params[:user] = @http_user.login
1469 project_name = params[:project]
1471 pro = DbProject.find_by_name project_name
1472 # ACL(index_project_undelete): in case of access, project is really hidden, e.g. does not get listed, accessing says project is not existing
1473 # ACL Adrian: this should have be checked anyway in index_project already
1474 if pro and pro.disabled_for?('access', nil, nil) and not @http_user.can_access?(pro)
1475 render_error :status => 404, :errorcode => 'unknown_project',
1476 :message => "Unknown project '#{project_name}'"
1481 path << build_query_from_hash(params, [:cmd, :user, :comment])
1482 pass_to_backend path
1485 # POST /source/<project>?cmd=createpatchinfo
1486 def index_project_createpatchinfo
1487 project_name = params[:project]
1489 pro = DbProject.find_by_name project_name
1490 # ACL(index_project_createpatchinfo): in case of access, project is really hidden, e.g. does not get listed, accessing says project is not existing
1491 # adrian: this should be checked anyway in index_project already
1492 if pro and pro.disabled_for?('access', nil, nil) and not @http_user.can_access?(pro)
1493 render_error :status => 404, :errorcode => 'unknown_project',
1494 :message => "Unknown project '#{project_name}'"
1500 name=params[:name] if params[:name]
1502 pkg_name = "_patchinfo:#{name.gsub(/\W/, '_')}"
1503 patchinfo_path = "#{request.path}/#{pkg_name}"
1505 # request binaries in project from backend
1506 binaries = list_all_binaries_in_path("/build/#{params[:project]}")
1508 if binaries.length < 1 and not params[:force]
1509 render_error :status => 400, :errorcode => "no_matched_binaries",
1510 :message => "No binary packages were found in project repositories"
1514 # FIXME: check for still building packages
1516 # create patchinfo package
1517 if not DbPackage.find_by_project_and_name( params[:project], pkg_name )
1518 prj = DbProject.find_by_name( params[:project] )
1519 pkg = DbPackage.new(:name => pkg_name, :title => "Patchinfo", :description => "Collected packages for update")
1520 prj.db_packages << pkg
1521 Package.find(pkg_name, :project => params[:project]).save
1526 # shall we do a force check here ?
1529 # create patchinfo XML file
1530 node = Builder::XmlMarkup.new(:indent=>2)
1531 xml = node.patchinfo(:name => name) do |n|
1532 binaries.each do |binary|
1535 node.packager @http_user.login
1542 # FIXME add all bugnumbers from attributes
1544 backend_put( patchinfo_path+"/_patchinfo?user="+@http_user.login+"&comment=generated%20file%20by%20frontend", xml )
1549 def list_all_binaries_in_path path
1550 # ACL(list_all_binaries_in_path) TODO: check if this needs to be instrumented
1551 d = backend_get(path)
1552 data = REXML::Document.new(d)
1555 data.elements.each("directory/entry") do |e|
1556 name = e.attributes["name"]
1557 list_all_binaries_in_path("#{path}/#{name}").each do |l|
1561 data.elements.each("binarylist/binary") do |b|
1562 name = b.attributes["filename"]
1563 # strip main name from binary
1564 # patchinfos are only designed for rpms so far
1565 binaries.push( name.sub(/-[^-]*-[^-]*.rpm$/, '' ) )
1572 # create a id collection of all packages doing a package source link to this one
1573 # POST /source/<project>/<package>?cmd=showlinked
1574 def index_package_showlinked
1575 valid_http_methods :post
1576 project_name = params[:project]
1577 package_name = params[:package]
1579 pack = DbPackage.find_by_project_and_name( project_name, package_name )
1582 # package comes from remote instance
1584 # FIXME: return an empty list for now
1585 # we could request the links on remote instance via that: but we would need to search also localy and merge ...
1587 # path = "/search/package/id?match=(@linkinfo/package=\"#{CGI.escape(package_name)}\"+and+@linkinfo/project=\"#{CGI.escape(project_name)}\")"
1588 # answer = Suse::Backend.post path, nil
1589 # render :text => answer.body, :content_type => 'text/xml'
1590 render :text => "<collection/>", :content_type => 'text/xml'
1594 builder = FasterBuilder::XmlMarkup.new( :indent => 2 )
1595 xml = builder.collection() do |c|
1596 pack.find_linking_packages.each do |l|
1597 unless pack.disabled_for?('access', nil, nil) and not @http_user.can_access?(pack)
1599 p[:project] = l.db_project.name
1605 render :text => xml.target!, :content_type => "text/xml"
1608 # POST /source/<project>/<package>?cmd=undelete
1609 def index_package_undelete
1610 valid_http_methods :post
1611 params[:user] = @http_user.login
1612 project_name = params[:project]
1613 package_name = params[:package]
1616 path << build_query_from_hash(params, [:cmd, :user, :comment])
1617 pass_to_backend path
1620 # FIXME: obsolete this for 3.0
1621 # POST /source/<project>/<package>?cmd=createSpecFileTemplate
1622 def index_package_createSpecFileTemplate
1623 # ACL(index_package_createSpecFileTemplate) TODO: check if this needs access check
1624 specfile_path = "#{request.path}/#{params[:package]}.spec"
1626 backend_get( specfile_path )
1627 render_error :status => 400, :errorcode => "spec_file_exists",
1628 :message => "SPEC file already exists."
1630 rescue ActiveXML::Transport::NotFoundError
1631 specfile = File.read "#{RAILS_ROOT}/files/specfiletemplate"
1632 backend_put( specfile_path, specfile )
1637 # OBS 3.0: this should be obsoleted, we have /build/ controller for this
1638 # POST /source/<project>/<package>?cmd=rebuild
1639 def index_package_rebuild
1640 project_name = params[:project]
1641 package_name = params[:package]
1642 repo_name = params[:repo]
1643 arch_name = params[:arch]
1645 # check for sources in this or linked project
1646 pkg = DbPackage.find_by_project_and_name(project_name, package_name)
1648 # check if this is a package on a remote OBS instance
1649 answer = Suse::Backend.get(request.path)
1651 render_error :status => 400, :errorcode => 'unknown_package',
1652 :message => "Unknown package '#{package_name}'"
1657 path = "/build/#{project_name}?cmd=rebuild&package=#{package_name}"
1659 if p.repositories.find_by_name(repo_name).nil?
1660 render_error :status => 400, :errorcode => 'unknown_repository',
1661 :message=> "Unknown repository '#{repo_name}'"
1664 path += "&repository=#{repo_name}"
1667 path += "&arch=#{arch_name}"
1670 backend.direct_http( URI(path), :method => "POST", :data => "" )
1675 # POST /source/<project>/<package>?cmd=commit
1676 def index_package_commit
1677 valid_http_methods :post
1678 params[:user] = @http_user.login
1681 path << build_query_from_hash(params, [:cmd, :user, :comment, :rev, :linkrev, :keeplink, :repairlink])
1682 pass_to_backend path
1684 if params[:package] == "_product"
1685 update_product_autopackages params[:project]
1689 # POST /source/<project>/<package>?cmd=commitfilelist
1690 def index_package_commitfilelist
1691 valid_http_methods :post
1692 params[:user] = @http_user.login
1693 project_name = params[:project]
1694 package_name = params[:package]
1696 prj = DbProject.find_by_name(project_name)
1697 pkg = prj.find_package(package_name)
1698 # ACL(index_package_commitfilelist): in case of access, package is really hidden and shown as non existing to users without access
1699 if pkg and pkg.disabled_for?('access', nil, nil) and not @http_user.can_access?(pkg)
1700 render_error :status => 404, :errorcode => 'unknown_package',
1701 :message => "Unknown package #{params[:package]} in project #{params[:project]}"
1705 # ACL(index_package_commitfilelist): sourceaccess gives permisson denied
1706 if pkg and pkg.disabled_for?('sourceaccess', nil, nil) and not @http_user.can_source_access?(pkg)
1707 render_error :status => 403, :errorcode => "source_access_no_permission",
1708 :message => "user #{params[:user]} has no read access to package #{params[:package]}, project #{params[:project]}"
1712 # ACL(index_package_commitfilelist): lookup via :rev / :linkrev is prevented now by backend if $nosharedtrees is set
1714 path << build_query_from_hash(params, [:cmd, :user, :comment, :rev, :linkrev, :keeplink, :repairlink])
1715 pass_to_backend path
1717 if params[:package] == "_product"
1718 update_product_autopackages params[:project]
1722 # POST /source/<project>/<package>?cmd=diff
1723 def index_package_diff
1724 valid_http_methods :post
1725 project_name = params[:project]
1726 package_name = params[:package]
1727 oproject_name = params[:oproject]
1728 opackage_name = params[:opackage]
1731 path << build_query_from_hash(params, [:cmd, :rev, :oproject, :opackage, :orev, :expand, :unified, :linkrev, :olinkrev, :missingok])
1732 pass_to_backend path
1735 # POST /source/<project>/<package>?cmd=linkdiff
1736 def index_package_linkdiff
1737 valid_http_methods :post
1738 project_name = params[:project]
1739 package_name = params[:package]
1742 path << build_query_from_hash(params, [:rev, :unified, :linkrev])
1743 pass_to_backend path
1746 # POST /source/<project>/<package>?cmd=copy
1747 def index_package_copy
1748 valid_http_methods :post
1749 params[:user] = @http_user.login
1751 sproject = params[:project]
1752 sproject = params[:oproject] if params[:oproject]
1753 spackage = params[:package]
1754 spackage = params[:opackage] if params[:opackage]
1756 # create target package, if it does not exist
1757 tpkg = DbPackage.find_by_project_and_name(params[:project], params[:package])
1759 answer = Suse::Backend.get("/source/#{CGI.escape(sproject)}/#{CGI.escape(spackage)}/_meta")
1761 p = Package.new(answer.body, :project => params[:project])
1762 p.name = params[:package]
1764 tpkg = DbPackage.find_by_project_and_name(params[:project], params[:package])
1766 render_error :status => 404, :errorcode => 'unknown_package',
1767 :message => "Unknown package #{spackage} in project #{sproject}"
1772 # ACL(index_package_copy): lookup via :rev / :linkrev is prevented now by backend if $nosharedtrees is set
1773 # We need to use the project name of package object, since it might come via a project linked project
1774 path = "/source/#{CGI.escape(tpkg.db_project.name)}/#{CGI.escape(tpkg.name)}"
1775 path << build_query_from_hash(params, [:cmd, :rev, :user, :comment, :oproject, :opackage, :orev, :expand, :keeplink, :repairlink, :linkrev, :olinkrev, :requestid, :dontupdatesource])
1777 pass_to_backend path
1780 # POST /source/<project>/<package>?cmd=runservice
1781 def index_package_runservice
1782 valid_http_methods :post
1783 params[:user] = @http_user.login
1784 project_name = params[:project]
1785 package_name = params[:package]
1788 path << build_query_from_hash(params, [:cmd, :comment])
1789 pass_to_backend path
1792 # POST /source/<project>/<package>?cmd=deleteuploadrev
1793 def index_package_deleteuploadrev
1794 valid_http_methods :post
1795 params[:user] = @http_user.login
1796 project_name = params[:project]
1797 package_name = params[:package]
1800 path << build_query_from_hash(params, [:cmd])
1801 pass_to_backend path
1804 # POST /source/<project>/<package>?cmd=linktobranch
1805 def index_package_linktobranch
1806 valid_http_methods :post
1807 params[:user] = @http_user.login
1808 prj_name = params[:project]
1809 pkg_name = params[:package]
1810 pkg_rev = params[:rev]
1811 pkg_linkrev = params[:linkrev]
1813 prj = DbProject.find_by_name prj_name
1814 pkg = prj.db_packages.find_by_name(pkg_name)
1816 render_error :status => 404, :errorcode => 'unknown_package',
1817 :message => "Unknown package #{pkg_name} in project #{prj_name}"
1821 #convert link to branch
1823 if not pkg_rev.nil? and not pkg_rev.empty?
1824 rev = "&orev=#{pkg_rev}"
1827 if not pkg_linkrev.nil? and not pkg_linkrev.empty?
1828 linkrev = "&linkrev=#{pkg_linkrev}"
1830 Suse::Backend.post "/source/#{prj_name}/#{pkg_name}?cmd=linktobranch&user=#{CGI.escape(params[:user])}#{rev}#{linkrev}", nil
1835 # POST /source/<project>/<package>?cmd=branch&target_project="optional_project"&target_package="optional_package"&update_project_attribute="alternative_attribute"&comment="message"
1836 def index_package_branch
1837 valid_http_methods :post
1838 params[:user] = @http_user.login
1839 prj_name = params[:project]
1840 pkg_name = params[:package]
1841 pkg_rev = params[:rev]
1842 target_project = params[:target_project]
1843 target_package = params[:target_package]
1844 if not params[:update_project_attribute]
1845 params[:update_project_attribute] = "OBS:UpdateProject"
1847 logger.debug "branch call of #{prj_name} #{pkg_name}"
1849 prj = DbProject.find_by_name prj_name
1850 if prj.nil? and DbProject.find_remote_project(prj_name).nil?
1851 render_error :status => 404, :errorcode => 'unknown_project',
1852 :message => "Unknown project #{prj_name}"
1856 pkg = prj.find_package( pkg_name )
1858 # Check if this is a package via project link to a remote OBS instance
1859 answer = Suse::Backend.get("/source/#{CGI.escape(prj.name)}/#{CGI.escape(pkg_name)}")
1861 render_error :status => 404, :errorcode => 'unknown_package',
1862 :message => "Unknown package #{pkg_name} in project #{prj.name}"
1868 # is a update project defined and a package there ?
1869 aname = params[:update_project_attribute]
1870 name_parts = aname.split(/:/)
1871 if name_parts.length != 2
1872 raise ArgumentError, "attribute '#{aname}' must be in the $NAMESPACE:$NAME style"
1875 if prj and a = prj.find_attribute(name_parts[0], name_parts[1]) and a.values[0]
1876 if pa = DbPackage.find_by_project_and_name( a.values[0].value, pkg.name )
1877 # We have a package in the update project already, take that
1879 prj = pkg.db_project
1880 logger.debug "branch call found package in update project #{prj.name}"
1882 update_prj = DbProject.find_by_name( a.values[0].value )
1883 update_pkg = update_prj.find_package( pkg.name )
1885 # We have no package in the update project yet, but sources are reachable via project link
1892 # validate and resolve devel package or devel project definitions
1893 if not params[:ignoredevel] and pkg and ( pkg.develproject or pkg.develpackage )
1894 pkg = pkg.resolve_devel_package
1895 prj = pkg.db_project
1896 logger.debug "devel project is #{prj.name} #{pkg.name}"
1899 # link against srcmd5 instead of plain revision
1902 dir = Directory.find({ :project => params[:project], :package => params[:package], :rev => params[:rev]})
1904 render_error :status => 400, :errorcode => 'invalid_filelist',
1905 :message => "no such revision"
1908 if dir.has_attribute? 'srcmd5'
1909 pkg_rev = dir.srcmd5
1911 render_error :status => 400, :errorcode => 'invalid_filelist',
1912 :message => "no srcmd5 revision found"
1917 oprj_name = "home:#{@http_user.login}:branches:#{prj_name}"
1918 oprj_name = "home:#{@http_user.login}:branches:#{prj.name}" if prj
1919 opkg_name = pkg_name
1920 opkg_name = pkg.name if pkg
1921 oprj_name = target_project unless target_project.nil?
1922 opkg_name = target_package unless target_package.nil?
1924 #create branch container
1925 oprj = DbProject.find_by_name oprj_name
1926 raise IllegalRequestError.new "invalid_project_name" unless valid_project_name?(oprj_name)
1928 unless @http_user.can_create_project?(oprj_name)
1929 render_error :status => 403, :errorcode => "create_project_no_permission",
1930 :message => "no permission to create project '#{oprj_name}' while executing branch command"
1934 DbProject.transaction do
1935 oprj = DbProject.new :name => oprj_name, :title => "Branch of #{prj_name}"
1936 oprj.add_user @http_user, "maintainer"
1937 oprj.flags.create( :status => "disable", :flag => 'publish')
1938 # ACL(index_package_branch): inherit all access flags from branched project
1940 if prj.disabled_for?('access', nil, nil)
1941 oprj.flags.create( :status => "disable", :flag => 'access')
1943 if prj.disabled_for?('sourceaccess', nil, nil)
1944 oprj.flags.create( :status => "disable", :flag => 'sourceaccess')
1946 if prj.disabled_for?('binarydownload', nil, nil)
1947 oprj.flags.create( :status => "disable", :flag => 'binarydownload')
1949 if prj.enabled_for?('privacy', nil, nil)
1950 oprj.flags.create( :status => "enable", :flag => 'privacy')
1954 # FIXME: support this also for remote projects
1955 prj.repositories.each do |repo|
1956 orepo = oprj.repositories.create :name => repo.name
1957 orepo.architectures = repo.architectures
1958 orepo.path_elements << PathElement.new(:link => repo, :position => 1)
1965 #create branch package
1966 unless valid_package_name? opkg_name
1967 render_error :status => 400, :errorcode => "invalid_package_name",
1968 :message => "invalid package name '#{opkg_name}'"
1970 if opkg = oprj.db_packages.find_by_name(opkg_name)
1972 # shall we clean all files here ?
1974 render_error :status => 400, :errorcode => "double_branch_package",
1975 :message => "branch target package already exists: #{oprj_name}/#{opkg_name}"
1979 unless @http_user.can_modify_package?(opkg)
1980 render_error :status => 403, :errorcode => "create_package_no_permission",
1981 :message => "no permission to create package '#{opkg_name}' for project '#{oprj_name}' while executing branch command"
1985 unless @http_user.can_create_package_in?(oprj)
1986 render_error :status => 403, :errorcode => "create_package_no_permission",
1987 :message => "no permission to create package '#{opkg_name}' for project '#{oprj_name}' while executing branch command"
1992 opkg = oprj.db_packages.create(:name => opkg_name, :title => pkg.title, :description => params.has_key?(:comment) ? params[:comment] : pkg.description)
1994 opkg = oprj.db_packages.create(:name => opkg_name, :description => params.has_key?(:comment) ? params[:comment] : "" )
1996 opkg.add_user @http_user, "maintainer"
2000 #create branch of sources in backend
2002 if not pkg_rev.nil? and not pkg_rev.empty?
2003 rev = "&orev=#{pkg_rev}"
2005 comment = params.has_key?(:comment) ? "&comment=#{CGI.escape(params[:comment])}" : ""
2007 Suse::Backend.post "/source/#{oprj_name}/#{opkg_name}?cmd=branch&oproject=#{CGI.escape(prj.name)}&opackage=#{CGI.escape(pkg.name)}#{rev}&user=#{CGI.escape(@http_user.login)}#{comment}", nil
2008 render_ok :data => {:targetproject => oprj_name, :targetpackage => opkg_name, :sourceproject => prj.name, :sourcepackage => pkg.name}
2010 Suse::Backend.post "/source/#{oprj_name}/#{opkg_name}?cmd=branch&oproject=#{CGI.escape(prj_name)}&opackage=#{CGI.escape(pkg_name)}#{rev}&user=#{CGI.escape(@http_user.login)}#{comment}", nil
2011 render_ok :data => {:targetproject => oprj_name, :targetpackage => opkg_name, :sourceproject => prj_name, :sourcepackage => pkg_name}
2015 # POST /source/<project>/<package>?cmd=set_flag&repository=:opt&arch=:opt&flag=flag&status=status
2016 def index_package_set_flag
2017 valid_http_methods :post
2019 required_parameters :project, :package, :flag, :status
2021 prj_name = params[:project]
2022 pkg_name = params[:package]
2024 # we can savely assume it exists - this function is called through dispatch_command
2025 prj = DbProject.find_by_name prj_name
2026 pkg = prj.find_package( pkg_name )
2028 render_error :status => 404, :errorcode => "unknown_package",
2029 :message => "Unknown package '#{pkg_name}' in project '#{prj_name}'"
2033 # first remove former flags of the same class
2035 pkg.remove_flag(params[:flag], params[:repository], params[:arch])
2036 pkg.add_flag(params[:flag], params[:status], params[:repository], params[:arch])
2037 rescue ArgumentError => e
2038 render_error :status => 400, :errorcode => 'invalid_flag', :message => e.message
2045 # POST /source/<project>?cmd=set_flag&repository=:opt&arch=:opt&flag=flag&status=status
2046 def index_project_set_flag
2047 valid_http_methods :post
2049 required_parameters :project, :flag, :status
2051 prj_name = params[:project]
2053 prj = DbProject.find_by_name prj_name
2055 render_error :status => 404, :errorcode => "unknown_project",
2056 :message => "Unknown project '#{prj_name}'"
2061 # first remove former flags of the same class
2062 prj.remove_flag(params[:flag], params[:repository], params[:arch])
2063 prj.add_flag(params[:flag], params[:status], params[:repository], params[:arch])
2064 rescue ArgumentError => e
2065 render_error :status => 400, :errorcode => 'invalid_flag', :message => e.message
2069 # ACL(index_project_set_flag): in case of access, project is really hidden, e.g. does not get listed, accessing says project is not existing
2070 if prj and prj.disabled_for?('access', params[:repository], params[:arch]) and not @http_user.can_access?(prj)
2071 render_error :status => 404, :errorcode => 'unknown_project',
2072 :message => "Unknown project '#{prj_name}'"
2075 # ACL(index_project_set_flag): you are not allowed to protect an unprotected project with access
2076 if params[:flag] == "access" and params[:status] == "disable" and prj.enabled_for?('access', params[:repository], params[:arch]) and not
2077 @http_user.is_admin?
2078 render_error :status => 403, :errorcode => "change_project_protection_level",
2079 :message => "admin rights are required to raise the protection level of a project"
2082 # ACL(index_project_set_flag): you are not allowed to protect an unprotected project with sourceaccess
2083 if params[:flag] == "sourceaccess" and params[:status] == "disable" and prj.enabled_for?('sourceaccess', params[:repository], params[:arch]) and not
2084 @http_user.is_admin?
2085 render_error :status => 403, :errorcode => "change_project_protection_level",
2086 :message => "admin rights are required to raise the protection level of a project"
2094 # POST /source/<project>/<package>?cmd=remove_flag&repository=:opt&arch=:opt&flag=flag
2095 def index_package_remove_flag
2096 valid_http_methods :post
2098 required_parameters :project, :package, :flag
2100 pkg = DbPackage.find_by_project_and_name( params[:project], params[:package] )
2101 pkg.remove_flag(params[:flag], params[:repository], params[:arch])
2106 # POST /source/<project>?cmd=remove_flag&repository=:opt&arch=:opt&flag=flag
2107 def index_project_remove_flag
2108 valid_http_methods :post
2109 required_parameters :project, :flag
2111 prj_name = params[:project]
2113 prj = DbProject.find_by_name prj_name
2115 render_error :status => 404, :errorcode => "unknown_project",
2116 :message => "Unknown project '#{prj_name}'"
2120 # ACL(index_project_remove_flag): in case of access, project is really hidden, e.g. does not get listed, accessing says project is not existing
2121 if prj and prj.disabled_for?('access', params[:repository], params[:arch]) and not @http_user.can_access?(prj)
2122 render_error :status => 404, :errorcode => 'unknown_project',
2123 :message => "Unknown project '#{prj_name}'"
2127 prj.remove_flag(params[:flag], params[:repository], params[:arch])
2132 def valid_project_name? name
2133 name =~ /^\w[-_+\w\.:]*$/
2136 def valid_package_name? name
2137 return true if name == "_pattern"
2138 return true if name == "_project"
2139 return true if name == "_product"
2140 return true if name =~ /^_product:[-_+\w\.:]*$/
2141 name =~ /^\w[-_+\w\.:]*$/