| 
  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  
   |