]> oss.titaniummirror.com Git - repo_shell.git/blobdiff - git_acl.c
Code for supporting git ACLs. acl_test is useful.
[repo_shell.git] / git_acl.c
diff --git a/git_acl.c b/git_acl.c
new file mode 100644 (file)
index 0000000..e077f78
--- /dev/null
+++ b/git_acl.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright © 2012, Titanium Mirror, Inc.
+ * All Rights Reserved.
+ *
+ * This document is the proprietary and confidential property of
+ * Titanium Mirror, Inc.  All use, distribution, reproduction or re-distribution
+ * is disallowed without the prior express written consent of
+ * Titanium Mirror, Inc.
+ */
+
+/*
+ * Parse the git_acl.cfg file to find the first permissions therein matching the
+ * requested user and repo names.
+ *
+ * @author R. Steve McKown <rsmckown@gmail.com>
+ */
+
+#include <stdbool.h>
+//#include <stdio.h>
+//#include <errno.h>
+//#include <stdlib.h>
+//#include <sys/types.h>
+//#include <fcntl.h>
+//#include <unistd.h>
+//#include <pwd.h>
+#include <string.h>
+#include "ini.h"
+#include "utility.h"
+#include "mystrtok.h"
+#include "stra.h"
+#include "git_acl.h"
+
+enum {
+  DFLT_IDS_SZ = 32
+};
+
+typedef struct {
+  char *repo;
+  char *user;
+  stra_t repoids;
+  stra_t userids;
+  perms_t perms;
+} acl_t;
+
+const char* perm_str[PERMS_COUNT] = {
+  "NOTFOUND", "NONE", "READ", "READ_WRITE"
+};
+
+const char* lm_none = "<none>";
+static char *lm_repoid = NULL;
+static char *lm_userid = NULL;
+static char lm_perms = PERMS_NOTFOUND;
+
+static char *get_lm_repoid()
+{
+  return (lm_repoid) ? lm_repoid : (char*)lm_none;
+}
+
+static void set_lm_repoid(const char* newstr)
+{
+  if (lm_repoid)
+    free(lm_repoid);
+  if (newstr)
+    lm_repoid = xstrdup(newstr);
+}
+
+static char *get_lm_userid()
+{
+  return (lm_userid) ? lm_userid : (char*)lm_none;
+}
+
+static void set_lm_userid(const char* newstr)
+{
+  if (lm_userid)
+    free(lm_userid);
+  if (newstr)
+    lm_userid = xstrdup(newstr);
+}
+
+static const char *perms_as_str(perms_t p)
+{
+  if (p < PERMS_NOTFOUND || p >= PERMS_COUNT)
+    die("perms_as_str: invalid perm %u", p);
+  return perm_str[p];
+}
+
+static perms_t perms_from_str(const char *str)
+{
+  perms_t p = PERMS_NOTFOUND;
+
+  if (!str)
+    return PERMS_NOTFOUND;
+  else if (!*str)
+    return PERMS_NONE;
+  else if (!strcmp(str, "r"))
+    return PERMS_READ;
+  else if (!strcmp(str, "rw"))
+    return PERMS_READ_WRITE;
+  else
+    die("Invalid perms value '%s'", str);
+}
+
+static acl_init(acl_t *acl)
+{
+  acl->repo = acl->user = NULL;
+  stra_init(&acl->repoids, DFLT_IDS_SZ);
+  stra_init(&acl->userids, DFLT_IDS_SZ);
+  acl->perms = PERMS_NOTFOUND;
+}
+
+static acl_clear(acl_t *acl)
+{
+  stra_destroy(&acl->repoids);
+  stra_destroy(&acl->userids);
+}
+
+static int str_has_word(const char* string, const char* word)
+{
+  char *_s = xstrdup(string);
+  char *s = _s;
+  char *p = my_strtok(&s, " \t\n");
+
+  while (p) {
+    if (!strcmp(p, word)) {
+      free(_s);
+      return true;
+    }
+    p = my_strtok(&s, " \t\n");
+  }
+  free(_s);
+  return false;
+}
+
+static int acl_handler(void* user, const char* section, const char* name,
+    const char* value)
+{
+  acl_t* acl = (acl_t*)user;
+
+  if (acl->perms)
+    return 1; /* Already matched an ACL entry for repo+user */
+
+  //debug("section='%s', name='%s', value='%s'", section, name, value);
+
+  if (!strcmp(section, "user_groups")) {
+    if (str_has_word(value, acl->user)) {
+      //debug("userids += '%s'", name);
+      stra_add(&acl->userids, name);
+    }
+  } else if (!strcmp(section, "repo_groups")) {
+    if (str_has_word(value, acl->repo)) {
+      //debug("repoids += '%s'", name);
+      stra_add(&acl->repoids, name);
+    }
+  } else if (!strncmp(section, "repo", 4)) {
+    char *_p = xstrdup(section + 4);
+    char *p = _p;
+    char *repo = my_strtok(&p, " \t\n");
+
+    if (!repo || my_strtok(&p, " \t\n"))
+      die("acl_handler: badly formatted section '%s'", section);
+    /* repo is repo name, name is userid, value is permission */
+    if (stra_find(&acl->repoids, repo) >= 0 &&
+        stra_find(&acl->userids, name) >= 0) {
+      acl->perms = perms_from_str(value);
+      set_lm_repoid(repo);
+      set_lm_userid(name);
+      lm_perms = acl->perms;
+      //debug("match: repoid='%s', userid='%s', perms='%s'(%u)", repo, name,
+      //    value, acl->perms);
+    }
+    free(_p);
+  } else
+    die("acl_handler: unknown section='%s' name='%s'", section, name);
+  return 1;
+}
+
+int git_acl(char *user, char *repo, char *file)
+{
+  acl_t acl;
+
+  if (!file || !*file)
+    file = GIT_ACL_FILE;
+  if (!user || !*user || !repo || !*repo) {
+    die("git_acl: invalid args user='%s', repo='%s', file='%s'", user, repo,
+       file);
+  }
+
+  set_lm_repoid(NULL);
+  set_lm_userid(NULL);
+  lm_perms = PERMS_NOTFOUND;
+  acl_init(&acl);
+  acl.user = user;
+  acl.repo = repo;
+  stra_add(&acl.userids, acl.user);
+  stra_add(&acl.repoids, acl.repo);
+
+  //debug("Searching for '%s'@'%s'", acl.user, acl.repo);
+  if (ini_parse(file, acl_handler, &acl) < 0)
+    die("cannot read acl file %s", file);
+  acl_clear(&acl); /* acl.perms remains unchanged */
+  return acl.perms;
+}
+
+char *git_acl_last_repoid() { return get_lm_repoid(); }
+
+char *git_acl_last_userid() { return get_lm_userid(); }
+
+int git_acl_last_perms() { return lm_perms; }
+
+const char *git_acl_perms_as_str(perms_t p) { return perms_as_str(p); }