VisualSVN server – Script to convert SVN-based permission file (authz) to Windows-based permission file (authz-windows)

visuasvnserver_logoThis article introduce a script to Script to convert SVN-based permission file (authz) to Windows-based permission file (authz-windows) in VisualSVN server.

VisualSVN Server is a good SVN server for Windows-based. By default, authentication in VisualSVN server is based on 2 files: authorization file (password is encoded with htpassword) and permission file. If later, we intend to use Windows/AD-based authentication for SVN server, then how can we keep all granted permission for users along all SVN repositories?

An example of authz file is as follows

[/]
user1=rw

[RD:/]
user2=rw

[RD:/analytics]
user2=rw
user3=rw
user4=rw

An exmple of authz.windows is as follows:

[/]
S-1-5-21-3028427551-2588925682-1401053627-1114=rw

[RD:/]
S-1-5-21-3028427551-2588925682-1401053627-1734=rw

[RD:/analytics]
S-1-5-21-3028427551-2588925682-1401053627-1734=rw
S-1-5-21-3028427551-2588925682-1401053627-1868=rw
S-1-5-21-3028427551-2588925682-1401053627-1855=rw

We can write a PHP script to convert authz -> authz-windows by generating Windows SID from current username:

<?php

define('SVN_REPOSITORY_ROOT_FOLDER','/data/www/test');
define('SOURCE_AUTHZ_FILE', 		'authz');
define('DEST_AUTHZ_WINDOWS_FILE', 	'authz-windows');

doProcess();

/**
 * Convert a username to an AD-based SID.
 * NOTE: Stupid processing method, since we establish 1 connection per username. But who cares when the amount of users is not too large :D/
 */
function convertUsernameToSID($bind_username_to_search = "BIND_USER_NAME")
{
	$suffix ="@YOURDOMAIN.com";
	$base_dn = "ou=YOUR_OU_Users,dc=YOURDOMAIN,dc=com";
	$server = "ldaps://www.YOUR_WEBSITE.com:636";
	$bind_username = "binduser";
	$bind_password = "bindpassword";

	$ds = ldap_connect($server);
	ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
	ldap_set_option($ds, LDAP_OPT_REFERRALS, 0);
	$r = ldap_bind($ds, $bind_username.$suffix, $bind_password);
	$sr = ldap_search($ds, $base_dn, "(samaccountname=".$bind_username_to_search.")");
	@$entries = ldap_get_entries($ds, $sr);
	if (empty($entries))
		return '';//-- find nothing T__T

	// All SID's begin with S-
	$sid = "S-";
	// Convert Bin to Hex and split into byte chunks
	@$sidinhex = str_split(bin2hex($entries[0]['objectsid'][0]), 2);
	// Byte 0 = Revision Level
	@$sid = $sid.hexdec($sidinhex[0])."-";
	// Byte 1-7 = 48 Bit Authority
	@$sid = $sid.hexdec($sidinhex[6].$sidinhex[5].$sidinhex[4].$sidinhex[3].$sidinhex[2].$sidinhex[1]);
	// Byte 8 count of sub authorities - Get number of sub-authorities
	@$subauths = hexdec($sidinhex[7]);
	//Loop through Sub Authorities
	for($i = 0; $i < $subauths; $i++)
	{
		$start = 8 + (4 * $i);
		// X amount of 32Bit (4 Byte) Sub Authorities
		@$sid = $sid."-".hexdec($sidinhex[$start+3].$sidinhex[$start+2].$sidinhex[$start+1].$sidinhex[$start]);
	}
	return $sid; 
}

/**
 * Main process: Read each lines of file, do covert if needed, and write to the target file
 */
function doProcess()
{
	$filearray = file(SVN_REPOSITORY_ROOT_FOLDER . DIRECTORY_SEPARATOR . SOURCE_AUTHZ_FILE);
	if (!($filearray))
	{
		echo "Cannot read file!!!!";
		return;
	}

	$group_name_mapping = array
	(
		'@group1custom' => 'GROUP-NAME-1',
		'@group2custom' => 'GROUP-NAME-2',
	);	//-- map pre-defined groups in SVN server to AD-based group name
	$permission_pattern = '/(@?[0-9a-zA-Z]+)s?=s?([a-z]*)/';	
	$prev_mapped_username = array();
	$str_output_to_write = '';

	while (list(,$oneline) = each($filearray))
	{
		if (preg_match_all($permission_pattern, $oneline, $matches))
		{
			if (isset($matches[1]) && isset($matches[2]))
			{				
				$object_name = $matches[1][0];
				$permission = $matches[2][0];
				//-- If it is a pre-defined group: (1) retrieved according group in AD (2) convert the name to SID
				if (substr($object_name, 0, 1) == '@')
				{
					if (isset($prev_mapped_username[$object_name]))
					{
						$group_sid = $prev_mapped_username[$object_name];
					}
					else
					{
						if (isset($group_name_mapping[$object_name]))
						{
							$group_sid = convertUsernameToSID($group_name_mapping[$object_name]);					
							$prev_mapped_username[$object_name] = $user_sid;							
						}
					}
					if (!empty($group_sid))
					{
						$str_output_to_write .= $group_sid . '=' . $permission . PHP_EOL;
					}
				}
				else
				{
					if (isset($prev_mapped_username[$object_name]))
					{
						$user_sid = $prev_mapped_username[$object_name];
					}
					else
					{
						$user_sid = convertUsernameToSID($object_name);
						$prev_mapped_username[$object_name] = $user_sid;
					}

					if (!empty($user_sid) && @strlen($user_sid) >= 20) //-- TRICK: Remove non-valid SIDs
					{
						$str_output_to_write .= $user_sid . '=' . $permission . PHP_EOL;
					}
				}
			}
			else
			{
				$str_output_to_write .= $oneline;
			}
		}
		else
		{
			if (substr($oneline, 0, 1) == '*')
			{
				continue; //-- Remove ALL * access. To be improved: Might replace with Everyone?
			}
			else
			{
				$str_output_to_write .= $oneline;
			}
		}
		//echo $oneline . '<br />';
	}

	if (!empty($str_output_to_write))
	{
		$fp = fopen(SVN_REPOSITORY_ROOT_FOLDER . DIRECTORY_SEPARATOR . DEST_AUTHZ_WINDOWS_FILE, 'w');
		if (!empty($fp))
		{
			fwrite($fp, $str_output_to_write);
			fclose($fp);
			echo 'DONE!!';
		}
		else
		{
			echo 'Cannot open ' . SVN_REPOSITORY_ROOT_FOLDER . DIRECTORY_SEPARATOR . DEST_AUTHZ_WINDOWS_FILE;
		}
	}
}

Leave a Reply