1<?php
2// WebSVN - Subversion repository viewing via the web using PHP
3// Copyright (C) 2004-2006 Tim Armes
4//
5// This program is free software; you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation; either version 2 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program; if not, write to the Free Software
17// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA	02111-1307	USA
18//
19// --
20//
21// authz.php
22//
23// Handle SVN access file
24
25class Authorization {
26	var $accessCache	= array();
27	var $accessFile		= null;
28	var $user			= null;
29
30	// {{{ __construct
31
32	function __construct() {
33		$this->setUsername();
34	}
35
36	// }}}
37
38	function hasUsername() {
39		return $this->user !== null;
40	}
41
42	function addAccessFile($accessFile) {
43		$this->accessFile = $accessFile;
44	}
45
46	// {{{ setUsername()
47	//
48	// Set the username from the current http session
49
50	function setUsername() {
51		if (isset($_SERVER['REMOTE_USER'])) {
52			$this->user = $_SERVER['REMOTE_USER'];
53		} else if (isset($_SERVER['REDIRECT_REMOTE_USER'])) {
54			$this->user = $_SERVER['REDIRECT_REMOTE_USER'];
55		} else if (isset($_SERVER['PHP_AUTH_USER'])) {
56			$this->user = $_SERVER['PHP_AUTH_USER'];
57		}
58	}
59
60	// }}}
61
62	// Private function to simplify creation of common SVN authz command string text.
63	function svnAuthzCommandString($repo, $path, $checkSubDirs = false) {
64		global $config;
65
66		$cmd			= $config->getSvnAuthzCommand();
67		$repoAndPath	= '--repository ' . quote($repo) . ' --path ' . quote($path);
68		$username		= !$this->hasUsername()	? '' : '--username ' . quote($this->user);
69		$subDirs		= !$checkSubDirs		? '' : '-R';
70		$authzFile		= quote($this->accessFile);
71		$retVal			= "${cmd} ${repoAndPath} ${username} ${subDirs} ${authzFile}";
72
73		return $retVal;
74	}
75
76	// {{{ hasReadAccess
77	//
78	// Returns true if the user has read access to the given path
79
80	function hasReadAccess($repos, $path, $checkSubDirs = false) {
81		if ($this->accessFile == null)
82			return false;
83
84		if ($path == '' || $path[0] != '/') {
85			$path = '/'.$path;
86		}
87
88		$cmd	= $this->svnAuthzCommandString($repos, $path, $checkSubDirs);
89		$result	= 'no';
90
91		// Access checks might be issued multiple times for the same repos and paths within one and
92		// the same request, introducing a lot of overhead because of "svnauthz" especially with
93		// many repos under Windows. The easiest way to somewhat optimize it for different scenarios
94		// is using a cache.
95		//
96		// https://github.com/websvnphp/websvn/issues/78#issuecomment-489306169
97		$cache			=& $this->accessCache;
98		$cached			= isset($cache[$cmd])	? $cache[$cmd]		: null;
99		$cachedWhen		= isset($cached)		? $cached['when']	: 0;
100		$cachedExpired	= (time() - 60) > $cachedWhen;
101
102		if ($cachedExpired) {
103			// Sorting by "when" should be established somehow to only remove the oldest element
104			// instead of an arbitrary first one, which might be the newest added last time.
105			if (count($cache) >= 1000) {
106				array_shift($cache);
107			}
108
109			$result			= runCommand($cmd)[0];
110			$cache[$cmd]	= array('when'		=> time(),
111									'result'	=> $result);
112		} else {
113			$result = $cached['result'];
114		}
115
116		return $result != 'no';
117	}
118
119	// }}}
120
121	// {{{ hasUnrestrictedReadAccess
122	//
123	// Returns true if the user has read access to the given path and too
124	// all subdirectories
125
126	function hasUnrestrictedReadAccess($repos, $path) {
127		return $this->hasReadAccess($repos, $path, true);
128	}
129
130	// }}}
131
132}
133