Project

General

Profile

Feature #2647 » svnpatch251_v3.patch

Daniel Hger, 2014-10-13 13:37

View differences:

redmine-2.5.1-new/app/controllers/application_controller.rb 2014-06-03 22:19:00.306007981 +0200
419 419
    return false
420 420
  end
421 421

  
422
  #by heagdl, added 2 error msgs
423
  def render_ad(options={})
424
    render_error({:message => "Access to this repository denied! Please contact your system administrator for further information.", :status => 401}.merge(options))
425
    return false
426
  end 
427

  
428
  def render_rd(options={})
429
    render_error({:message => "Access to this revision denied! You are not authorized to see the associated paths. Please contact your system administrator for further information.", :status => 401}.merge(options))
430
    return false
431
  end 
432

  
422 433
  # Renders an error response
423 434
  def render_error(arg)
424 435
    arg = {:message => arg} unless arg.is_a?(Hash)
redmine-2.5.1-new/app/controllers/repositories_controller.rb 2014-06-04 02:21:26.728007983 +0200
19 19
require 'SVG/Graph/BarHorizontal'
20 20
require 'digest/sha1'
21 21
require 'redmine/scm/adapters'
22
require 'open3' #may be unused
23
require 'svncheck'
22 24

  
23 25
class ChangesetNotFound < Exception; end
24 26
class InvalidRevisionParam < Exception; end
......
32 34
  before_filter :find_repository, :only => [:edit, :update, :destroy, :committers]
33 35
  before_filter :find_project_repository, :except => [:new, :create, :edit, :update, :destroy, :committers]
34 36
  before_filter :find_changeset, :only => [:revision, :add_related_issue, :remove_related_issue]
37
  before_filter :check_repo_access, :only => [:revision, :revisions, :show, :changes, :diff, :graph, :stats, :entry, :annotate, :raw]
35 38
  before_filter :authorize
36 39
  accept_rss_auth :revisions
37 40

  
......
435 438
    )
436 439
    graph.burn
437 440
  end
441

  
442
  #check if the current user is allowed to access the repository at the requested paths
443
  def check_repo_access
444
    if (@repository.type.eql? "Repository::Subversion")
445
      begin
446
        @svnchk = SVNCheck.new(@repository.url,@repository.root_url,User.current.login)
447
        if params[:action] == "revision" 
448
          if !@svnchk.chkrev(@changeset.id)
449
            render_rd; return false
450
          end 
451
        else 
452
          if params[:path].nil?
453
            reqpath = "/" 
454
          else
455
            reqpath = "/"+params[:path]
456
          end 
457
          logger.info 'SVN-Request for following path: ' + reqpath
458
          if !(@svnchk.chkurl(reqpath))
459
            render_ad; return false
460
          end 
461
        end 
462
      rescue Exception => e
463
        logger.fatal "An error occured during svnchk!"
464
        logger.info e.message
465
      end 
466
    end 
467
  end 
438 468
end
redmine-2.5.1-new/app/helpers/application_helper.rb 2014-06-03 22:24:07.820007037 +0200
771 771
                              :repository_id => repository.identifier_param,
772 772
                              :rev => changeset.revision},
773 773
                             :class => 'changeset',
774
                             :title => truncate_single_line_raw(changeset.comments, 100))
774
                             :title => if (repository.type.eql? "Repository::Subversion")
775
                                         require 'svncheck'
776
                                         svnc = SVNCheck.new(repository.url,repository.root_url,User.current.login)
777
                                         if !svnc.chkrev(changeset.id)
778
                                           "Access to this revision denied!"
779
                                         else 
780
                                           truncate_single_line_raw(changeset.comments, 100) 
781
                                         end  
782
                                       else 
783
                                         truncate_single_line_raw(changeset.comments, 100) 
784
                                       end) 
775 785
            end
776 786
          end
777 787
        elsif sep == '#'
redmine-2.5.1-new/app/models/changeset.rb 2014-06-03 22:19:00.308007965 +0200
267 267
    comments =~ /\A(.+?)\r?\n(.*)$/m
268 268
    @short_comments = $1 || comments
269 269
    @long_comments = $2.to_s.strip
270
    #check if allowed to show by haegdl:
271
    if (repository.type.eql? "Repository::Subversion")
272
      require 'svncheck'
273
      svnc = SVNCheck.new(repository.url,repository.root_url,User.current.login)
274
      if !svnc.chkrev(id)
275
        @short_comments = "Access to this revision denied!"
