summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--yarns.mdwn432
1 files changed, 0 insertions, 432 deletions
diff --git a/yarns.mdwn b/yarns.mdwn
deleted file mode 100644
index 9deb11e..0000000
--- a/yarns.mdwn
+++ /dev/null
@@ -1,432 +0,0 @@
----
-title: "A Gitano ruleset: tests"
-author: Lars Wirzenius (liw@liw.fi)
-date: 2017-02-05
-...
-
-Introduction
-=============================================================================
-
-[Gitano][gitano] is the server software I use for my [personal git server][gitliwfi],
-and also at [work][ql]. One of Gitano's primary features is a strong
-language for defining access control, called [Lace][lace]. However, with
-great power comes long shoelaces that get easily loosened. This
-document explains what my ruleset does, and why. It is also a test
-suite for the ruleset, to make sure it does what it's supposed to, but
-avoids some of the more obvious pitfalls.
-
-[Gitano]: https://www.gitano.org.uk/
-[Lace]: https://www.gitano.org.uk/lace/
-[gitliwfi]: http://git.liw.fi/
-[ql]: http://qvarnlabs.com/
-
-Discussion
------------------------------------------------------------------------------
-
-* There's going to be repositories that are public to the world (in a
- read-only manner) and those that are meant to be private within a
- group of people (e.g., all of company staff, or just operations
- staff).
-
-* Some people will have full write access to some repositories,
- whereas everyone else (even those with Gitano accounts) have only
- read access to those respositories.
-
-* When outsiders have write access, it may need to be restricted, such
- that they can do things, but can't change master or other branches
- themselves, or create release tags. We call these guests.
-
-* There's automated system, such as CI, which only ever need read-only
- access. (If we find a need for a bot with write access later, we'll
- re-think then. YAGNI.)
-
-Rough outline for ruleset
------------------------------------------------------------------------------
-
-* Only a Gitano admin can create users, groups, and repositories, or
- add users to groups.
-
-* All repository access is controlled via group membership. Each
- repository is assumed to have three group associated with it:
- `writers`, `readers`, and `guests`. These are per-repository
- configuration variables, whose value should be the name of the
- Gitano group that defines who are writers, readers, or guests,
- correspondingly.
-
- For example, if `qvarn.git` has `writers` defined as
- `qvarnlabs-staff`, then only those in the `qvarnlabs-staff` group
- have full write access.
-
-* All groups have full read access. (Git doesn't really allow limiting
- read at a more granular scale than a repository.)
-
-* Writers have full write access. They can push changes, and update
- any branches and tags.
-
-* Guests are like writers, but can only update branches and tags
- prefixed by their Gitano username.
-
-* Public repositories are marked by setting the configuration variable
- `public` to `yes`. Anyone can clone public repositories, pull from
- them, and cgit shows them to anyone. Public repositories are
- published via the anonymous git prototocol.
-
-
-The ruleset change to standard Gitano ruleset
-=============================================================================
-
-The following lines added to `rules/project.lace` in the
-`gitano-admin.git` repository implement the ruleset change, on top of
-the standard Gitano ruleset.
-
- define repo_is_public config/public exact yes
- allow "Everyone can read a public repo" op_read repo_is_public
-
- define user_is_repo_reader group exact ${config/readers}
- allow "Readers may read" op_read user_is_repo_reader
-
- define user_is_repo_writer group exact ${config/writers}
- allow "Writers may read and write" op_is_basic user_is_repo_writer
- allow "Writers may update any branch" op_is_normal user_is_repo_writer
-
- define user_is_repo_guest group exact ${config/guests}
- define ref_is_for_user ref prefix ${user}/
- allow "Guests may read and write" op_is_basic user_is_repo_guest
- allow "Guests may update their own refs" op_is_normal user_is_repo_guest
-
-
-Use cases as automated test scenarios
-=============================================================================
-
-These [yarn][yarn] scenarios need to be run as a Gitano user who is in the
-`gitano-admins` group so that they can create users, and do other
-priviledged operations. Further, this should be done against a fresh
-Gitano, without the test users etc.
-
-[yarn]: http://liw.fi/cmdtest/
-
-Personas for use cases
------------------------------------------------------------------------------
-
-* **Ian Inhouse Developer** is a staff member and works on a free software
- project with the random silly name Qvarn.
-
-* **Olive Opshead** is a sysadmin and needs to maintain ops related
- repositories, some of which are sensitive.
-
-* **Steven Sect** is the company secretary and needs to access some
- private respositories with internal company data.
-
-* **Gabriella Guest** is an outside developer, collaborating on Qvarn
- with Ian. Gabriella has been granted restricted commit access to the
- Qvarn repository.
-
-* **CI** is an automated system that monitors some repositories to
- build, test, and deliver software.
-
-Ian makes a bug fix in Qvarn
------------------------------------------------------------------------------
-
- SCENARIO Ian can make a bug fix in Qvarn
- WHEN admin creates user ian
- AND admin creates group qvarndevs
- AND admin adds ian to qvarndevs
- AND admin creates repository qvarn
- AND admin sets qvarn config writers to qvarndevs
- THEN ian can clone qvarn
- WHEN ian creates qvarn branch bugfix
- AND ian changes qvarn branch bugfix
- THEN ian can push qvarn
- WHEN ian merges qvarn branch bugfix to master
- THEN ian can push qvarn
- FINALLY admin removes things that were created
-
-Steven can see Qvarn, but not push changes
------------------------------------------------------------------------------
-
- SCENARIO Steven can clone Qvarn but not push changes
- WHEN admin creates user steven
- AND admin creates group qvarn-readers
- AND admin adds steven to qvarn-readers
- AND admin creates repository qvarn
- AND admin sets qvarn config readers to qvarn-readers
- AND admin sets qvarn config writers to qvarn-writers
- THEN steven can clone qvarn
- WHEN steven creates qvarn branch bugfix
- AND steven changes qvarn branch bugfix
- AND steven merges qvarn branch bugfix to master
- THEN steven cannot push qvarn
- FINALLY admin removes things that were created
-
-Ian can make a Qvarn release
------------------------------------------------------------------------------
-
- SCENARIO Ian can make a Qvarn release
- WHEN admin creates user ian
- AND admin creates group qvarndevs
- AND admin adds ian to qvarndevs
- AND admin creates repository qvarn
- AND admin sets qvarn config writers to qvarndevs
- THEN ian can clone qvarn
- WHEN ian creates qvarn branch bugfix
- AND ian changes qvarn branch bugfix
- THEN ian can push qvarn
- WHEN ian merges qvarn branch bugfix to master
- AND ian tags qvarn master branch with qvarn-1.0
- THEN ian can push qvarn with tags
- FINALLY admin removes things that were created
-
-Steven can't push a release tag
------------------------------------------------------------------------------
-
- SCENARIO Steven can't tag a Qvarn release
- WHEN admin creates user steven
- AND admin creates group qvarn-readers
- AND admin creates group qvarn-writers
- AND admin adds steven to qvarn-readers
- AND admin creates repository qvarn
- AND admin sets qvarn config readers to qvarn-readers
- AND admin sets qvarn config writers to qvarn-writers
- THEN steven can clone qvarn
- WHEN steven tags qvarn master branch with qvarn-2.0
- THEN steven cannot push qvarn with tags
- FINALLY admin removes things that were created
-
-Gabriella can push changes and tag with her own prefix
------------------------------------------------------------------------------
-
- SCENARIO Gabriella can change and tag her own branch
- WHEN admin creates user gabriella
- AND admin creates group qvarn-guests
- AND admin adds gabriella to qvarn-guests
- AND admin creates repository qvarn
- AND admin sets qvarn config guests to qvarn-guests
- THEN gabriella can clone qvarn
- WHEN gabriella creates qvarn branch gabriella/bugfix
- AND gabriella changes qvarn branch gabriella/bugfix
- THEN gabriella can push qvarn
- WHEN gabriella tags qvarn gabriella/bugfix branch with gabriella/works
- THEN gabriella can push qvarn with tags
- FINALLY admin removes things that were created
-
-Steven can't read the ops/secrets repo
------------------------------------------------------------------------------
-
- SCENARIO Steven can't clone ops/secrets
- WHEN admin creates user steven
- AND admin creates repository ops/secrets
- THEN steven cannot clone ops/secrets
- FINALLY admin removes things that were created
-
-A public repository can be clone with the anonymous git protocol
------------------------------------------------------------------------------
-
- SCENARIO everyone can clone a public repository
- WHEN admin creates repository qvarn
- AND admin sets qvarn config public to yes
- THEN we can clone qvarn via the git protocol
- FINALLY admin removes things that were created
-
-Scenario step implementations
-=============================================================================
-
-WHEN admin creates user
------------------------------------------------------------------------------
-
- IMPLEMENTS WHEN admin creates user (\S+)
- username = helper.get_next_match()
- # FIXME: Create first, this is temporary
- helper.append_to_list('users', username)
- helper.gitano(
- None,
- 'user add {} {}@example.com Test {}'.format(
- username, username, username))
- pubkey = helper.ssh_keygen(username)
- helper.gitano(None,
- 'as {} sshkey add default'.format(username), stdin=pubkey)
-
-WHEN admin creates group
------------------------------------------------------------------------------
-
- IMPLEMENTS WHEN admin creates group (\S+)
- group = helper.get_next_match()
- # FIXME: Create first, this is temporary
- helper.append_to_list('groups', group)
- helper.gitano(None, 'group add {} Test group'.format(group))
-
-WHEN admin adds user to group
------------------------------------------------------------------------------
-
- IMPLEMENTS WHEN admin adds (\S+) to (\S+)
- user = helper.get_next_match()
- group = helper.get_next_match()
- output = helper.gitano(
- None, 'group adduser {} {}'.format(group, user))
-
-WHEN admin created repository
------------------------------------------------------------------------------
-
- IMPLEMENTS WHEN admin creates repository (\S+)
- repo = helper.get_next_match()
- # FIXME: Create first, this is temporary
- helper.append_to_list('repositories', repo)
- helper.gitano(None, 'create {}'.format(repo))
-
- # Make a commit in master in the repo, push that.
- user = 'admin'
- url = helper.repo_ssh_url(repo)
- dirname = helper.local_checkout_dirname(user, repo)
- helper.git_as_checked(None, ['clone', url, dirname])
- cliapp.runcmd(['git', 'config', 'user.email', user], cwd=dirname)
- cliapp.runcmd(['git', 'config', 'user.name', user], cwd=dirname)
- with open(os.path.join(dirname, 'foo.txt'), 'a') as f:
- f.write('')
- cliapp.runcmd(['git', 'add', 'foo.txt'], cwd=dirname)
- cliapp.runcmd(['git', 'commit', '-mfoo'], cwd=dirname)
- helper.git_as_checked(None, ['push', '--all'], cwd=dirname)
-
-WHEN admin sets repository config
------------------------------------------------------------------------------
-
- IMPLEMENTS WHEN admin sets (\S+) config (\S+) to (\S+)
- repo = helper.get_next_match()
- key = helper.get_next_match()
- value = helper.get_next_match()
- helper.gitano(None, 'config {} set {} {}'.format(repo, key, value))
-
-THEN a user can clone a repository
------------------------------------------------------------------------------
-
- IMPLEMENTS THEN (\S+) can clone (\S+)
- user = helper.get_next_match()
- repo = helper.get_next_match()
- url = helper.repo_ssh_url(repo)
- dirname = helper.local_checkout_dirname(user, repo)
- helper.git_as_checked(user, ['clone', url, dirname])
- cliapp.runcmd(['git', 'config', 'user.email', user], cwd=dirname)
- cliapp.runcmd(['git', 'config', 'user.name', user], cwd=dirname)
-
-THEN a user can't clone a repository
------------------------------------------------------------------------------
-
- IMPLEMENTS THEN (\S+) cannot clone (\S+)
- user = helper.get_next_match()
- repo = helper.get_next_match()
- url = helper.repo_ssh_url(repo)
- dirname = helper.local_checkout_dirname(user, repo)
- exit, out, err = helper.git_as(user, ['clone', url, dirname])
- helper.assertNotEqual(exit, 0)
-
-THEN a repository can be cloned over the git protocol
------------------------------------------------------------------------------
-
- IMPLEMENTS THEN we can clone (\S+) via the git protocol
- repo = helper.get_next_match()
- server = os.environ['GITANO_SERVER']
- url = 'git://{}/{}'.format(server, repo)
- dirname = helper.local_checkout_dirname('anonymous', repo)
- cliapp.runcmd(['git', 'clone', url, dirname])
-
-WHEN a user creates a local branch
------------------------------------------------------------------------------
-
- IMPLEMENTS WHEN (\S+) creates (\S+) branch (\S+)
- user = helper.get_next_match()
- repo = helper.get_next_match()
- branch = helper.get_next_match()
- dirname = helper.local_checkout_dirname(user, repo)
- cliapp.runcmd(['git', 'checkout', '-b', branch], cwd=dirname)
-
-WHEN a user changes a branch locally
------------------------------------------------------------------------------
-
- IMPLEMENTS WHEN (\S+) changes (\S+) branch (\S+)
- user = helper.get_next_match()
- repo = helper.get_next_match()
- branch = helper.get_next_match()
- dirname = helper.local_checkout_dirname(user, repo)
- with open(os.path.join(dirname, 'foo.txt'), 'a') as f:
- f.write('foo\n')
- cliapp.runcmd(['git', 'add', 'foo.txt'], cwd=dirname)
- cliapp.runcmd(['git', 'commit', '-mfoo'], cwd=dirname)
-
-THEN a user can push all local branches
------------------------------------------------------------------------------
-
- IMPLEMENTS THEN (\S+) can push (\S+)
- user = helper.get_next_match()
- repo = helper.get_next_match()
- url = helper.repo_ssh_url(repo)
- dirname = helper.local_checkout_dirname(user, repo)
- helper.git_as_checked(user, ['push', '--all', 'origin'], cwd=dirname)
-
-THEN a user can push all local tags
------------------------------------------------------------------------------
-
- IMPLEMENTS THEN (\S+) can push (\S+) with tags
- user = helper.get_next_match()
- repo = helper.get_next_match()
- url = helper.repo_ssh_url(repo)
- dirname = helper.local_checkout_dirname(user, repo)
- helper.git_as_checked(user, ['push', '--tags', 'origin'], cwd=dirname)
-
-THEN a user can't push local branches
------------------------------------------------------------------------------
-
- IMPLEMENTS THEN (\S+) cannot push (\S+)
- user = helper.get_next_match()
- repo = helper.get_next_match()
- dirname = helper.local_checkout_dirname(user, repo)
- exit, out, err = helper.git_as(
- user,
- ['push', '--all', 'origin'], cwd=dirname)
- sys.stdout.write(out)
- sys.stderr.write(err)
- helper.assertNotEqual(exit, 0)
-
-THEN a user can't push local tags
------------------------------------------------------------------------------
-
- IMPLEMENTS THEN (\S+) cannot push (\S+) with tags
- user = helper.get_next_match()
- repo = helper.get_next_match()
- dirname = helper.local_checkout_dirname(user, repo)
- exit, out, err = helper.git_as(
- user,
- ['push', '--tags', 'origin'], cwd=dirname)
- helper.assertNotEqual(exit, 0)
-
-WHEN a user merges a branch locally
------------------------------------------------------------------------------
-
- IMPLEMENTS WHEN (\S+) merges (\S+) branch (\S+) to (\S+)
- user = helper.get_next_match()
- repo = helper.get_next_match()
- branch_from = helper.get_next_match()
- branch_to = helper.get_next_match()
- dirname = helper.local_checkout_dirname(user, repo)
- cliapp.runcmd(['git', 'checkout', branch_to], cwd=dirname)
- cliapp.runcmd(['git', 'merge', branch_from], cwd=dirname)
-
-WHEN a user creates a local tag
------------------------------------------------------------------------------
-
- IMPLEMENTS WHEN (\S+) tags (\S+) (\S+) branch with (\S+)
- user = helper.get_next_match()
- repo = helper.get_next_match()
- branch = helper.get_next_match()
- tag = helper.get_next_match()
- dirname = helper.local_checkout_dirname(user, repo)
- cliapp.runcmd(['git', 'tag', '-mrelease!', tag], cwd=dirname)
-
-FINALLY clean up anything created during tests
------------------------------------------------------------------------------
-
- IMPLEMENTS FINALLY admin removes things that were created
- def iter(var, prefix):
- items = helper.get_variable(var, [])
- for item in items:
- helper.gitano_confirm_with_token(prefix, item)
- iter('users', 'user del')
- iter('groups', 'group del')
- iter('repositories', 'destroy')