/***************************************************************************
 * CVSID: $Id: polkitd-test.c,v 1.1 2006-03-14 06:14:33 david Exp $
 *
 * polkitd-test.c : Test harness for PolicyKit daemon
 *
 * Copyright (C) 2006 David Zeuthen, <david@fubar.dk>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307	 USA
 *
 **************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#include <glib/gstdio.h>

#include "policy.h"

static char *testdir;

static void 
my_exit (int exit_code)
{
	int rc;
	GDir *dir;
	GError *err;
	const char *f;

	g_print ("Removing tmpdir '%s'\n", testdir);
	
	err = NULL;
	if ((dir = g_dir_open (testdir, 0, &err)) == NULL) {
		g_warning ("Unable to open %s: %s", testdir, err->message);
		g_error_free (err);
		goto error;
	}
	while ((f = g_dir_read_name (dir)) != NULL) {
		char *file;

		file = g_strdup_printf ("%s/%s", testdir, f);
		rc = g_unlink (file);
		if (rc != 0) {
			g_warning ("Unable to unlink %s: %d (%s)", file, errno, strerror (errno));
			goto error;
		}
		g_free (file);
	}

	g_dir_close (dir);
	
	rc = g_rmdir (testdir);
	if (rc != 0) {
		g_warning ("Unable to rmdir %s: %d (%s)", testdir, errno, strerror (errno));
		goto error;
	}

error:	
	exit (exit_code);
}

static void 
do_check (const char *policy,
	  uid_t uid,
	  int num_gids,
	  gid_t *gids,
	  const char *resource,
	  gboolean expected)
{
	int i;
	gboolean allowed;
	char *gidstring;
	GString *str;

	str = g_string_new ("");
	for (i = 0; i < num_gids; i++) {
		if (i != 0)
			g_string_append (str, ", ");
		g_string_append_printf (str, "%d", gids[i]);
	}
	gidstring = g_string_free (str, FALSE);

	if (POLICY_RESULT_OK != policy_is_uid_gid_allowed_for_policy (
		    uid, num_gids, gids, policy, resource, &allowed)) {
		g_warning ("fail: no policy %s", policy);
		my_exit (1);
	}
	
	if (allowed != expected) {
		g_warning ("fail: for uid %d (gids %s) expected %s on privilege '%s' for resource '%s' but got %s", 
			   uid, gidstring, 
			   expected ? "TRUE" : "FALSE", 
			   policy, 
			   (char*) (resource != NULL ? resource : ""), 
			   allowed ? "TRUE" : "FALSE");
		my_exit (1);
	}
	
	g_print ("pass: uid %d (gids %s) got %s on privilege '%s' for resource '%s'\n", 
		 uid, gidstring, 
		 expected ? "TRUE " : "FALSE", 
		 policy, 
		 (char*) (resource != NULL ? resource : ""));

	g_free (gidstring);
}

static void
write_test_policy (const char *policy, const char *allow_rule, const char *deny_rule)
{
	char *file;
	FILE *f;

	file = g_strdup_printf ("%s/%s.privilege", testdir, policy);
	f = fopen (file, "w");
	if (f == NULL) {
		g_warning ("Cannot created test policy '%s'", file);
		my_exit (1);
	}
	fprintf (f, 
		 "[Policy]\n"
		 "Allow=%s\n"
		 "Deny=%s\n", 
		 allow_rule, deny_rule);
	fclose (f);

	g_print ("Created test policy '%s' at '%s'\n"
		 "  Allow '%s'\n"
		 "  Deny  '%s'\n",
		 policy, file, allow_rule, deny_rule);

	g_free (file);
}

static void
do_read_tests (void)
{
	gid_t gid500[1] = {500};
	int gid500_len = sizeof (gid500) / sizeof (gid_t);
	gid_t gid501[1] = {501};
	int gid501_len = sizeof (gid501) / sizeof (gid_t);
	gid_t gid502[1] = {502};
	int gid502_len = sizeof (gid502) / sizeof (gid_t);

	gid_t gid500_1[2] = {500, 1};
	int gid500_1_len = sizeof (gid500_1) / sizeof (gid_t);
	gid_t gid501_1[2] = {501, 1};
	int gid501_1_len = sizeof (gid501_1) / sizeof (gid_t);
	gid_t gid502_1[2] = {502, 1};
	int gid502_1_len = sizeof (gid502_1) / sizeof (gid_t);

	/* feel free to add more tests here */

	write_test_policy ("test0", "uid:__none__ uid:500", "");
	do_check ("test0", 500, gid500_len, gid500, NULL, TRUE);
	do_check ("test0", 501, gid501_len, gid501, NULL, FALSE);
	do_check ("test0", 502, gid502_len, gid502, NULL, FALSE);

	write_test_policy ("test1", "uid:__all__", "uid:500:res0");
	do_check ("test1", 500, gid500_len, gid500, NULL, TRUE);
	do_check ("test1", 501, gid501_len, gid501, NULL, TRUE);
	do_check ("test1", 502, gid502_len, gid502, NULL, TRUE);
	do_check ("test1", 500, gid500_len, gid500, "res0", FALSE);
	do_check ("test1", 501, gid501_len, gid501, "res0", TRUE);
	do_check ("test1", 502, gid502_len, gid502, "res0", TRUE);
	do_check ("test1", 500, gid500_len, gid500, "res1", TRUE);
	do_check ("test1", 501, gid501_len, gid501, "res1", TRUE);
	do_check ("test1", 502, gid502_len, gid502, "res1", TRUE);
	
	write_test_policy ("test2", "gid:1", "uid:501");	
	do_check ("test2", 500, gid500_len, gid500, NULL, FALSE);
	do_check ("test2", 501, gid501_len, gid501, NULL, FALSE);
	do_check ("test2", 502, gid502_len, gid502, NULL, FALSE);
	do_check ("test2", 500, gid500_1_len, gid500_1, NULL, TRUE);
	do_check ("test2", 501, gid501_1_len, gid501_1, NULL, FALSE);
	do_check ("test2", 502, gid502_1_len, gid502_1, NULL, TRUE);
	
	write_test_policy ("test3", "gid:1 uid:502:res1", "uid:501 uid:500:res0");	
	do_check ("test3", 500, gid500_1_len, gid500_1, "res0", FALSE);
	do_check ("test3", 501, gid501_1_len, gid501_1, "res0", FALSE);
	do_check ("test3", 502, gid502_1_len, gid502_1, "res0", TRUE);
	do_check ("test3", 500, gid500_1_len, gid500_1, "res1", TRUE);
	do_check ("test3", 501, gid501_1_len, gid501_1, "res1", FALSE);
	do_check ("test3", 502, gid502_1_len, gid502_1, "res1", TRUE);
	do_check ("test3", 500, gid500_len, gid500, "res1", FALSE);
	do_check ("test3", 501, gid501_len, gid501, "res1", FALSE);
	do_check ("test3", 502, gid502_len, gid502, "res1", TRUE);

	write_test_policy ("test4", "gid:1:res1 uid:500:res2", "gid:502:res2");	
	do_check ("test4", 500, gid500_1_len, gid500_1, "res0", FALSE);
	do_check ("test4", 501, gid501_1_len, gid501_1, "res0", FALSE);
	do_check ("test4", 502, gid502_1_len, gid502_1, "res0", FALSE);
	do_check ("test4", 500, gid500_1_len, gid500_1, "res1", TRUE);
	do_check ("test4", 501, gid501_1_len, gid501_1, "res1", TRUE);
	do_check ("test4", 502, gid502_1_len, gid502_1, "res1", TRUE);
	do_check ("test4", 500, gid500_len, gid500, "res2", TRUE);
	do_check ("test4", 501, gid501_len, gid501, "res2", FALSE);
	do_check ("test4", 502, gid502_len, gid502, "res2", FALSE);

	write_test_policy ("test5", "gid:1", "uid:500:res-has-:colon-in-name");	
	do_check ("test5", 500, gid500_1_len, gid500_1, "res-has-:colon-in-name", FALSE);
	do_check ("test5", 501, gid501_1_len, gid501_1, "res-has-:colon-in-name", TRUE);
	do_check ("test5", 502, gid502_1_len, gid502_1, "res-has-:colon-in-name", TRUE);
	do_check ("test5", 500, gid500_len, gid500, "res-has-:colon-in-name", FALSE);
	do_check ("test5", 501, gid501_len, gid501, "res-has-:colon-in-name", FALSE);
	do_check ("test5", 502, gid502_len, gid502, "res-has-:colon-in-name", FALSE);

}

int 
main (int argc, char *argv[])
{
	int i;
	GList *l;
	GList *policies;

	testdir = g_strdup ("/tmp/policy-test-XXXXXX");
	testdir = mkdtemp (testdir);
	if (testdir == NULL) {
		g_warning ("Cannot create tmpdir, errno %d (%s)", errno, strerror (errno));
		g_free (testdir);
		exit (1);
	}

	g_message ("policy-test started; using tmpdir=%s", testdir);

	policy_util_set_policy_directory (testdir);

	do_read_tests ();

	if (policy_get_policies (&policies) != POLICY_RESULT_OK) {
		g_message ("Cannot get policies");
		goto fail;
	}
	g_print ("Loaded %d policies\n", g_list_length (policies));
	for (l = policies, i = 0; l != NULL; l = g_list_next (l), i++) {
		const char *policy;
		policy = (const char *) l->data;
		g_print (" policy %d: '%s'\n", i, policy);
	}
	g_list_foreach (policies, (GFunc) g_free, NULL);
	g_list_free (policies);

	g_print ("policy-test completed\n");

	my_exit (0);

fail:
	my_exit (1);
	return 1;
}


syntax highlighted by Code2HTML, v. 0.9.1