276
        @long_comments = "You have no rights to access this path in your svn repository!"
277
      end 
278
    end 
270 279
    return @short_comments, @long_comments
271 280
  end
272 281

  
redmine-2.5.1-new/lib/svncheck.rb 2014-06-04 02:22:59.513007752 +0200
1
#################################################
2
# INFO 
3
#
4
# This file was written by Daniel Haeger (haegdl@idmt.fraunhofer.de)
5
# Its a helper for checking if a user is permitted to access a SVN
6
# repository protected by mod_authz_svn with path-based authz.
7
#
8
# In the same directory, a 'right to read' dominates a 'deny'
9
# For initialisation, the paths to the authz file of the repo
10
# and the user have to be specified! 
11
#
12
# root_url is different that url if the svn should just
13
# access a subdir!
14
#
15
# TODO
16
# 
17
# -wrap it in begin.rescue.end blocks
18
# -allow aliases
19
# -logging instead of puts using STDERR.puts "error" ?
20
#
21
# Last edited: 13.10.14 13:37
22
#
23
##################################################
24

  
25
class SVNCheck
26
  def initialize(url, rooturl, user)
27
    puts "Started SVNCheck with:\n " + url + "\n " + rooturl + "\n " + user
28

  
29
    status = 0
30
    currentpath = ""
31
    @user = user.dup 
32
    @authzpath = rooturl.dup #"/opt/svn/systies_cp/conf/authz"
33
    if @authzpath.start_with?("file://")
34
      @authzpath.slice!("file://") #remove beginning (we need absolut path)
35
    end
36
    @authzpath = @authzpath.chomp("/") + "/conf/authz"
37

  
38
    usergroups = [] #all groups the user is in, groups have to be specified before beeing used
39
    @ap = [] #paths with allowed access
40
    @dp = [] #paths with denied access
41

  
42
    #set prefix path
43
    @urlprefix = "" #if repository is a svn subdirectory, we need a prefix to match paths from authz file 
44

  
45
    #catch if rooturl.nil? (when repo is just created!) Add/Change your name below to be able to browse the svn for the first time!!!!
46
    if ((rooturl.size == 0) && !(user.eql?("haegdl") || user.eql?("admin")))
47
      puts "ERROR during SVNCheck, rooturl is nil, please access the repo from admin/haegdl account to activate it!"
48
    else
49
      #set prefix if needed
50
      if url.length > rooturl.length
51
        @urlprefix = url.sub(rooturl, "")
52
      end 
53
  
54
      File.open(@authzpath, "r") do |f|
55
        f.each_line do |line|
56
          line.gsub!(/\s+/, "") #.downcase! removes whitespace; lowercasing disabled!
57
          if !(line.empty? || (line[0,1] == "#") || (line[0,9] == "[aliases]")) #comments or empty lines shall not be processed
58
            nchanged = true  #not changed; used to ignore section lines    
59
      
60
            #section check 
61
            if line[0,8] == '[groups]' then 
62
              status = 1	#1=> group declaration 
63
              nchanged = false
64
            elsif line[0,2] == '[/' then
65
              status = 2 # path check mode
66
              nchanged = false
67
            end
68
            
69
            #PLACE FOR STRING OPERATION
70
            case status
71
            when 1		#check for groups
72
              parts = line.split("=")
73
              if ( !(parts.empty?) && (parts.size == 2) && nchanged)
74
                if parts[1].split(",").include? user  
75
                  usergroups << "@" + parts[0]
76
                else
77
                  #allow groups in group creation
78
                  parts[1].split(",") do |x|
79
                    if usergroups.include? x
80
                      usergroups << parts[0]
81
                      break
82
                    end
83
                  end
84
                end    
85
              end
86
            when 2
87
              if !(nchanged)
88
                #this line specifies path
89
                currentpath = line.gsub!(/[\[\]]/, "")
90
              else
91
                #3 checks: first: groups_allowed?; second: userallowed
92
                parts = line.split("=")
93
    	        if (!(parts.empty?) && (parts.size == 2)) 
94
  	          #check groups
95
                  if (usergroups.include? parts[0]) 	#name matches a group name the user is in
96
                    case parts[1]
97
                    when "rw", "wr", "r"
98
                      @ap << currentpath
99
                    when "w"
100
                      puts "NOTE: just write-access, doing nothing!"
101
                    else
102
                      puts "ERROR, 2nd part could not be interpreted" + parts[1]
103
                    end
104
                  end
105
                  #check explicit mentioned user
106
                  if (parts[0].eql? user)  #if username is mentiend
107
                    case parts[1]
108
                    when "rw", "wr", "r"
109
                      @ap << currentpath
110
                    when "w"
111
                      puts "NOTE: just write-access, not implemented yet, doing nothing!"
112
                    else
113
                      puts "ERROR, 2nd Part could not be interpreited" + "###" + parts[1] + "###"
114
                    end
115
                  end
116
                #*= all user
117
  	        if (parts[0].eql? "*")
118
                    case parts[1]
119
                    when "rw", "wr", "r"
120
                      @ap << currentpath 
121
                    when "w"
122
                      puts "ERROR, not implemented yet!"
123
                    else 
124
                      puts "ERROR, second part could not be interpreted"
125
                    end
126
                  end
127
                end
128
                #interpret denied access
129
                if (parts.size == 1)
130
                  if ((parts[0].eql? user)|| (usergroups.include? parts[0]) || (parts[0].eql? "*") )
131
                    @dp << currentpath
132
                  end
133
                end
134
              end                 
135
            else
136
              puts "ERROR!"
137
            end   
138
          end 
139
        end
140
      end
141
    end
142
  end
143
  
144
  # Erklaerung:
145
  # 
146
  # Suche alle Pfade für die Richtlinien existieren, welche ein Teil des zu ueberpruefenden Pfades
147
  # sind und gleichzeitig fuer den Benutzer gelten (andere werden ignoriert!)
148
  # Bestimme den laengsten uebereinstimmenden Pfad fuer den es eine Regel gibt und wende sie an.
149

  
150
  def chk(chkurl)
151
    chkp =  chkurl#.sub("/","") # @urlprefix + ...
152
    puts "PATH TO CHECK: " + chkp + " PREFIX IS: " + @urlprefix
153

  
154
    ap = @ap.dup
155
    dp = @dp.dup
156
    #remove tailing '/'
157
    (ap + dp).each do |s|
158
      if s.length > 1 
159
        s.chomp!("/")
160
      end
161
    end
162
    if chkp.length > 1
163
      chkp.chomp!("/")
164
    end 
165
    #just keep paths matching to the path we have to check
166
    dp.delete_if { |x| !dirorsubdir(chkp,x) } 
167
    ap.delete_if { |x| !dirorsubdir(chkp,x) } 
168
    puts "Authz rules found for following paths:"
169
    puts (ap + dp)
170
    path = (ap + dp).sort_by(&:length).reverse.first
171
    puts "Winner Path = " + path unless path.nil?
172
    
173
    if (path.nil? || path.eql?(""))
174
      puts "ACCESS DENIED! Checked path: " + chkp + " There is no rule for this person in the conf file!"
175
      return false
176
    else
177
      if ap.include?(path)
178
        #access granted!
179
        puts "ACCESS GRANTED! Checked path: " + chkp + " Allowed by read-rule in: "+ path
180
        return true
181
      end
182
      if dp.include?(path)
183
        #access denied!
184
        puts "ACCESS DENIED! Checked path: " + chkp + " Denied by rule in: " + path
185
        return false
186
      end
187
    end
188
  end
189

  
190
  def dirorsubdir(chkp, x)
191
    if chkp.start_with?(x)
192
      if ((chkp.length > x.length) && (chkp[x.length,1] == "/" ))
193
        #chkp is in directory x or one of its subdirs (recursiv)
194
        return true
195
      elsif chkp == x
196
        # path that was asked for
197
        return true
198
      elsif x == "/"
199
        #is always valid (check if other paths match better is solved by sorting in chk
200
        return true
201
      else
202
        #deny all other
203
        return false
204
      end
205
    else
206
      return false
207
    end
208
  end
209

  
210
  #resolve revisions to paths
211
  def chkrev(csetid)
212
    puts 'SVN-Request for Revision: ' + Changeset.find(csetid).revision
213
    changes = Change.where(changeset_id: csetid)  
214
    changes.each do |x|
215
      if !(chk(x.path)) 
216
        puts 'SVN-Request DENIED for ' + User.current.login
217
        return false
218
      end
219
    end
220
    #didnt returned false before -> no request denied
221
    puts 'SVN-Request ALLOWED for ' + User.current.login
222
    return true
223
  end
224

  
225
  def chkurl(url)
226
    #add prefix if svn-subdir (not needed @revisions)
227
    return chk(@urlprefix.chomp("/") + url)
228
  end
229

  
230
end 
(6-6/6)