]> source.dussan.org Git - gitblit.git/commitdiff
Finer-grained repository access permissions (issue 36)
authorJames Moger <james.moger@gitblit.com>
Wed, 10 Oct 2012 04:05:34 +0000 (00:05 -0400)
committerJames Moger <james.moger@gitblit.com>
Wed, 10 Oct 2012 04:05:34 +0000 (00:05 -0400)
Implemented discrete repository access permissions to replace the
really primitive course-grained permissions used to this point.  This
implementation allows for finer-grained access control, but still
falls short of integrated, branch-based permissions sought by some.

Access permissions follow the conventions established by Gitosis and
Gitolite so they should feel immediately comfortable to experienced
users.  This permissions infrastructure is complete and works exactly as
expected.  Unfortunately, there is no ui in this commit to change
permissions, that will be forthcoming.  In the meantime, Gitblit
hot-reloads users.conf so the permissions can be manipulated at runtime
with a text editor.

The following per-repository permissions are now supported:
- V (view in web ui, RSS feeds, download zip)
- R (clone)
- RW (clone and push)
- RWC (clone and push with ref creation)
- RWD (clone and push with ref creation, deletion)
- RW+ (clone and push with ref creation, deletion, rewind)

And a users.conf entry looks something like this:

[user "hannibal"]
password = bossman
repository = RWD:topsecret.git

34 files changed:
docs/01_features.mkd
docs/01_setup.mkd
docs/04_releases.mkd
docs/permissions_matrix.ods [new file with mode: 0644]
docs/permissions_matrix.png [new file with mode: 0644]
src/com/gitblit/ConfigUserService.java
src/com/gitblit/Constants.java
src/com/gitblit/DownloadZipFilter.java
src/com/gitblit/FederationPullExecutor.java
src/com/gitblit/FileUserService.java
src/com/gitblit/GitBlit.java
src/com/gitblit/GitFilter.java
src/com/gitblit/GitServlet.java
src/com/gitblit/GitblitUserService.java
src/com/gitblit/IUserService.java
src/com/gitblit/PagesFilter.java
src/com/gitblit/SyndicationFilter.java
src/com/gitblit/models/RepositoryModel.java
src/com/gitblit/models/TeamModel.java
src/com/gitblit/models/UserModel.java
src/com/gitblit/utils/JsonUtils.java
src/com/gitblit/wicket/pages/BasePage.java
src/com/gitblit/wicket/pages/ForkPage.java
src/com/gitblit/wicket/pages/ForksPage.java
src/com/gitblit/wicket/pages/RepositoryPage.java
src/com/gitblit/wicket/pages/RootPage.java
tests/com/gitblit/tests/FederationTests.java
tests/com/gitblit/tests/GitBlitSuite.java
tests/com/gitblit/tests/GitBlitTest.java
tests/com/gitblit/tests/GitServletTest.java
tests/com/gitblit/tests/JGitUtilsTest.java
tests/com/gitblit/tests/PermissionsTest.java [new file with mode: 0644]
tests/com/gitblit/tests/RpcTests.java
tests/com/gitblit/tests/UserServiceTest.java

index 2baacf20d1eecce538a6433ba3c31091f169e6c8..e1c0afa477913c3a6036a49915a336fde50fea74 100644 (file)
@@ -1,12 +1,21 @@
 ## Standard Features (GO/WAR)\r
 - JGit SmartHTTP servlet\r
 - Browser and git client authentication\r
-- Four *per-repository* access control configurations with a Read-Only control flag\r
+- Four *per-repository* access restriction configurations with a Read-Only control flag\r
     - ![anonymous](blank.png) *Anonymous View, Clone & Push*\r
     - ![push](lock_go_16x16.png) *Authenticated Push*\r
     - ![clone](lock_pull_16x16.png) *Authenticated Clone & Push*\r
     - ![view](shield_16x16.png) *Authenticated View, Clone & Push*\r
     - ![freeze](cold_16x16.png) Freeze repository (i.e. deny push, make read-only)\r
+- Six *per-user/team* repository access permissions\r
+    - **V** (view in web ui, RSS feeds, download zip)\r
+    - **R** (clone)\r
+    - **RW** (clone and push)\r
+    - **RWC** (clone and push with ref creation)\r
+    - **RWD** (clone and push with ref creation, deletion)\r
+    - **RW+** (clone and push with ref creation, deletion, rewind)\r
+- Optional feature to allow users to create personal repositories\r
+- Optional feature to fork a repository to a personal repository\r
 - Ability to federate with one or more other Gitblit instances\r
 - RSS/JSON RPC interface\r
 - Java/Swing Gitblit Manager tool \r
@@ -38,7 +47,7 @@
 - Single text file for users configuration\r
 - Optional utility pages\r
     - ![docs](book_16x16.png) Docs page which enumerates all Markdown files within a repository\r
-    - ![tickets](bug_16x16.png) Ticgit ticket pages *(based on last MIT release bf57b032 2009-01-27)*\r
+    - ![tickets](bug_16x16.png) **readonly** Ticgit ticket pages *(based on last MIT release bf57b032 2009-01-27)*\r
 - Translations\r
     - English\r
     - Japanese\r
index fa1bcd908c2cb0e1e48aaba2db2bb7fcfd7db939..1cebb3b78105744feae30350a6255932f8492311 100644 (file)
@@ -244,6 +244,25 @@ All repositories created with Gitblit are *bare* and will automatically have *.g
 #### Repository Owner\r
 The *Repository Owner* has the special permission of being able to edit a repository through the web UI.  The Repository Owner is not permitted to rename the repository, delete the repository, or reassign ownership to another user.\r
 \r
+### Access Restrictions and Access Permissions\r
+![permissions matrix](permissions_matrix.png "Permissions and Restrictions")\r
+\r
+#### Discrete Permissions (Gitblit v1.2.0+)\r
+\r
+Since v1.2.0, Gitblit supports more discrete permissions.  While Gitblit does not offer a built-in solution for branch-based permissions like Gitolite, it does allow for the following repository access permissions:\r
+\r
+- **V** (view in web ui, RSS feeds, download zip)\r
+- **R** (clone)\r
+- **RW** (clone and push)\r
+- **RWC** (clone and push with ref creation)\r
+- **RWD** (clone and push with ref creation, deletion)\r
+- **RW+** (clone and push with ref creation, deletion, rewind)\r
+\r
+#### No-So-Discrete Permissions (Gitblit <= v1.1.0)\r
+\r
+Prior to v1.2.0, Gitblit had two main access permission groupings:  \r
+What you were permitted to do as an anonymous user and then **RW+** for any permitted user.\r
+\r
 ### Teams\r
 \r
 Since v0.8.0, Gitblit supports *teams* for the original `users.properties` user service and the current default user service `users.conf`.  Teams have assigned users and assigned repositories.  A user can be a member of multiple teams and a repository may belong to multiple teams.  This allows the administrator to quickly add a user to a team without having to keep track of all the appropriate repositories. \r
@@ -257,11 +276,12 @@ The `users.conf` file uses a Git-style configuration format:
            password = admin\r
            role = "#admin"\r
            role = "#notfederated"\r
-           repository = repo1.git\r
-           repository = repo2.git\r
+           repository = RW+:repo1.git\r
+           repository = RW+:repo2.git\r
            \r
        [user "hannibal"]\r
                password = bossman\r
+               repository = RWD:topsecret.git\r
 \r
        [user "faceman"]\r
                password = vanity\r
@@ -277,7 +297,7 @@ The `users.conf` file uses a Git-style configuration format:
                user = faceman\r
                user = murdock\r
                user = babaracus\r
-               repository = topsecret.git\r
+               repository = RW:topsecret.git\r
                mailingList = list@ateam.org\r
                postReceiveScript = sendmail\r
 \r
@@ -291,15 +311,49 @@ The format of `users.properties` loosely follows Jetty's convention for HashReal
     username=password,role1,role2,role3...\r
     @teamname=&mailinglist,!username1,!username2,!username3,repository1,repository2,repository3...\r
 \r
-#### Usernames\r
+### Usernames\r
 Usernames must be unique and are case-insensitive.  \r
 Whitespace is illegal.\r
 \r
-#### Passwords\r
+### Passwords\r
 User passwords are CASE-SENSITIVE and may be *plain*, *md5*, or *combined-md5* formatted (see `gitblit.properties` -> *realm.passwordStorage*).\r
 \r
-#### User Roles\r
-There are two actual *roles* in Gitblit: *#admin*, which grants administrative powers to that user, and *#notfederated*, which prevents an account from being pulled by another Gitblit instance.  Administrators automatically have access to all repositories.  All other *roles* are repository names.  If a repository is access-restricted, the user must have the repository's name within his/her roles to bypass the access restriction.  This is how users are granted access to a restricted repository.\r
+### User Roles\r
+There are four actual *roles* in Gitblit:\r
+\r
+- *#admin*, which grants administrative powers to that user\r
+- *#notfederated*, which prevents an account from being pulled by another Gitblit instance\r
+- *#create*, which allows the user the power to create personal repositories\r
+- *#fork*, which allows the user to create a personal fork of an existing Gitblit-hosted repository\r
+\r
+Administrators automatically have access to all repositories.  All other *roles* are repository permissions.  If a repository is access-restricted, the user must have the repository's name within his/her roles to bypass the access restriction.  This is how users are granted access to a restricted repository.\r
+\r
+**NOTE:**  \r
+The following roles are equivalent:\r
+\r
+- myrepo.git\r
+- RW+:myrepo.git\r
+\r
+This is to preserve backwards-compatibility with Gitblit <= 1.1.0 which granted rewind power to all access-permitted users.\r
+\r
+### Personal Repositories & Forks\r
+\r
+Personal Repositories and Forks are related but are controlled individually.\r
+\r
+#### Creating a Personal Repository\r
+A user may be granted the power to create personal repositories by specifying the *#create* role through the web ui or through the RPC mechanism via the Gitblit Manager.  Personal repositories are exactly like common/shared repositories except that the owner has a few additional administrative powers for that repository, like rename and delete.\r
+\r
+#### Creating a Fork\r
+A user may also be granted the power to fork an existing repository hosted on your Gitblit server to their own personal clone by specifying the *#fork* role through the web ui or via the Gitblit Manager.\r
+\r
+Forks are mostly likely personal repositories or common/shared repositories except for two important differences:\r
+\r
+1. Forks inherit a view/clone access list from the origin repository.  \r
+i.e. if Team A has clone access to the origin repository, then by default Team A also has clone access to the fork.  This is to facilitate collaboration.\r
+2. Forks are always listed in the fork network, regardless of any access restriction set on the fork.  \r
+In other words, if you fork *RepoA.git* to *~me/RepoA.git* and then set the access restriction of *~me/RepoA.git* to *Authenticated View, Clone, & Push* your fork will still be listed in the fork network for *RepoA.git*.\r
+\r
+If you really must have an invisible fork, the clone it locally, create a new personal repository for your invisible fork, and push it back to that personal repository.\r
 \r
 ## Alternative Authentication and Authorization\r
 \r
index 31c503c059b2a6e66dd6e1cf931eecff24a5c34a..bb9b734077a612d25fb24bc96c9a7e25da7cb910 100644 (file)
@@ -17,18 +17,26 @@ If you are updating from an earlier release AND you have indexed branches with t
 - Fixed connection leak in LDAPUserService (issue 139)\r
 - Fixed bug in commit page where changes to a submodule threw a null pointer exception (issue 132)\r
 - Fixed bug in the diff view for filenames that have non-ASCII characters (issue 128)\r
-- Fix missing translations in Gitblit Manager (issue 145)\r
 \r
 #### additions\r
 \r
-- Added simple project pages.  A project is a subfolder off the *git.repositoriesFolder*.\r
+- Implemented discrete repository permissions (issue 36)  \r
+    - V (view in web ui, RSS feeds, download zip)\r
+    - R (clone)\r
+    - RW (clone and push)\r
+    - RWC (clone and push with ref creation)\r
+    - RWD (clone and push with ref creation, deletion)\r
+    - RW+ (clone and push with ref creation, deletion, rewind)  \r
+While not as sophisticated as Gitolite, this does give finer access controls.  These permissions fit in cleanly with the existing users.conf and users.properties files.  In Gitblit <= 1.1.0, all your existing user accounts have RW+ access.   If you are upgrading to 1.2.0, the RW+ access is *preserved* and you will have to lower/adjust accordingly.\r
+- Added DELETE, CREATE, and NON-FAST-FORWARD ref change logging\r
 - Added support for personal repositories.  \r
 Personal repositories can be created by accounts with the *create* permission and are stored in *git.repositoriesFolder/~username*.  Each user with personal repositories will have a user page, something like the GitHub profile page.  Personal repositories have all the same features as common repositories, except personal repositories can be renamed by their owner.\r
 - Added support for server-side forking of a repository to a personal repository (issue 137)  \r
-In order to fork a repository, the user account must have the *fork* permission **and** the repository must *allow forks*.  The clone inherits the access restrictions of its origin.  i.e. if Team A has access to the origin repository, then by default Team A also has access to the fork.  This is to facilitate collaboration.  The fork owner may change access to the fork and add/remove users/teams, etc as required __however__ it should be noted that all personal forks will be enumerated in the fork network regardless of access view restrictions.  If you really must have an invisible fork, the clone it locally, create a new repository for your invisible fork, and push it back.\r
+In order to fork a repository, the user account must have the *fork* permission **and** the repository must *allow forks*.  The clone inherits the access list of its origin.  i.e. if Team A has clone access to the origin repository, then by default Team A also has clone access to the fork.  This is to facilitate collaboration.  The fork owner may change access to the fork and add/remove users/teams, etc as required <u>however</u> it should be noted that all personal forks will be enumerated in the fork network regardless of access view restrictions.  If you really must have an invisible fork, the clone it locally, create a new repository for your invisible fork, and push it back to Gitblit.\r
+- Added simple project pages.  A project is a subfolder off the *git.repositoriesFolder*.\r
 - Added support for X-Forwarded-Context for Apache subdomain proxy configurations (issue 135)\r
 - Delete branch feature (issue 121, Github/ajermakovics)\r
-- Added line links to blob view at the expense of zebra striping (issue 130)\r
+- Added line links to blob view (issue 130)\r
 - Added RedmineUserService (github/mallowlabs)\r
 \r
 #### changes\r
diff --git a/docs/permissions_matrix.ods b/docs/permissions_matrix.ods
new file mode 100644 (file)
index 0000000..6df0b4d
Binary files /dev/null and b/docs/permissions_matrix.ods differ
diff --git a/docs/permissions_matrix.png b/docs/permissions_matrix.png
new file mode 100644 (file)
index 0000000..d463ae1
Binary files /dev/null and b/docs/permissions_matrix.png differ
index 831ede9b15a35b1c138ab39871f35573b46289fb..d2740094a5a4c8350920afe8794c83a9bec3c122 100644 (file)
@@ -33,6 +33,7 @@ import org.eclipse.jgit.util.FS;
 import org.slf4j.Logger;\r
 import org.slf4j.LoggerFactory;\r
 \r
+import com.gitblit.Constants.AccessPermission;\r
 import com.gitblit.models.TeamModel;\r
 import com.gitblit.models.UserModel;\r
 import com.gitblit.utils.ArrayUtils;\r
@@ -267,6 +268,55 @@ public class ConfigUserService implements IUserService {
                return updateUserModel(model.username, model);\r
        }\r
 \r
+       /**\r
+        * Updates/writes all specified user objects.\r
+        * \r
+        * @param models a list of user models\r
+        * @return true if update is successful\r
+        * @since 1.2.0\r
+        */\r
+       @Override\r
+       public boolean updateUserModels(List<UserModel> models) {\r
+               try {\r
+                       read();\r
+                       for (UserModel model : models) {\r
+                               UserModel originalUser = users.remove(model.username.toLowerCase());\r
+                               users.put(model.username.toLowerCase(), model);\r
+                               // null check on "final" teams because JSON-sourced UserModel\r
+                               // can have a null teams object\r
+                               if (model.teams != null) {\r
+                                       for (TeamModel team : model.teams) {\r
+                                               TeamModel t = teams.get(team.name.toLowerCase());\r
+                                               if (t == null) {\r
+                                                       // new team\r
+                                                       team.addUser(model.username);\r
+                                                       teams.put(team.name.toLowerCase(), team);\r
+                                               } else {\r
+                                                       // do not clobber existing team definition\r
+                                                       // maybe because this is a federated user\r
+                                                       t.addUser(model.username);                                                      \r
+                                               }\r
+                                       }\r
+\r
+                                       // check for implicit team removal\r
+                                       if (originalUser != null) {\r
+                                               for (TeamModel team : originalUser.teams) {\r
+                                                       if (!model.isTeamMember(team.name)) {\r
+                                                               team.removeUser(model.username);\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+                       write();\r
+                       return true;\r
+               } catch (Throwable t) {\r
+                       logger.error(MessageFormat.format("Failed to update user {0} models!", models.size()),\r
+                                       t);\r
+               }\r
+               return false;\r
+       }\r
+\r
        /**\r
         * Updates/writes and replaces a complete user object keyed by username.\r
         * This method allows for renaming a user.\r
@@ -413,7 +463,7 @@ public class ConfigUserService implements IUserService {
                        read();\r
                        for (Map.Entry<String, TeamModel> entry : teams.entrySet()) {\r
                                TeamModel model = entry.getValue();\r
-                               if (model.hasRepository(role)) {\r
+                               if (model.hasRepositoryPermission(role)) {\r
                                        list.add(model.name);\r
                                }\r
                        }\r
@@ -447,10 +497,10 @@ public class ConfigUserService implements IUserService {
                        for (TeamModel team : teams.values()) {\r
                                // team has role, check against revised team list\r
                                if (specifiedTeams.contains(team.name.toLowerCase())) {\r
-                                       team.addRepository(role);\r
+                                       team.addRepositoryPermission(role);\r
                                } else {\r
                                        // remove role from team\r
-                                       team.removeRepository(role);\r
+                                       team.removeRepositoryPermission(role);\r
                                }\r
                        }\r
 \r
@@ -494,6 +544,28 @@ public class ConfigUserService implements IUserService {
                return updateTeamModel(model.name, model);\r
        }\r
 \r
+       /**\r
+        * Updates/writes all specified team objects.\r
+        * \r
+        * @param models a list of team models\r
+        * @return true if update is successful\r
+        * @since 1.2.0\r
+        */\r
+       @Override\r
+       public boolean updateTeamModels(List<TeamModel> models) {\r
+               try {\r
+                       read();\r
+                       for (TeamModel team : models) {\r
+                               teams.put(team.name.toLowerCase(), team);\r
+                       }\r
+                       write();\r
+                       return true;\r
+               } catch (Throwable t) {\r
+                       logger.error(MessageFormat.format("Failed to update team {0} models!", models.size()), t);\r
+               }\r
+               return false;\r
+       }\r
+\r
        /**\r
         * Updates/writes and replaces a complete team object keyed by teamname.\r
         * This method allows for renaming a team.\r
@@ -602,7 +674,7 @@ public class ConfigUserService implements IUserService {
                        read();\r
                        for (Map.Entry<String, UserModel> entry : users.entrySet()) {\r
                                UserModel model = entry.getValue();\r
-                               if (model.hasRepository(role)) {\r
+                               if (model.hasRepositoryPermission(role)) {\r
                                        list.add(model.username);\r
                                }\r
                        }\r
@@ -623,6 +695,7 @@ public class ConfigUserService implements IUserService {
         * @return true if successful\r
         */\r
        @Override\r
+       @Deprecated\r
        public boolean setUsernamesForRepositoryRole(String role, List<String> usernames) {\r
                try {\r
                        Set<String> specifiedUsers = new HashSet<String>();\r
@@ -636,10 +709,10 @@ public class ConfigUserService implements IUserService {
                        for (UserModel user : users.values()) {\r
                                // user has role, check against revised user list\r
                                if (specifiedUsers.contains(user.username.toLowerCase())) {\r
-                                       user.addRepository(role);\r
+                                       user.addRepositoryPermission(role);\r
                                } else {\r
                                        // remove role from user\r
-                                       user.removeRepository(role);\r
+                                       user.removeRepositoryPermission(role);\r
                                }\r
                        }\r
 \r
@@ -665,17 +738,17 @@ public class ConfigUserService implements IUserService {
                        read();\r
                        // identify users which require role rename\r
                        for (UserModel model : users.values()) {\r
-                               if (model.hasRepository(oldRole)) {\r
-                                       model.removeRepository(oldRole);\r
-                                       model.addRepository(newRole);\r
+                               if (model.hasRepositoryPermission(oldRole)) {\r
+                                       AccessPermission permission = model.removeRepositoryPermission(oldRole);\r
+                                       model.setRepositoryPermission(newRole, permission);\r
                                }\r
                        }\r
 \r
                        // identify teams which require role rename\r
                        for (TeamModel model : teams.values()) {\r
-                               if (model.hasRepository(oldRole)) {\r
-                                       model.removeRepository(oldRole);\r
-                                       model.addRepository(newRole);\r
+                               if (model.hasRepositoryPermission(oldRole)) {\r
+                                       AccessPermission permission = model.removeRepositoryPermission(oldRole);\r
+                                       model.setRepositoryPermission(newRole, permission);\r
                                }\r
                        }\r
                        // persist changes\r
@@ -701,12 +774,12 @@ public class ConfigUserService implements IUserService {
 \r
                        // identify users which require role rename\r
                        for (UserModel user : users.values()) {\r
-                               user.removeRepository(role);\r
+                               user.removeRepositoryPermission(role);\r
                        }\r
 \r
                        // identify teams which require role rename\r
                        for (TeamModel team : teams.values()) {\r
-                               team.removeRepository(role);\r
+                               team.removeRepositoryPermission(role);\r
                        }\r
 \r
                        // persist changes\r
@@ -768,21 +841,44 @@ public class ConfigUserService implements IUserService {
                        config.setStringList(USER, model.username, ROLE, roles);\r
 \r
                        // repository memberships\r
-                       // null check on "final" repositories because JSON-sourced UserModel\r
-                       // can have a null repositories object\r
-                       if (!ArrayUtils.isEmpty(model.repositories)) {\r
-                               config.setStringList(USER, model.username, REPOSITORY, new ArrayList<String>(\r
-                                               model.repositories));\r
+                       if (model.permissions == null) {\r
+                               // null check on "final" repositories because JSON-sourced UserModel\r
+                               // can have a null repositories object\r
+                               if (!ArrayUtils.isEmpty(model.repositories)) {\r
+                                       config.setStringList(USER, model.username, REPOSITORY, new ArrayList<String>(\r
+                                                       model.repositories));\r
+                               }\r
+                       } else {\r
+                               // discrete repository permissions\r
+                               List<String> permissions = new ArrayList<String>();\r
+                               for (Map.Entry<String, AccessPermission> entry : model.permissions.entrySet()) {\r
+                                       if (entry.getValue().exceeds(AccessPermission.NONE)) {\r
+                                               permissions.add(entry.getValue().asRole(entry.getKey()));\r
+                                       }\r
+                               }\r
+                               config.setStringList(USER, model.username, REPOSITORY, permissions);\r
                        }\r
                }\r
 \r
                // write teams\r
                for (TeamModel model : teams.values()) {\r
-                       // null check on "final" repositories because JSON-sourced TeamModel\r
-                       // can have a null repositories object\r
-                       if (!ArrayUtils.isEmpty(model.repositories)) {\r
-                               config.setStringList(TEAM, model.name, REPOSITORY, new ArrayList<String>(\r
-                                               model.repositories));\r
+                       if (model.permissions == null) {\r
+                               // null check on "final" repositories because JSON-sourced TeamModel\r
+                               // can have a null repositories object\r
+                               if (!ArrayUtils.isEmpty(model.repositories)) {\r
+                                       config.setStringList(TEAM, model.name, REPOSITORY, new ArrayList<String>(\r
+                                                       model.repositories));\r
+                               }\r
+                       } else {\r
+                               // discrete repository permissions\r
+                               List<String> permissions = new ArrayList<String>();\r
+                               for (Map.Entry<String, AccessPermission> entry : model.permissions.entrySet()) {\r
+                                       if (entry.getValue().exceeds(AccessPermission.NONE)) {\r
+                                               // code:repository (e.g. RW+:~james/myrepo.git\r
+                                               permissions.add(entry.getValue().asRole(entry.getKey()));\r
+                                       }\r
+                               }\r
+                               config.setStringList(TEAM, model.name, REPOSITORY, permissions);\r
                        }\r
 \r
                        // null check on "final" users because JSON-sourced TeamModel\r
@@ -872,7 +968,7 @@ public class ConfigUserService implements IUserService {
                                        Set<String> repositories = new HashSet<String>(Arrays.asList(config\r
                                                        .getStringList(USER, username, REPOSITORY)));\r
                                        for (String repository : repositories) {\r
-                                               user.addRepository(repository);\r
+                                               user.addRepositoryPermission(repository);\r
                                        }\r
 \r
                                        // update cache\r
@@ -886,7 +982,7 @@ public class ConfigUserService implements IUserService {
                                Set<String> teamnames = config.getSubsections(TEAM);\r
                                for (String teamname : teamnames) {\r
                                        TeamModel team = new TeamModel(teamname);\r
-                                       team.addRepositories(Arrays.asList(config.getStringList(TEAM, teamname,\r
+                                       team.addRepositoryPermissions(Arrays.asList(config.getStringList(TEAM, teamname,\r
                                                        REPOSITORY)));\r
                                        team.addUsers(Arrays.asList(config.getStringList(TEAM, teamname, USER)));\r
                                        team.addMailingLists(Arrays.asList(config.getStringList(TEAM, teamname,\r
index c831c42df798ecc916099c143f024a809e95b19a..ed48bd277b34cdf3fbfae9b7379842db4836166d 100644 (file)
  */\r
 package com.gitblit;\r
 \r
+import java.lang.annotation.Documented;\r
+import java.lang.annotation.Retention;\r
+import java.lang.annotation.RetentionPolicy;\r
+\r
 \r
 /**\r
  * Constant values used by Gitblit.\r
@@ -309,4 +313,72 @@ public class Constants {
                        return null;\r
                }\r
        }\r
+       \r
+       /**\r
+        * The access permissions available for a repository. \r
+        */\r
+       public static enum AccessPermission {\r
+               NONE("N"), VIEW("V"), CLONE("R"), PUSH("RW"), CREATE("RWC"), DELETE("RWD"), REWIND("RW+");\r
+               \r
+               public static AccessPermission LEGACY = REWIND;\r
+               \r
+               public final String code;\r
+               \r
+               private AccessPermission(String code) {\r
+                       this.code = code;\r
+               }\r
+               \r
+               public boolean atLeast(AccessPermission perm) {\r
+                       return ordinal() >= perm.ordinal();\r
+               }\r
+\r
+               public boolean exceeds(AccessPermission perm) {\r
+                       return ordinal() > perm.ordinal();\r
+               }\r
+               \r
+               public String asRole(String repository) {\r
+                       return code + ":" + repository;\r
+               }\r
+               \r
+               @Override\r
+               public String toString() {\r
+                       return code;\r
+               }\r
+               \r
+               public static AccessPermission permissionFromRole(String role) {\r
+                       String [] fields = role.split(":", 2);\r
+                       if (fields.length == 1) {\r
+                               // legacy/undefined assume full permissions\r
+                               return AccessPermission.LEGACY;\r
+                       } else {\r
+                               // code:repository\r
+                               return AccessPermission.fromCode(fields[0]);\r
+                       }\r
+               }\r
+               \r
+               public static String repositoryFromRole(String role) {\r
+                       String [] fields = role.split(":", 2);\r
+                       if (fields.length == 1) {\r
+                               // legacy/undefined assume full permissions\r
+                               return role;\r
+                       } else {\r
+                               // code:repository\r
+                               return fields[1];\r
+                       }\r
+               }\r
+               \r
+               public static AccessPermission fromCode(String code) {\r
+                       for (AccessPermission perm : values()) {\r
+                               if (perm.code.equalsIgnoreCase(code)) {\r
+                                       return perm;\r
+                               }\r
+                       }\r
+                       return AccessPermission.NONE;\r
+               }\r
+       }\r
+       \r
+       @Documented\r
+       @Retention(RetentionPolicy.RUNTIME)\r
+       public @interface Unused {\r
+       }\r
 }\r
index e515b55ec38bbc5d075a202aac39dbc16201d713..225e5e115ab7dec192886bf67a469df2aa06dd4e 100644 (file)
@@ -91,7 +91,7 @@ public class DownloadZipFilter extends AccessRestrictionFilter {
         */\r
        @Override\r
        protected boolean canAccess(RepositoryModel repository, UserModel user, String action) {\r
-               return user.canAccessRepository(repository);\r
+               return user.canView(repository);\r
        }\r
 \r
 }\r
index 7b9c55ba5297ebf17ad658e440159a42b2f60f61..03109dea01e1db0e621ddf94406f18aed84c06e7 100644 (file)
@@ -26,6 +26,7 @@ import java.util.ArrayList;
 import java.util.Arrays;\r
 import java.util.Collection;\r
 import java.util.Date;\r
+import java.util.HashMap;\r
 import java.util.HashSet;\r
 import java.util.List;\r
 import java.util.Map;\r
@@ -41,6 +42,7 @@ import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
 import org.slf4j.Logger;\r
 import org.slf4j.LoggerFactory;\r
 \r
+import com.gitblit.Constants.AccessPermission;\r
 import com.gitblit.Constants.FederationPullStatus;\r
 import com.gitblit.Constants.FederationStrategy;\r
 import com.gitblit.GitBlitException.ForbiddenException;\r
@@ -333,10 +335,20 @@ public class FederationPullExecutor implements Runnable {
                                                // reparent all repository permissions if the local\r
                                                // repositories are stored within subfolders\r
                                                if (!StringUtils.isEmpty(registrationFolder)) {\r
-                                                       List<String> permissions = new ArrayList<String>(user.repositories);\r
-                                                       user.repositories.clear();\r
-                                                       for (String permission : permissions) {\r
-                                                               user.addRepository(registrationFolder + "/" + permission);\r
+                                                       if (user.permissions != null && user.permissions.size() > 0) {\r
+                                                               // pulling from >= 1.2 version\r
+                                                               Map<String, AccessPermission> copy = new HashMap<String, AccessPermission>(user.permissions);\r
+                                                               user.permissions.clear();\r
+                                                               for (Map.Entry<String, AccessPermission> entry : copy.entrySet()) {\r
+                                                                       user.setRepositoryPermission(registrationFolder + "/" + entry.getKey(), entry.getValue());\r
+                                                               }\r
+                                                       } else {\r
+                                                               // pulling from <= 1.1 version\r
+                                                               List<String> permissions = new ArrayList<String>(user.repositories);\r
+                                                               user.repositories.clear();\r
+                                                               for (String permission : permissions) {\r
+                                                                       user.addRepositoryPermission(registrationFolder + "/" + permission);\r
+                                                               }\r
                                                        }\r
                                                }\r
 \r
@@ -347,8 +359,17 @@ public class FederationPullExecutor implements Runnable {
                                                        GitBlit.self().updateUserModel(user.username, user, true);\r
                                                } else {\r
                                                        // update repository permissions of local user\r
-                                                       for (String repository : user.repositories) {\r
-                                                               localUser.addRepository(repository);\r
+                                                       if (user.permissions != null && user.permissions.size() > 0) {\r
+                                                               // pulling from >= 1.2 version\r
+                                                               Map<String, AccessPermission> copy = new HashMap<String, AccessPermission>(user.permissions);\r
+                                                               for (Map.Entry<String, AccessPermission> entry : copy.entrySet()) {\r
+                                                                       localUser.setRepositoryPermission(entry.getKey(), entry.getValue());\r
+                                                               }\r
+                                                       } else {\r
+                                                               // pulling from <= 1.1 version\r
+                                                               for (String repository : user.repositories) {\r
+                                                                       localUser.addRepositoryPermission(repository);\r
+                                                               }\r
                                                        }\r
                                                        localUser.password = user.password;\r
                                                        localUser.canAdmin = user.canAdmin;\r
@@ -369,12 +390,16 @@ public class FederationPullExecutor implements Runnable {
 \r
                                                        // update team repositories\r
                                                        TeamModel remoteTeam = user.getTeam(teamname);\r
-                                                       if (remoteTeam != null && !ArrayUtils.isEmpty(remoteTeam.repositories)) {\r
-                                                               int before = team.repositories.size();\r
-                                                               team.addRepositories(remoteTeam.repositories);\r
-                                                               int after = team.repositories.size();\r
-                                                               if (after > before) {\r
-                                                                       // repository count changed, update\r
+                                                       if (remoteTeam != null) {\r
+                                                               if (remoteTeam.permissions != null) {\r
+                                                                       // pulling from >= 1.2\r
+                                                                       for (Map.Entry<String, AccessPermission> entry : remoteTeam.permissions.entrySet()){\r
+                                                                               team.setRepositoryPermission(entry.getKey(), entry.getValue());\r
+                                                                       }\r
+                                                                       GitBlit.self().updateTeamModel(teamname, team, false);\r
+                                                               } else if(!ArrayUtils.isEmpty(remoteTeam.repositories)) {\r
+                                                                       // pulling from <= 1.1\r
+                                                                       team.addRepositoryPermissions(remoteTeam.repositories);\r
                                                                        GitBlit.self().updateTeamModel(teamname, team, false);\r
                                                                }\r
                                                        }\r
index f439469608d3d26718498c63abea4bf9a56daa0f..c06266dc395a664336d92db0fb6a532a297e232e 100644 (file)
@@ -31,6 +31,7 @@ import java.util.concurrent.ConcurrentHashMap;
 import org.slf4j.Logger;\r
 import org.slf4j.LoggerFactory;\r
 \r
+import com.gitblit.Constants.AccessPermission;\r
 import com.gitblit.models.TeamModel;\r
 import com.gitblit.models.UserModel;\r
 import com.gitblit.utils.ArrayUtils;\r
@@ -243,7 +244,7 @@ public class FileUserService extends FileSettings implements IUserService {
                                }\r
                                break;\r
                        default:\r
-                               model.addRepository(role);\r
+                               model.addRepositoryPermission(role);\r
                        }\r
                }\r
                // set the teams for the user\r
@@ -266,6 +267,29 @@ public class FileUserService extends FileSettings implements IUserService {
                return updateUserModel(model.username, model);\r
        }\r
 \r
+       /**\r
+        * Updates/writes all specified user objects.\r
+        * \r
+        * @param model a list of user models\r
+        * @return true if update is successful\r
+        * @since 1.2.0\r
+        */\r
+       @Override\r
+       public boolean updateUserModels(List<UserModel> models) {\r
+               try {                   \r
+                       Properties allUsers = read();\r
+                       for (UserModel model : models) {\r
+                               updateUserCache(allUsers, model.username, model);\r
+                       }\r
+                       write(allUsers);\r
+                       return true;\r
+               } catch (Throwable t) {\r
+                       logger.error(MessageFormat.format("Failed to update {0} user models!", models.size()),\r
+                                       t);\r
+               }\r
+               return false;\r
+       }\r
+\r
        /**\r
         * Updates/writes and replaces a complete user object keyed by username.\r
         * This method allows for renaming a user.\r
@@ -280,8 +304,43 @@ public class FileUserService extends FileSettings implements IUserService {
        public boolean updateUserModel(String username, UserModel model) {\r
                try {                   \r
                        Properties allUsers = read();\r
+                       updateUserCache(allUsers, username, model);\r
+                       write(allUsers);\r
+                       return true;\r
+               } catch (Throwable t) {\r
+                       logger.error(MessageFormat.format("Failed to update user model {0}!", model.username),\r
+                                       t);\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       /**\r
+        * Updates/writes and replaces a complete user object keyed by username.\r
+        * This method allows for renaming a user.\r
+        * \r
+        * @param username\r
+        *            the old username\r
+        * @param model\r
+        *            the user object to use for username\r
+        * @return true if update is successful\r
+        */\r
+       private boolean updateUserCache(Properties allUsers, String username, UserModel model) {\r
+               try {                   \r
                        UserModel oldUser = getUserModel(username);\r
-                       ArrayList<String> roles = new ArrayList<String>(model.repositories);\r
+                       List<String> roles;\r
+                       if (model.permissions == null) {\r
+                               // legacy, use repository list\r
+                               roles = new ArrayList<String>(model.repositories);\r
+                       } else {\r
+                               // discrete repository permissions\r
+                               roles = new ArrayList<String>();\r
+                               for (Map.Entry<String, AccessPermission> entry : model.permissions.entrySet()) {\r
+                                       if (entry.getValue().exceeds(AccessPermission.NONE)) {\r
+                                               // code:repository (e.g. RW+:~james/myrepo.git\r
+                                               roles.add(entry.getValue().asRole(entry.getKey()));\r
+                                       }\r
+                               }\r
+                       }\r
 \r
                        // Permissions\r
                        if (model.canAdmin) {\r
@@ -336,8 +395,6 @@ public class FileUserService extends FileSettings implements IUserService {
                                        }\r
                                }\r
                        }\r
-\r
-                       write(allUsers);\r
                        return true;\r
                } catch (Throwable t) {\r
                        logger.error(MessageFormat.format("Failed to update user model {0}!", model.username),\r
@@ -552,8 +609,8 @@ public class FileUserService extends FileSettings implements IUserService {
                                String[] roles = value.split(",");\r
                                // skip first value (password)\r
                                for (int i = 1; i < roles.length; i++) {\r
-                                       String r = roles[i];\r
-                                       if (r.equalsIgnoreCase(oldRole)) {\r
+                                       String repository = AccessPermission.repositoryFromRole(roles[i]);\r
+                                       if (repository.equalsIgnoreCase(oldRole)) {\r
                                                needsRenameRole.add(username);\r
                                                break;\r
                                        }\r
@@ -573,9 +630,13 @@ public class FileUserService extends FileSettings implements IUserService {
 \r
                                // skip first value (password)\r
                                for (int i = 1; i < values.length; i++) {\r
-                                       String value = values[i];\r
-                                       if (!value.equalsIgnoreCase(oldRole)) {\r
-                                               sb.append(value);\r
+                                       String repository = AccessPermission.repositoryFromRole(values[i]);\r
+                                       if (repository.equalsIgnoreCase(oldRole)) {\r
+                                               AccessPermission permission = AccessPermission.permissionFromRole(values[i]);\r
+                                               sb.append(permission.asRole(newRole));\r
+                                               sb.append(',');\r
+                                       } else {\r
+                                               sb.append(values[i]);\r
                                                sb.append(',');\r
                                        }\r
                                }\r
@@ -612,9 +673,9 @@ public class FileUserService extends FileSettings implements IUserService {
                                String value = allUsers.getProperty(username);\r
                                String[] roles = value.split(",");\r
                                // skip first value (password)\r
-                               for (int i = 1; i < roles.length; i++) {\r
-                                       String r = roles[i];\r
-                                       if (r.equalsIgnoreCase(role)) {\r
+                               for (int i = 1; i < roles.length; i++) {                                        \r
+                                       String repository = AccessPermission.repositoryFromRole(roles[i]);\r
+                                       if (repository.equalsIgnoreCase(role)) {\r
                                                needsDeleteRole.add(username);\r
                                                break;\r
                                        }\r
@@ -630,10 +691,10 @@ public class FileUserService extends FileSettings implements IUserService {
                                sb.append(password);\r
                                sb.append(',');\r
                                // skip first value (password)\r
-                               for (int i = 1; i < values.length; i++) {\r
-                                       String value = values[i];\r
-                                       if (!value.equalsIgnoreCase(role)) {\r
-                                               sb.append(value);\r
+                               for (int i = 1; i < values.length; i++) {                                       \r
+                                       String repository = AccessPermission.repositoryFromRole(values[i]);\r
+                                       if (!repository.equalsIgnoreCase(role)) {\r
+                                               sb.append(values[i]);\r
                                                sb.append(',');\r
                                        }\r
                                }\r
@@ -722,7 +783,7 @@ public class FileUserService extends FileSettings implements IUserService {
                                                        repositories.add(role);\r
                                                }\r
                                        }\r
-                                       team.addRepositories(repositories);\r
+                                       team.addRepositoryPermissions(repositories);\r
                                        team.addUsers(users);\r
                                        team.addMailingLists(mailingLists);\r
                                        team.preReceiveScripts.addAll(preReceive);\r
@@ -912,6 +973,27 @@ public class FileUserService extends FileSettings implements IUserService {
        public boolean updateTeamModel(TeamModel model) {\r
                return updateTeamModel(model.name, model);\r
        }\r
+       \r
+       /**\r
+        * Updates/writes all specified team objects.\r
+        * \r
+        * @param models a list of team models\r
+        * @return true if update is successful\r
+        * @since 1.2.0\r
+        */\r
+       public boolean updateTeamModels(List<TeamModel> models) {\r
+               try {\r
+                       Properties allUsers = read();\r
+                       for (TeamModel model : models) {\r
+                               updateTeamCache(allUsers, model.name, model);\r
+                       }\r
+                       write(allUsers);\r
+                       return true;\r
+               } catch (Throwable t) {\r
+                       logger.error(MessageFormat.format("Failed to update {0} team models!", models.size()), t);\r
+               }\r
+               return false;\r
+       }\r
 \r
        /**\r
         * Updates/writes and replaces a complete team object keyed by teamname.\r
@@ -939,12 +1021,30 @@ public class FileUserService extends FileSettings implements IUserService {
 \r
        private void updateTeamCache(Properties allUsers, String teamname, TeamModel model) {\r
                StringBuilder sb = new StringBuilder();\r
-               if (!ArrayUtils.isEmpty(model.repositories)) {\r
-                       for (String repository : model.repositories) {\r
-                               sb.append(repository);\r
-                               sb.append(',');\r
+               List<String> roles;\r
+               if (model.permissions == null) {\r
+                       // legacy, use repository list\r
+                       if (model.repositories != null) {\r
+                               roles = new ArrayList<String>(model.repositories);\r
+                       } else {\r
+                               roles = new ArrayList<String>();\r
+                       }\r
+               } else {\r
+                       // discrete repository permissions\r
+                       roles = new ArrayList<String>();\r
+                       for (Map.Entry<String, AccessPermission> entry : model.permissions.entrySet()) {\r
+                               if (entry.getValue().exceeds(AccessPermission.NONE)) {\r
+                                       // code:repository (e.g. RW+:~james/myrepo.git\r
+                                       roles.add(entry.getValue().asRole(entry.getKey()));\r
+                               }\r
                        }\r
                }\r
+               \r
+               for (String role : roles) {\r
+                               sb.append(role);\r
+                               sb.append(',');\r
+               }\r
+               \r
                if (!ArrayUtils.isEmpty(model.users)) {\r
                        for (String user : model.users) {\r
                                sb.append('!');\r
index 7fbd3efdc9b955b2aa8171dd6764a99f846016f8..8c6d9ebaed4d979420b4f856a31cc0cfa8e74f39 100644 (file)
@@ -69,6 +69,7 @@ import org.eclipse.jgit.util.FileUtils;
 import org.slf4j.Logger;\r
 import org.slf4j.LoggerFactory;\r
 \r
+import com.gitblit.Constants.AccessPermission;\r
 import com.gitblit.Constants.AccessRestrictionType;\r
 import com.gitblit.Constants.AuthorizationControl;\r
 import com.gitblit.Constants.FederationRequest;\r
@@ -618,6 +619,7 @@ public class GitBlit implements ServletContextListener {
         * @param usernames\r
         * @return true if successful\r
         */\r
+       @Deprecated\r
        public boolean setRepositoryUsers(RepositoryModel repository, List<String> repositoryUsers) {\r
                return userService.setUsernamesForRepositoryRole(repository.name, repositoryUsers);\r
        }\r
@@ -699,6 +701,7 @@ public class GitBlit implements ServletContextListener {
         * @param teamnames\r
         * @return true if successful\r
         */\r
+       @Deprecated\r
        public boolean setRepositoryTeams(RepositoryModel repository, List<String> repositoryTeams) {\r
                return userService.setTeamnamesForRepositoryRole(repository.name, repositoryTeams);\r
        }\r
@@ -957,14 +960,13 @@ public class GitBlit implements ServletContextListener {
                if (model == null) {\r
                        return null;\r
                }\r
-               if (model.accessRestriction.atLeast(AccessRestrictionType.VIEW)) {\r
-                       if (user != null && user.canAccessRepository(model)) {\r
-                               return model;\r
-                       }\r
-                       return null;\r
-               } else {\r
+               if (user == null) {\r
+                       user = UserModel.ANONYMOUS;\r
+               }\r
+               if (user.canView(model)) {\r
                        return model;\r
                }\r
+               return null;\r
        }\r
 \r
        /**\r
@@ -1224,11 +1226,7 @@ public class GitBlit implements ServletContextListener {
                }\r
                model.hasCommits = JGitUtils.hasCommits(r);\r
                model.lastChange = JGitUtils.getLastChange(r);\r
-               if (repositoryName.indexOf('/') == -1) {\r
-                       model.projectPath = "";\r
-               } else {\r
-                       model.projectPath = repositoryName.substring(0, repositoryName.indexOf('/'));\r
-               }\r
+               model.projectPath = StringUtils.getFirstPathElement(repositoryName);\r
                \r
                StoredConfig config = r.getConfig();\r
                boolean hasOrigin = !StringUtils.isEmpty(config.getString("remote", "origin", "url"));\r
@@ -1449,6 +1447,9 @@ public class GitBlit implements ServletContextListener {
         */\r
        private void closeRepository(String repositoryName) {\r
                Repository repository = getRepository(repositoryName);\r
+               if (repository == null) {\r
+                       return;\r
+               }\r
                RepositoryCache.close(repository);\r
 \r
                // assume 2 uses in case reflection fails\r
@@ -1756,7 +1757,7 @@ public class GitBlit implements ServletContextListener {
                        clearRepositoryMetadataCache(repositoryName);\r
                        \r
                        RepositoryModel model = removeFromCachedRepositoryList(repositoryName);\r
-                       if (!ArrayUtils.isEmpty(model.forks)) {\r
+                       if (model != null && !ArrayUtils.isEmpty(model.forks)) {\r
                                resetRepositoryListCache();\r
                        }\r
 \r
@@ -2646,26 +2647,46 @@ public class GitBlit implements ServletContextListener {
 \r
                // create a Gitblit repository model for the clone\r
                RepositoryModel cloneModel = repository.cloneAs(cloneName);\r
+               // owner has REWIND/RW+ permissions\r
                cloneModel.owner = user.username;\r
                updateRepositoryModel(cloneName, cloneModel, false);\r
 \r
-               if (AuthorizationControl.NAMED.equals(cloneModel.authorizationControl)) {\r
-                       // add the owner of the source repository to the clone's access list\r
-                       if (!StringUtils.isEmpty(repository.owner)) {\r
-                               UserModel owner = getUserModel(repository.owner);\r
-                               if (owner != null) {\r
-                                       owner.repositories.add(cloneName);\r
-                                       updateUserModel(owner.username, owner, false);\r
-                               }\r
+               // add the owner of the source repository to the clone's access list\r
+               if (!StringUtils.isEmpty(repository.owner)) {\r
+                       UserModel originOwner = getUserModel(repository.owner);\r
+                       if (originOwner != null) {\r
+                               originOwner.setRepositoryPermission(cloneName, AccessPermission.CLONE);\r
+                               updateUserModel(originOwner.username, originOwner, false);\r
                        }\r
+               }\r
 \r
-                       // inherit origin's access lists\r
-                       List<String> users = getRepositoryUsers(repository);\r
-                       setRepositoryUsers(cloneModel, users);\r
+               // grant origin's user list clone permission to fork\r
+               List<String> users = getRepositoryUsers(repository);\r
+               List<UserModel> cloneUsers = new ArrayList<UserModel>();\r
+               for (String name : users) {\r
+                       if (!name.equalsIgnoreCase(user.username)) {\r
+                               UserModel cloneUser = getUserModel(name);\r
+                               if (cloneUser.canClone(repository)) {\r
+                                       // origin user can clone origin, grant clone access to fork\r
+                                       cloneUser.setRepositoryPermission(cloneName, AccessPermission.CLONE);\r
+                               }\r
+                               cloneUsers.add(cloneUser);\r
+                       }\r
+               }\r
+               userService.updateUserModels(cloneUsers);\r
 \r
-                       List<String> teams = getRepositoryTeams(repository);\r
-                       setRepositoryTeams(cloneModel, teams);\r
+               // grant origin's team list clone permission to fork\r
+               List<String> teams = getRepositoryTeams(repository);\r
+               List<TeamModel> cloneTeams = new ArrayList<TeamModel>();\r
+               for (String name : teams) {\r
+                       TeamModel cloneTeam = getTeamModel(name);\r
+                       if (cloneTeam.canClone(repository)) {\r
+                               // origin team can clone origin, grant clone access to fork\r
+                               cloneTeam.setRepositoryPermission(cloneName, AccessPermission.CLONE);\r
+                       }\r
+                       cloneTeams.add(cloneTeam);\r
                }\r
+               userService.updateTeamModels(cloneTeams);                       \r
 \r
                // add this clone to the cached model\r
                addToCachedRepositoryList(cloneModel);\r
index 8ce4d3a7ce444a675e45879df2afa8de79c7d61a..cfe4fe3f8ee57170585bb51f3d27ebdb5ccc13c6 100644 (file)
@@ -147,33 +147,25 @@ public class GitFilter extends AccessRestrictionFilter {
                        // Git Servlet disabled\r
                        return false;\r
                }               \r
-               boolean readOnly = repository.isFrozen; \r
-               if (readOnly || repository.accessRestriction.atLeast(AccessRestrictionType.PUSH)) {\r
-                       boolean authorizedUser = user.canAccessRepository(repository);\r
-                       if (action.equals(gitReceivePack)) {\r
-                               // Push request\r
-                               if (!readOnly && authorizedUser) {\r
-                                       // clone-restricted or push-authorized\r
-                                       return true;\r
-                               } else {\r
-                                       // user is unauthorized to push to this repository\r
-                                       logger.warn(MessageFormat.format("user {0} is not authorized to push to {1}",\r
-                                                       user.username, repository));\r
-                                       return false;\r
-                               }\r
-                       } else if (action.equals(gitUploadPack)) {\r
-                               // Clone request\r
-                               boolean cloneRestricted = repository.accessRestriction\r
-                                               .atLeast(AccessRestrictionType.CLONE);\r
-                               if (!cloneRestricted || (cloneRestricted && authorizedUser)) {\r
-                                       // push-restricted or clone-authorized\r
-                                       return true;\r
-                               } else {\r
-                                       // user is unauthorized to clone this repository\r
-                                       logger.warn(MessageFormat.format("user {0} is not authorized to clone {1}",\r
-                                                       user.username, repository));\r
-                                       return false;\r
-                               }\r
+               if (action.equals(gitReceivePack)) {\r
+                       // Push request\r
+                       if (user.canPush(repository)) {\r
+                               return true;\r
+                       } else {\r
+                               // user is unauthorized to push to this repository\r
+                               logger.warn(MessageFormat.format("user {0} is not authorized to push to {1}",\r
+                                               user.username, repository));\r
+                               return false;\r
+                       }\r
+               } else if (action.equals(gitUploadPack)) {\r
+                       // Clone request\r
+                       if (user.canClone(repository)) {\r
+                               return true;\r
+                       } else {\r
+                               // user is unauthorized to clone this repository\r
+                               logger.warn(MessageFormat.format("user {0} is not authorized to clone {1}",\r
+                                               user.username, repository));\r
+                               return false;\r
                        }\r
                }\r
                return true;\r
index 2571693d14922b6973cd0e15dd636d977511f3b4..8e2326d43ae41d1935070c46870460eacd53aa80 100644 (file)
@@ -105,6 +105,21 @@ public class GitServlet extends org.eclipse.jgit.http.server.GitServlet {
                                ReceivePack rp = super.create(req, db);\r
                                rp.setPreReceiveHook(hook);\r
                                rp.setPostReceiveHook(hook);\r
+\r
+                               // determine pushing user\r
+                               PersonIdent person = rp.getRefLogIdent();\r
+                               UserModel user = GitBlit.self().getUserModel(person.getName());\r
+                               if (user == null) {\r
+                                       // anonymous push, create a temporary usermodel\r
+                                       user = new UserModel(person.getName());\r
+                               }\r
+                               \r
+                               // enforce advanced ref permissions\r
+                               RepositoryModel repository = GitBlit.self().getRepositoryModel(repositoryName);\r
+                               rp.setAllowCreates(user.canCreateRef(repository));\r
+                               rp.setAllowDeletes(user.canDeleteRef(repository));\r
+                               rp.setAllowNonFastForwards(user.canRewindRef(repository));\r
+                               \r
                                return rp;\r
                        }\r
                });\r
@@ -209,7 +224,25 @@ public class GitServlet extends org.eclipse.jgit.http.server.GitServlet {
                        scripts.addAll(repository.postReceiveScripts);\r
                        UserModel user = getUserModel(rp);\r
                        runGroovy(repository, user, commands, rp, scripts);\r
-\r
+                       for (ReceiveCommand cmd : commands) {\r
+                               if (Result.OK.equals(cmd.getResult())) {\r
+                                       // add some logging for important ref changes\r
+                                       switch (cmd.getType()) {\r
+                                       case DELETE:\r
+                                               logger.info(MessageFormat.format("{0} DELETED {1} in {2} ({3})", user.username, cmd.getRefName(), repository.name, cmd.getOldId().name()));\r
+                                               break;\r
+                                       case CREATE:\r
+                                               logger.info(MessageFormat.format("{0} CREATED {1} in {2}", user.username, cmd.getRefName(), repository.name));\r
+                                               break;\r
+                                       case UPDATE_NONFASTFORWARD:\r
+                                               logger.info(MessageFormat.format("{0} UPDATED NON-FAST-FORWARD {1} in {2} (from {3} to {4})", user.username, cmd.getRefName(), repository.name, cmd.getOldId().name(), cmd.getNewId().name()));\r
+                                               break;\r
+                                       default:\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
                        // Experimental\r
                        // runNativeScript(rp, "hooks/post-receive", commands);\r
                }\r
index b4640b58b76f8b4ba56d571fa0bd02a63f25dc7f..141ad8f11cdf228414d864e80342745012900a6f 100644 (file)
@@ -167,6 +167,11 @@ public class GitblitUserService implements IUserService {
                return serviceImpl.updateUserModel(model);\r
        }\r
 \r
+       @Override\r
+       public boolean updateUserModels(List<UserModel> models) {\r
+               return serviceImpl.updateUserModels(models);\r
+       }\r
+\r
        @Override\r
        public boolean updateUserModel(String username, UserModel model) {\r
                if (supportsCredentialChanges()) {\r
@@ -232,6 +237,7 @@ public class GitblitUserService implements IUserService {
        }\r
 \r
        @Override\r
+       @Deprecated\r
        public boolean setTeamnamesForRepositoryRole(String role, List<String> teamnames) {\r
                return serviceImpl.setTeamnamesForRepositoryRole(role, teamnames);\r
        }\r
@@ -246,6 +252,11 @@ public class GitblitUserService implements IUserService {
                return serviceImpl.updateTeamModel(model);\r
        }\r
 \r
+       @Override\r
+       public boolean updateTeamModels(List<TeamModel> models) {\r
+               return serviceImpl.updateTeamModels(models);\r
+       }\r
+\r
        @Override\r
        public boolean updateTeamModel(String teamname, TeamModel model) {\r
                if (!supportsTeamMembershipChanges()) {\r
@@ -275,6 +286,7 @@ public class GitblitUserService implements IUserService {
        }\r
 \r
        @Override\r
+       @Deprecated\r
        public boolean setUsernamesForRepositoryRole(String role, List<String> usernames) {\r
                return serviceImpl.setUsernamesForRepositoryRole(role, usernames);\r
        }\r
index 8822d02430fa4d83604e31ce09159e31a1836d6b..059d648a7ce17959608476706b8cd820df910224 100644 (file)
@@ -126,6 +126,15 @@ public interface IUserService {
         */\r
        boolean updateUserModel(UserModel model);\r
 \r
+       /**\r
+        * Updates/writes all specified user objects.\r
+        * \r
+        * @param models a list of user models\r
+        * @return true if update is successful\r
+        * @since 1.2.0\r
+        */\r
+       boolean updateUserModels(List<UserModel> models);\r
+       \r
        /**\r
         * Adds/updates a user object keyed by username. This method allows for\r
         * renaming a user.\r
@@ -205,7 +214,8 @@ public interface IUserService {
         * @param teamnames\r
         * @return true if successful\r
         * @since 0.8.0\r
-        */     \r
+        */\r
+       @Deprecated\r
        boolean setTeamnamesForRepositoryRole(String role, List<String> teamnames);\r
        \r
        /**\r
@@ -226,6 +236,15 @@ public interface IUserService {
         */     \r
        boolean updateTeamModel(TeamModel model);\r
 \r
+       /**\r
+        * Updates/writes all specified team objects.\r
+        * \r
+        * @param models a list of team models\r
+        * @return true if update is successful\r
+        * @since 1.2.0\r
+        */     \r
+       boolean updateTeamModels(List<TeamModel> models);\r
+       \r
        /**\r
         * Updates/writes and replaces a complete team object keyed by teamname.\r
         * This method allows for renaming a team.\r
@@ -277,6 +296,7 @@ public interface IUserService {
         * @param usernames\r
         * @return true if successful\r
         */\r
+       @Deprecated\r
        boolean setUsernamesForRepositoryRole(String role, List<String> usernames);\r
 \r
        /**\r
index c092c64d7060a65c938e12f638b2d435ce824ee2..11cdfa5687ecadce62fa5ba67e2a6794db331274 100644 (file)
@@ -111,6 +111,6 @@ public class PagesFilter extends AccessRestrictionFilter {
         */\r
        @Override\r
        protected boolean canAccess(RepositoryModel repository, UserModel user, String action) {                \r
-               return user.canAccessRepository(repository);\r
+               return user.canView(repository);\r
        }\r
 }\r
index 0dff1c87a909df28bcce2668aa7d046c529e8d05..61bf22588785f5f1a385faf869370c32e95ada0c 100644 (file)
@@ -113,7 +113,7 @@ public class SyndicationFilter extends AuthenticationFilter {
                                        return;\r
                                } else {\r
                                        // check user access for request\r
-                                       if (user.canAdmin || user.canAccessRepository(model)) {\r
+                                       if (user.canView(model)) {\r
                                                // authenticated request permitted.\r
                                                // pass processing to the restricted servlet.\r
                                                newSession(authenticatedRequest, httpResponse);\r
index caf7e7e4b889bf6b6722dff05552a25a4c045d88..914523df65f1377b19e7a0d7eb96f2a7ceacaa5e 100644 (file)
@@ -88,7 +88,8 @@ public class RepositoryModel implements Serializable, Comparable<RepositoryModel
                this.accessRestriction = AccessRestrictionType.NONE;\r
                this.authorizationControl = AuthorizationControl.NAMED;\r
                this.federationSets = new ArrayList<String>();\r
-               this.federationStrategy = FederationStrategy.FEDERATE_THIS;             \r
+               this.federationStrategy = FederationStrategy.FEDERATE_THIS;     \r
+               this.projectPath = StringUtils.getFirstPathElement(name);\r
        }\r
        \r
        public List<String> getLocalBranches() {\r
@@ -175,8 +176,8 @@ public class RepositoryModel implements Serializable, Comparable<RepositoryModel
                clone.projectPath = StringUtils.getFirstPathElement(cloneName);\r
                clone.isBare = true;\r
                clone.description = description;\r
-               clone.accessRestriction = accessRestriction;\r
-               clone.authorizationControl = authorizationControl;\r
+               clone.accessRestriction = AccessRestrictionType.PUSH;\r
+               clone.authorizationControl = AuthorizationControl.NAMED;\r
                clone.federationStrategy = federationStrategy;\r
                clone.showReadme = showReadme;\r
                clone.showRemoteBranches = false;\r
index 3d176e59e6001d2cd0f719093d41a9e5b7e4bc0c..d185b9d6cbf452fa56b3d9d40bd057beae160eb5 100644 (file)
@@ -18,10 +18,16 @@ package com.gitblit.models;
 import java.io.Serializable;\r
 import java.util.ArrayList;\r
 import java.util.Collection;\r
+import java.util.HashMap;\r
 import java.util.HashSet;\r
 import java.util.List;\r
+import java.util.Map;\r
 import java.util.Set;\r
 \r
+import com.gitblit.Constants.AccessPermission;\r
+import com.gitblit.Constants.AccessRestrictionType;\r
+import com.gitblit.Constants.Unused;\r
+\r
 /**\r
  * TeamModel is a serializable model class that represents a group of users and\r
  * a list of accessible repositories.\r
@@ -36,7 +42,10 @@ public class TeamModel implements Serializable, Comparable<TeamModel> {
        // field names are reflectively mapped in EditTeam page\r
        public String name;\r
        public final Set<String> users = new HashSet<String>();\r
+       // retained for backwards-compatibility with RPC clients\r
+       @Deprecated\r
        public final Set<String> repositories = new HashSet<String>();\r
+       public final Map<String, AccessPermission> permissions = new HashMap<String, AccessPermission>();\r
        public final Set<String> mailingLists = new HashSet<String>();\r
        public final List<String> preReceiveScripts = new ArrayList<String>();\r
        public final List<String> postReceiveScripts = new ArrayList<String>();\r
@@ -45,24 +54,136 @@ public class TeamModel implements Serializable, Comparable<TeamModel> {
                this.name = name;\r
        }\r
 \r
+       /**\r
+        * @use hasRepositoryPermission\r
+        * @param name\r
+        * @return\r
+        */\r
+       @Deprecated\r
+       @Unused\r
        public boolean hasRepository(String name) {\r
-               return repositories.contains(name.toLowerCase());\r
+               return hasRepositoryPermission(name);\r
        }\r
 \r
+       @Deprecated\r
+       @Unused\r
        public void addRepository(String name) {\r
-               repositories.add(name.toLowerCase());\r
+               addRepositoryPermission(name);\r
        }\r
        \r
+       @Deprecated\r
+       @Unused\r
        public void addRepositories(Collection<String> names) {\r
-               for (String name:names) {\r
-                       repositories.add(name.toLowerCase());\r
-               }\r
-       }       \r
+               addRepositoryPermissions(names);\r
+       }\r
 \r
+       @Deprecated\r
+       @Unused\r
        public void removeRepository(String name) {\r
-               repositories.remove(name.toLowerCase());\r
+               removeRepositoryPermission(name);\r
+       }\r
+       \r
+       /**\r
+        * Returns true if the team has any type of specified access permission for\r
+        * this repository.\r
+        * \r
+        * @param name\r
+        * @return true if team has a specified access permission for the repository\r
+        */\r
+       public boolean hasRepositoryPermission(String name) {\r
+               String repository = AccessPermission.repositoryFromRole(name).toLowerCase();\r
+               return permissions.containsKey(repository) || repositories.contains(repository);\r
        }\r
        \r
+       /**\r
+        * Adds a repository permission to the team.\r
+        * <p>\r
+        * Role may be formatted as:\r
+        * <ul>\r
+        * <li> myrepo.git <i>(this is implicitly RW+)</i>\r
+        * <li> RW+:myrepo.git\r
+        * </ul>\r
+        * @param role\r
+        */\r
+       public void addRepositoryPermission(String role) {\r
+               AccessPermission permission = AccessPermission.permissionFromRole(role);\r
+               String repository = AccessPermission.repositoryFromRole(role).toLowerCase();\r
+               repositories.add(repository);\r
+               permissions.put(repository, permission);\r
+       }\r
+\r
+       public void addRepositoryPermissions(Collection<String> roles) {\r
+               for (String role:roles) {\r
+                       addRepositoryPermission(role);\r
+               }\r
+       }\r
+       \r
+       public AccessPermission removeRepositoryPermission(String name) {\r
+               String repository = AccessPermission.repositoryFromRole(name).toLowerCase();\r
+               repositories.remove(repository);\r
+               return permissions.remove(repository);\r
+       }\r
+       \r
+       public void setRepositoryPermission(String repository, AccessPermission permission) {\r
+               permissions.put(repository.toLowerCase(), permission);\r
+               repositories.add(repository.toLowerCase());\r
+       }\r
+       \r
+       public AccessPermission getRepositoryPermission(RepositoryModel repository) {\r
+               AccessPermission permission = AccessPermission.NONE;\r
+               if (permissions.containsKey(repository.name.toLowerCase())) {\r
+                       AccessPermission p = permissions.get(repository.name.toLowerCase());\r
+                       if (p != null) {\r
+                               permission = p;\r
+                       }\r
+               }\r
+               return permission;\r
+       }\r
+       \r
+       private boolean canAccess(RepositoryModel repository, AccessRestrictionType ifRestriction, AccessPermission requirePermission) {\r
+               if (repository.accessRestriction.atLeast(ifRestriction)) {\r
+                       AccessPermission permission = getRepositoryPermission(repository);\r
+                       return permission.atLeast(requirePermission);\r
+               }\r
+               return true;\r
+       }\r
+       \r
+       public boolean canView(RepositoryModel repository) {\r
+               return canAccess(repository, AccessRestrictionType.VIEW, AccessPermission.VIEW);\r
+       }\r
+\r
+       public boolean canClone(RepositoryModel repository) {\r
+               return canAccess(repository, AccessRestrictionType.CLONE, AccessPermission.CLONE);\r
+       }\r
+\r
+       public boolean canPush(RepositoryModel repository) {\r
+               if (repository.isFrozen) {\r
+                       return false;\r
+               }\r
+               return canAccess(repository, AccessRestrictionType.PUSH, AccessPermission.PUSH);\r
+       }\r
+\r
+       public boolean canCreateRef(RepositoryModel repository) {\r
+               if (repository.isFrozen) {\r
+                       return false;\r
+               }\r
+               return canAccess(repository, AccessRestrictionType.PUSH, AccessPermission.CREATE);\r
+       }\r
+\r
+       public boolean canDeleteRef(RepositoryModel repository) {\r
+               if (repository.isFrozen) {\r
+                       return false;\r
+               }\r
+               return canAccess(repository, AccessRestrictionType.PUSH, AccessPermission.DELETE);\r
+       }\r
+\r
+       public boolean canRewindRef(RepositoryModel repository) {\r
+               if (repository.isFrozen) {\r
+                       return false;\r
+               }\r
+               return canAccess(repository, AccessRestrictionType.PUSH, AccessPermission.REWIND);\r
+       }\r
+\r
        public boolean hasUser(String name) {\r
                return users.contains(name.toLowerCase());\r
        }\r
index 94bd055dba7f845381f3570b4178e064037ba7c8..ee73025792e5858a99b8e80f8042dade94cfe306 100644 (file)
@@ -17,11 +17,15 @@ package com.gitblit.models;
 \r
 import java.io.Serializable;\r
 import java.security.Principal;\r
+import java.util.HashMap;\r
 import java.util.HashSet;\r
+import java.util.Map;\r
 import java.util.Set;\r
 \r
+import com.gitblit.Constants.AccessPermission;\r
 import com.gitblit.Constants.AccessRestrictionType;\r
 import com.gitblit.Constants.AuthorizationControl;\r
+import com.gitblit.Constants.Unused;\r
 import com.gitblit.utils.StringUtils;\r
 \r
 /**\r
@@ -48,7 +52,10 @@ public class UserModel implements Principal, Serializable, Comparable<UserModel>
        public boolean canFork;\r
        public boolean canCreate;\r
        public boolean excludeFromFederation;\r
+       // retained for backwards-compatibility with RPC clients\r
+       @Deprecated\r
        public final Set<String> repositories = new HashSet<String>();\r
+       public final Map<String, AccessPermission> permissions = new HashMap<String, AccessPermission>();\r
        public final Set<TeamModel> teams = new HashSet<TeamModel>();\r
 \r
        // non-persisted fields\r
@@ -77,6 +84,8 @@ public class UserModel implements Principal, Serializable, Comparable<UserModel>
                                || hasTeamAccess(repositoryName);\r
        }\r
 \r
+       @Deprecated\r
+       @Unused\r
        public boolean canAccessRepository(RepositoryModel repository) {\r
                boolean isOwner = !StringUtils.isEmpty(repository.owner)\r
                                && repository.owner.equals(username);\r
@@ -85,62 +94,170 @@ public class UserModel implements Principal, Serializable, Comparable<UserModel>
                                || hasTeamAccess(repository.name) || allowAuthenticated;\r
        }\r
 \r
+       @Deprecated\r
+       @Unused\r
        public boolean hasTeamAccess(String repositoryName) {\r
                for (TeamModel team : teams) {\r
-                       if (team.hasRepository(repositoryName)) {\r
+                       if (team.hasRepositoryPermission(repositoryName)) {\r
                                return true;\r
                        }\r
                }\r
                return false;\r
        }\r
        \r
-       public boolean canViewRepository(RepositoryModel repository) {\r
-               if (canAdmin) {\r
-                       return true;\r
+       @Deprecated\r
+       @Unused\r
+       public boolean hasRepository(String name) {\r
+               return hasRepositoryPermission(name);\r
+       }\r
+\r
+       @Deprecated\r
+       @Unused\r
+       public void addRepository(String name) {\r
+               addRepositoryPermission(name);\r
+       }\r
+\r
+       @Deprecated\r
+       @Unused\r
+       public void removeRepository(String name) {\r
+               removeRepositoryPermission(name);\r
+       }\r
+       \r
+       /**\r
+        * Returns true if the user has any type of specified access permission for\r
+        * this repository.\r
+        * \r
+        * @param name\r
+        * @return true if user has a specified access permission for the repository\r
+        */\r
+       public boolean hasRepositoryPermission(String name) {\r
+               String repository = AccessPermission.repositoryFromRole(name).toLowerCase();\r
+               return permissions.containsKey(repository) || repositories.contains(repository);\r
+       }\r
+       \r
+       /**\r
+        * Adds a repository permission to the team.\r
+        * <p>\r
+        * Role may be formatted as:\r
+        * <ul>\r
+        * <li> myrepo.git <i>(this is implicitly RW+)</i>\r
+        * <li> RW+:myrepo.git\r
+        * </ul>\r
+        * @param role\r
+        */\r
+       public void addRepositoryPermission(String role) {\r
+               AccessPermission permission = AccessPermission.permissionFromRole(role);\r
+               String repository = AccessPermission.repositoryFromRole(role).toLowerCase();\r
+               repositories.add(repository);\r
+               permissions.put(repository, permission);\r
+       }\r
+       \r
+       public AccessPermission removeRepositoryPermission(String name) {\r
+               String repository = AccessPermission.repositoryFromRole(name).toLowerCase();\r
+               repositories.remove(repository);\r
+               return permissions.remove(repository);\r
+       }\r
+               \r
+       public void setRepositoryPermission(String repository, AccessPermission permission) {\r
+               permissions.put(repository.toLowerCase(), permission);\r
+       }\r
+\r
+       public AccessPermission getRepositoryPermission(RepositoryModel repository) {\r
+               if (canAdmin || repository.isOwner(username) || repository.isUsersPersonalRepository(username)) {\r
+                       return AccessPermission.REWIND;\r
+               }\r
+               if (AuthorizationControl.AUTHENTICATED.equals(repository.authorizationControl) && isAuthenticated) {\r
+                       // AUTHENTICATED is a shortcut for authorizing all logged-in users RW access\r
+                       return AccessPermission.REWIND;\r
                }\r
-               if (repository.accessRestriction.atLeast(AccessRestrictionType.VIEW)) {\r
-                       return canAccessRepository(repository);\r
+               \r
+               // determine best permission available based on user's personal permissions\r
+               // and the permissions of teams of which the user belongs\r
+               AccessPermission permission = AccessPermission.NONE;\r
+               if (permissions.containsKey(repository.name.toLowerCase())) {\r
+                       AccessPermission p = permissions.get(repository.name.toLowerCase());\r
+                       if (p != null) {\r
+                               permission = p;\r
+                       }\r
+               }\r
+               \r
+               for (TeamModel team : teams) {\r
+                       AccessPermission p = team.getRepositoryPermission(repository);\r
+                       if (permission == null || p.exceeds(permission)) {\r
+                               // use team permission\r
+                               permission = p;\r
+                       }\r
+               }\r
+               return permission;\r
+       }\r
+       \r
+       private boolean canAccess(RepositoryModel repository, AccessRestrictionType ifRestriction, AccessPermission requirePermission) {\r
+               if (repository.accessRestriction.atLeast(ifRestriction)) {\r
+                       AccessPermission permission = getRepositoryPermission(repository);\r
+                       return permission.atLeast(requirePermission);\r
                }\r
                return true;\r
        }\r
        \r
-       public boolean canForkRepository(RepositoryModel repository) {\r
-               if (canAdmin) {\r
-                       return true;\r
+       public boolean canView(RepositoryModel repository) {\r
+               return canAccess(repository, AccessRestrictionType.VIEW, AccessPermission.VIEW);\r
+       }\r
+\r
+       public boolean canClone(RepositoryModel repository) {\r
+               return canAccess(repository, AccessRestrictionType.CLONE, AccessPermission.CLONE);\r
+       }\r
+\r
+       public boolean canPush(RepositoryModel repository) {\r
+               if (repository.isFrozen) {\r
+                       return false;\r
+               }\r
+               return canAccess(repository, AccessRestrictionType.PUSH, AccessPermission.PUSH);\r
+       }\r
+\r
+       public boolean canCreateRef(RepositoryModel repository) {\r
+               if (repository.isFrozen) {\r
+                       return false;\r
                }\r
-               if (!canFork) {\r
-                       // user has been prohibited from forking\r
+               return canAccess(repository, AccessRestrictionType.PUSH, AccessPermission.CREATE);\r
+       }\r
+\r
+       public boolean canDeleteRef(RepositoryModel repository) {\r
+               if (repository.isFrozen) {\r
                        return false;\r
                }\r
-               if (!isAuthenticated) {\r
-                       // unauthenticated user model\r
+               return canAccess(repository, AccessRestrictionType.PUSH, AccessPermission.DELETE);\r
+       }\r
+\r
+       public boolean canRewindRef(RepositoryModel repository) {\r
+               if (repository.isFrozen) {\r
                        return false;\r
                }\r
-               if (("~" + username).equalsIgnoreCase(repository.projectPath)) {\r
-                       // this repository is already a personal repository\r
+               return canAccess(repository, AccessRestrictionType.PUSH, AccessPermission.REWIND);\r
+       }\r
+\r
+       public boolean canFork(RepositoryModel repository) {\r
+               if (repository.isUsersPersonalRepository(username)) {\r
+                       // can not fork your own repository\r
                        return false;\r
                }\r
+               if (canAdmin || repository.isOwner(username)) {\r
+                       return true;\r
+               }\r
                if (!repository.allowForks) {\r
-                       // repository prohibits forks\r
                        return false;\r
                }\r
-               if (repository.accessRestriction.atLeast(AccessRestrictionType.CLONE)) {\r
-                       return canAccessRepository(repository);\r
+               if (!isAuthenticated || !canFork) {\r
+                       return false;\r
                }\r
-               // repository is not clone-restricted\r
-               return true;\r
-       }\r
-\r
-       public boolean hasRepository(String name) {\r
-               return repositories.contains(name.toLowerCase());\r
+               return canClone(repository);\r
        }\r
-\r
-       public void addRepository(String name) {\r
-               repositories.add(name.toLowerCase());\r
+       \r
+       public boolean canDelete(RepositoryModel model) {\r
+               return canAdmin || model.isUsersPersonalRepository(username);\r
        }\r
-\r
-       public void removeRepository(String name) {\r
-               repositories.remove(name.toLowerCase());\r
+       \r
+       public boolean canEdit(RepositoryModel model) {\r
+               return canAdmin || model.isUsersPersonalRepository(username) || model.isOwner(username);\r
        }\r
 \r
        public boolean isTeamMember(String teamname) {\r
index bc9a1e00f098ea1f72b72b28630074d9f9b40d3d..24f4ecb8671dfdc9e7f7ac8fe923cc192742c77b 100644 (file)
@@ -32,6 +32,7 @@ import java.util.Locale;
 import java.util.Map;\r
 import java.util.TimeZone;\r
 \r
+import com.gitblit.Constants.AccessPermission;\r
 import com.gitblit.GitBlitException.ForbiddenException;\r
 import com.gitblit.GitBlitException.NotAllowedException;\r
 import com.gitblit.GitBlitException.UnauthorizedException;\r
@@ -266,6 +267,7 @@ public class JsonUtils {
        public static Gson gson(ExclusionStrategy... strategies) {\r
                GsonBuilder builder = new GsonBuilder();\r
                builder.registerTypeAdapter(Date.class, new GmtDateTypeAdapter());\r
+               builder.registerTypeAdapter(AccessPermission.class, new AccessPermissionTypeAdapter());\r
                builder.setPrettyPrinting();\r
                if (!ArrayUtils.isEmpty(strategies)) {\r
                        builder.setExclusionStrategies(strategies);\r
@@ -303,6 +305,24 @@ public class JsonUtils {
                        }\r
                }\r
        }\r
+       \r
+       private static class AccessPermissionTypeAdapter implements JsonSerializer<AccessPermission>, JsonDeserializer<AccessPermission> {\r
+\r
+               private AccessPermissionTypeAdapter() {\r
+               }\r
+\r
+               @Override\r
+               public synchronized JsonElement serialize(AccessPermission permission, Type type,\r
+                               JsonSerializationContext jsonSerializationContext) {\r
+                       return new JsonPrimitive(permission.code);\r
+               }\r
+\r
+               @Override\r
+               public synchronized AccessPermission deserialize(JsonElement jsonElement, Type type,\r
+                               JsonDeserializationContext jsonDeserializationContext) {\r
+                       return AccessPermission.fromCode(jsonElement.getAsString());                                    \r
+               }\r
+       }\r
 \r
        public static class ExcludeField implements ExclusionStrategy {\r
 \r
index 00d9677f1f10e3caadb299014533938fd73264d4..cce323fdb9bf5cea1a3e4f557b0c4b591ba093a8 100644 (file)
@@ -297,7 +297,7 @@ public abstract class BasePage extends WebPage {
                        for (ProjectModel projectModel : availableModels) {\r
                                for (String repositoryName : projectModel.repositories) {\r
                                        for (TeamModel teamModel : teamModels) {\r
-                                               if (teamModel.hasRepository(repositoryName)) {\r
+                                               if (teamModel.hasRepositoryPermission(repositoryName)) {\r
                                                        models.add(projectModel);\r
                                                }\r
                                        }\r
index 082dab51179e01cf7a5f20a01f4e0aa506fe0d48..340bd823ea0682b5b11ae56e6761b006eb591343 100644 (file)
@@ -40,7 +40,7 @@ public class ForkPage extends RepositoryPage {
 \r
                RepositoryModel repository = getRepositoryModel();\r
                UserModel user = session.getUser();\r
-               boolean canFork = user.canForkRepository(repository);\r
+               boolean canFork = user.canFork(repository);\r
 \r
                if (!canFork) {\r
                        // redirect to the summary page if this repository is not empty\r
index 2e67e2b779d87e74c4c62fb73c5b3882ad07c302..6155f3edc6875cce5ec50c02d0eaf976b41913b0 100644 (file)
@@ -94,7 +94,7 @@ public class ForksPage extends RepositoryPage {
                                if (user == null) {\r
                                        user = UserModel.ANONYMOUS;\r
                                }\r
-                               if (user.canViewRepository(repository)) {\r
+                               if (user.canView(repository)) {\r
                                        if (pageRepository.equals(repository)) {\r
                                                // do not link to self\r
                                                item.add(new Label("aFork", StringUtils.stripDotGit(repo)));\r
index 2afc2c4d9f4f3e24e93be3f18f4edf3e696033e7..9048eba34ca82efddbdecb62fca9238c632be3ad 100644 (file)
@@ -209,7 +209,7 @@ public abstract class RepositoryPage extends BasePage {
                        if (origin == null) {\r
                                // no origin repository\r
                                add(new Label("originRepository").setVisible(false));\r
-                       } else if (!user.canViewRepository(origin)) {\r
+                       } else if (!user.canView(origin)) {\r
                                // show origin repository without link\r
                                Fragment forkFrag = new Fragment("originRepository", "originFragment", this);\r
                                forkFrag.add(new Label("originRepository", StringUtils.stripDotGit(model.originRepository)));\r
@@ -242,7 +242,7 @@ public abstract class RepositoryPage extends BasePage {
                } else {\r
                        String fork = GitBlit.self().getFork(user.username, model.name);\r
                        boolean hasFork = fork != null;\r
-                       boolean canFork = user.canForkRepository(model);\r
+                       boolean canFork = user.canFork(model);\r
 \r
                        if (hasFork || !canFork) {\r
                                // user not allowed to fork or fork already exists or repo forbids forking\r
index 1e6f130c9ed2e69b334a1d33ccdb3a34f4a97adc..adcd7b16b9f6e0a0d145ae2b03ec3e289e94c6ee 100644 (file)
@@ -418,7 +418,7 @@ public abstract class RootPage extends BasePage {
                        // brute-force our way through finding the matching models\r
                        for (RepositoryModel repositoryModel : availableModels) {\r
                                for (TeamModel teamModel : teamModels) {\r
-                                       if (teamModel.hasRepository(repositoryModel.name)) {\r
+                                       if (teamModel.hasRepositoryPermission(repositoryModel.name)) {\r
                                                models.add(repositoryModel);\r
                                        }\r
                                }\r
index 2c4ffdc9b7f40b419ed2894bdab2b8649eb7ff82..c8f686ad468fbd82bf5377869b210575c2785475 100644 (file)
@@ -136,7 +136,7 @@ public class FederationTests {
                \r
                TeamModel team = new TeamModel("testteam");\r
                team.addUser("test");\r
-               team.addRepository("helloworld.git");\r
+               team.addRepositoryPermission("helloworld.git");\r
                assertTrue(RpcUtils.createTeam(team, url, account, password.toCharArray()));\r
                \r
                users = FederationUtils.getUsers(getRegistration());\r
index 07c5e08e9ab659a84b4d43caf35348f7a7fc96e6..22bcf137c24ba3d282e0313e9f38b8999aa9d2a6 100644 (file)
@@ -49,7 +49,7 @@ import com.gitblit.utils.JGitUtils;
 @RunWith(Suite.class)\r
 @SuiteClasses({ ArrayUtilsTest.class, FileUtilsTest.class, TimeUtilsTest.class,\r
                StringUtilsTest.class, Base64Test.class, JsonUtilsTest.class, ByteFormatTest.class,\r
-               ObjectCacheTest.class, UserServiceTest.class, LdapUserServiceTest.class,\r
+               ObjectCacheTest.class, PermissionsTest.class, UserServiceTest.class, LdapUserServiceTest.class,\r
                MarkdownUtilsTest.class, JGitUtilsTest.class, SyndicationUtilsTest.class,\r
                DiffUtilsTest.class, MetricUtilsTest.class, TicgitUtilsTest.class,\r
                GitBlitTest.class, FederationTests.class, RpcTests.class, GitServletTest.class,\r
index 418f9384099162760f537b54dfddcb8d69ffc352..a188f180f94b4716c95dc7e25e30301a3905421e 100644 (file)
@@ -52,20 +52,21 @@ public class GitBlitTest {
                List<String> users = GitBlit.self().getAllUsernames();\r
                assertTrue("No users found!", users.size() > 0);\r
                assertTrue("Admin not found", users.contains("admin"));\r
-               UserModel model = GitBlit.self().getUserModel("admin");\r
-               assertEquals("admin", model.toString());\r
-               assertTrue("Admin missing #admin role!", model.canAdmin);\r
-               model.canAdmin = false;\r
-               assertFalse("Admin should not have #admin!", model.canAdmin);\r
+               UserModel user = GitBlit.self().getUserModel("admin");\r
+               assertEquals("admin", user.toString());\r
+               assertTrue("Admin missing #admin role!", user.canAdmin);\r
+               user.canAdmin = false;\r
+               assertFalse("Admin should not have #admin!", user.canAdmin);\r
                String repository = GitBlitSuite.getHelloworldRepository().getDirectory().getName();\r
                RepositoryModel repositoryModel = GitBlit.self().getRepositoryModel(repository);\r
+               repositoryModel.accessRestriction = AccessRestrictionType.VIEW;\r
                assertFalse("Admin can still access repository!",\r
-                               model.canAccessRepository(repositoryModel));\r
-               model.addRepository(repository);\r
-               assertTrue("Admin can't access repository!", model.canAccessRepository(repositoryModel));\r
-               assertEquals(GitBlit.self().getRepositoryModel(model, "pretend"), null);\r
-               assertNotNull(GitBlit.self().getRepositoryModel(model, repository));\r
-               assertTrue(GitBlit.self().getRepositoryModels(model).size() > 0);\r
+                               user.canView(repositoryModel));\r
+               user.addRepositoryPermission(repository);\r
+               assertTrue("Admin can't access repository!", user.canView(repositoryModel));\r
+               assertEquals(GitBlit.self().getRepositoryModel(user, "pretend"), null);\r
+               assertNotNull(GitBlit.self().getRepositoryModel(user, repository));\r
+               assertTrue(GitBlit.self().getRepositoryModels(user).size() > 0);\r
        }\r
 \r
        @Test\r
index bdbb2a5a04c50fcc2ebef0ce17fd522986f5a93e..09e0e5ad28961faf3a2445c5a3381e1aaf1b2efe 100644 (file)
@@ -13,18 +13,28 @@ import java.util.concurrent.atomic.AtomicBoolean;
 \r
 import org.eclipse.jgit.api.CloneCommand;\r
 import org.eclipse.jgit.api.Git;\r
+import org.eclipse.jgit.api.ResetCommand.ResetType;\r
+import org.eclipse.jgit.api.errors.GitAPIException;\r
 import org.eclipse.jgit.lib.Constants;\r
+import org.eclipse.jgit.revwalk.RevCommit;\r
+import org.eclipse.jgit.transport.CredentialsProvider;\r
+import org.eclipse.jgit.transport.PushResult;\r
+import org.eclipse.jgit.transport.RefSpec;\r
+import org.eclipse.jgit.transport.RemoteRefUpdate;\r
+import org.eclipse.jgit.transport.RemoteRefUpdate.Status;\r
 import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;\r
 import org.eclipse.jgit.util.FileUtils;\r
 import org.junit.AfterClass;\r
 import org.junit.BeforeClass;\r
 import org.junit.Test;\r
 \r
+import com.gitblit.Constants.AccessPermission;\r
 import com.gitblit.Constants.AccessRestrictionType;\r
 import com.gitblit.Constants.AuthorizationControl;\r
 import com.gitblit.GitBlit;\r
 import com.gitblit.models.RepositoryModel;\r
 import com.gitblit.models.UserModel;\r
+import com.gitblit.utils.JGitUtils;\r
 \r
 public class GitServletTest {\r
 \r
@@ -233,6 +243,213 @@ public class GitServletTest {
                }\r
                close(git);\r
        }\r
+\r
+       @Test\r
+       public void testBlockClone() throws Exception {\r
+               testRefChange(AccessPermission.VIEW, null, null, null);\r
+       }\r
+\r
+       @Test\r
+       public void testBlockPush() throws Exception {\r
+               testRefChange(AccessPermission.CLONE, null, null, null);\r
+       }\r
+\r
+       @Test\r
+       public void testBlockBranchCreation() throws Exception {\r
+               testRefChange(AccessPermission.PUSH, Status.REJECTED_OTHER_REASON, null, null);\r
+       }\r
+\r
+       @Test\r
+       public void testBlockBranchDeletion() throws Exception {\r
+               testRefChange(AccessPermission.CREATE, Status.OK, Status.REJECTED_OTHER_REASON, null);\r
+       }\r
+       \r
+       @Test\r
+       public void testBlockBranchRewind() throws Exception {\r
+               testRefChange(AccessPermission.DELETE, Status.OK, Status.OK, Status.REJECTED_OTHER_REASON);\r
+       }\r
+\r
+       @Test\r
+       public void testBranchRewind() throws Exception {               \r
+               testRefChange(AccessPermission.REWIND, Status.OK, Status.OK, Status.OK);\r
+       }\r
+\r
+       private void testRefChange(AccessPermission permission, Status expectedCreate, Status expectedDelete, Status expectedRewind) throws Exception {\r
+\r
+               UserModel user = new UserModel("james");\r
+               user.password = "james";\r
+               \r
+               if (GitBlit.self().getUserModel(user.username) != null) {\r
+                       GitBlit.self().deleteUser(user.username);\r
+               }\r
+               \r
+               CredentialsProvider cp = new UsernamePasswordCredentialsProvider(user.username, user.password);\r
+               \r
+               // fork from original to a temporary bare repo\r
+               File refChecks = new File(GitBlitSuite.REPOSITORIES, "refchecks/ticgit.git");\r
+               if (refChecks.exists()) {\r
+                       FileUtils.delete(refChecks, FileUtils.RECURSIVE);\r
+               }\r
+               CloneCommand clone = Git.cloneRepository();\r
+               clone.setURI(MessageFormat.format("{0}/git/ticgit.git", url));\r
+               clone.setDirectory(refChecks);\r
+               clone.setBare(true);\r
+               clone.setCloneAllBranches(true);\r
+               clone.setCredentialsProvider(cp);\r
+               close(clone.call());\r
+\r
+               // elevate repository to clone permission\r
+               RepositoryModel model = GitBlit.self().getRepositoryModel("refchecks/ticgit.git");\r
+               switch (permission) {\r
+                       case VIEW:\r
+                               model.accessRestriction = AccessRestrictionType.CLONE;\r
+                               break;\r
+                       case CLONE:\r
+                               model.accessRestriction = AccessRestrictionType.CLONE;\r
+                               break;\r
+                       default:\r
+                               model.accessRestriction = AccessRestrictionType.PUSH;\r
+               }\r
+               model.authorizationControl = AuthorizationControl.NAMED;\r
+               \r
+               // grant user specified\r
+               user.setRepositoryPermission(model.name, permission);\r
+\r
+               GitBlit.self().updateUserModel(user.username, user, true);\r
+               GitBlit.self().updateRepositoryModel(model.name, model, false);\r
+\r
+               // clone temp bare repo to working copy\r
+               File local = new File(GitBlitSuite.REPOSITORIES, "refchecks/ticgit-wc");\r
+               if (local.exists()) {\r
+                       FileUtils.delete(local, FileUtils.RECURSIVE);\r
+               }\r
+               clone = Git.cloneRepository();\r
+               clone.setURI(MessageFormat.format("{0}/git/{1}", url, model.name));\r
+               clone.setDirectory(local);\r
+               clone.setBare(false);\r
+               clone.setCloneAllBranches(true);\r
+               clone.setCredentialsProvider(cp);\r
+               \r
+               try {\r
+                       close(clone.call());\r
+               } catch (GitAPIException e) {\r
+                       if (permission.atLeast(AccessPermission.CLONE)) {\r
+                               throw e;\r
+                       } else {\r
+                               // user does not have clone permission\r
+                               assertTrue(e.getMessage(), e.getMessage().contains("not permitted"));                           \r
+                               return;\r
+                       }\r
+               }\r
+               \r
+               Git git = Git.open(local);\r
+               \r
+               // commit a file and push it\r
+               File file = new File(local, "PUSHCHK");\r
+               OutputStreamWriter os = new OutputStreamWriter(new FileOutputStream(file, true), Constants.CHARSET);\r
+               BufferedWriter w = new BufferedWriter(os);\r
+               w.write("// " + new Date().toString() + "\n");\r
+               w.close();\r
+               git.add().addFilepattern(file.getName()).call();\r
+               git.commit().setMessage("push test").call();\r
+               Iterable<PushResult> results = null;\r
+               try {\r
+                       results = git.push().setCredentialsProvider(cp).setRemote("origin").call();\r
+               } catch (GitAPIException e) {\r
+                       if (permission.atLeast(AccessPermission.PUSH)) {\r
+                               throw e;\r
+                       } else {\r
+                               // user does not have push permission\r
+                               assertTrue(e.getMessage(), e.getMessage().contains("not permitted"));\r
+                               close(git);\r
+                               return;\r
+                       }\r
+               }\r
+               \r
+               for (PushResult result : results) {\r
+                       RemoteRefUpdate ref = result.getRemoteUpdate("refs/heads/master");\r
+                       Status status = ref.getStatus();\r
+                       if (permission.atLeast(AccessPermission.PUSH)) {\r
+                               assertTrue("User failed to push commit?! " + status.name(), Status.OK.equals(status));\r
+                       } else {\r
+                               assertTrue("User was able to push commit! " + status.name(), Status.REJECTED_OTHER_REASON.equals(status));\r
+                               close(git);\r
+                               // skip delete test\r
+                               return;\r
+                       }\r
+               }\r
+               \r
+               // create a local branch and push the new branch back to the origin                             \r
+               git.branchCreate().setName("protectme").call();\r
+               RefSpec refSpec = new RefSpec("refs/heads/protectme:refs/heads/protectme");\r
+               results = git.push().setCredentialsProvider(cp).setRefSpecs(refSpec).setRemote("origin").call();\r
+               for (PushResult result : results) {\r
+                       RemoteRefUpdate ref = result.getRemoteUpdate("refs/heads/protectme");\r
+                       Status status = ref.getStatus();\r
+                       if (Status.OK.equals(expectedCreate)) {\r
+                               assertTrue("User failed to push creation?! " + status.name(), status.equals(expectedCreate));\r
+                       } else {\r
+                               assertTrue("User was able to push ref creation! " + status.name(), status.equals(expectedCreate));\r
+                               close(git);\r
+                               // skip delete test\r
+                               return;\r
+                       }\r
+               }\r
+               \r
+               // delete the branch locally\r
+               git.branchDelete().setBranchNames("protectme").call();\r
+               \r
+               // push a delete ref command\r
+               refSpec = new RefSpec(":refs/heads/protectme");\r
+               results = git.push().setCredentialsProvider(cp).setRefSpecs(refSpec).setRemote("origin").call();\r
+               for (PushResult result : results) {\r
+                       RemoteRefUpdate ref = result.getRemoteUpdate("refs/heads/protectme");\r
+                       Status status = ref.getStatus();\r
+                       if (Status.OK.equals(expectedDelete)) {\r
+                               assertTrue("User failed to push ref deletion?! " + status.name(), status.equals(Status.OK));\r
+                       } else {\r
+                               assertTrue("User was able to push ref deletion?! " + status.name(), status.equals(expectedDelete));\r
+                               close(git);\r
+                               // skip rewind test\r
+                               return;\r
+                       }\r
+               }\r
+               \r
+               // rewind master by two commits\r
+               git.reset().setRef("HEAD~2").setMode(ResetType.HARD).call();\r
+               \r
+               // commit a change on this detached HEAD\r
+               file = new File(local, "REWINDCHK");\r
+               os = new OutputStreamWriter(new FileOutputStream(file, true), Constants.CHARSET);\r
+               w = new BufferedWriter(os);\r
+               w.write("// " + new Date().toString() + "\n");\r
+               w.close();\r
+               git.add().addFilepattern(file.getName()).call();\r
+               RevCommit commit = git.commit().setMessage("rewind master and new commit").call();\r
+               \r
+               // Reset master to our new commit now we our local branch tip is no longer\r
+               // upstream of the remote branch tip.  It is an alternate tip of the branch.\r
+               JGitUtils.setBranchRef(git.getRepository(), "refs/heads/master", commit.getName());\r
+               \r
+               // Try pushing our new tip to the origin.\r
+               // This requires the server to "rewind" it's master branch and update it\r
+               // to point to our alternate tip.  This leaves the original master tip\r
+               // unreferenced.\r
+               results = git.push().setCredentialsProvider(cp).setRemote("origin").setForce(true).call();\r
+               for (PushResult result : results) {\r
+                       RemoteRefUpdate ref = result.getRemoteUpdate("refs/heads/master");\r
+                       Status status = ref.getStatus();\r
+                       if (Status.OK.equals(expectedRewind)) {\r
+                               assertTrue("User failed to rewind master?! " + status.name(), status.equals(expectedRewind));\r
+                       } else {\r
+                               assertTrue("User was able to rewind master?! " + status.name(), status.equals(expectedRewind));\r
+                       }\r
+               }\r
+               close(git);\r
+               \r
+               GitBlit.self().deleteUser(user.username);\r
+       }\r
+\r
        \r
        private void close(Git git) {\r
                // really close the repository\r
index f48704615cfc270667e1309f93282fa73a9efb0c..7e4d630901517695663f187f6dff5c3ace30656c 100644 (file)
@@ -35,6 +35,7 @@ import org.eclipse.jgit.lib.FileMode;
 import org.eclipse.jgit.lib.ObjectId;\r
 import org.eclipse.jgit.lib.PersonIdent;\r
 import org.eclipse.jgit.lib.Repository;\r
+import org.eclipse.jgit.lib.RepositoryCache;\r
 import org.eclipse.jgit.lib.RepositoryCache.FileKey;\r
 import org.eclipse.jgit.revwalk.RevCommit;\r
 import org.eclipse.jgit.revwalk.RevTree;\r
@@ -141,7 +142,8 @@ public class JGitUtilsTest {
                        assertEquals(folder.lastModified(), JGitUtils.getLastChange(repository).getTime());\r
                        assertNull(JGitUtils.getCommit(repository, null));\r
                        repository.close();\r
-                       assertTrue(GitBlit.self().deleteRepository(repositoryName));\r
+                       RepositoryCache.close(repository);\r
+                       FileUtils.delete(repository.getDirectory(), FileUtils.RECURSIVE);\r
                }\r
        }\r
 \r
diff --git a/tests/com/gitblit/tests/PermissionsTest.java b/tests/com/gitblit/tests/PermissionsTest.java
new file mode 100644 (file)
index 0000000..cb9925e
--- /dev/null
@@ -0,0 +1,2391 @@
+/*
+ * Copyright 2012 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.tests;
+
+import java.util.Date;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+import com.gitblit.Constants.AccessPermission;
+import com.gitblit.Constants.AccessRestrictionType;
+import com.gitblit.Constants.AuthorizationControl;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.TeamModel;
+import com.gitblit.models.UserModel;
+
+/**
+ * Comprehensive, brute-force test of all permutations of discrete permissions.
+ * 
+ * @author James Moger
+ *
+ */
+public class PermissionsTest extends Assert {
+
+       /**
+        * Admin access rights/permissions
+        */
+       @Test
+       public void testAdmin() throws Exception {
+               UserModel user = new UserModel("admin");
+               user.canAdmin = true;
+               
+               for (AccessRestrictionType ar : AccessRestrictionType.values()) {
+                       RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+                       repository.authorizationControl = AuthorizationControl.NAMED;
+                       repository.accessRestriction = ar;
+                               
+                       assertTrue("admin CAN NOT view!", user.canView(repository));
+                       assertTrue("admin CAN NOT clone!", user.canClone(repository));
+                       assertTrue("admin CAN NOT push!", user.canPush(repository));
+                       
+                       assertTrue("admin CAN NOT create ref!", user.canCreateRef(repository));
+                       assertTrue("admin CAN NOT delete ref!", user.canDeleteRef(repository));
+                       assertTrue("admin CAN NOT rewind ref!", user.canRewindRef(repository));
+                       
+                       assertTrue("admin CAN NOT fork!", user.canFork(repository));
+                       
+                       assertTrue("admin CAN NOT delete!", user.canDelete(repository));
+                       assertTrue("admin CAN NOT edit!", user.canEdit(repository));
+               }
+       }
+       
+       /**
+        * Anonymous access rights/permissions 
+        */
+       @Test
+       public void testAnonymous_NONE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.NONE;
+               
+               UserModel user = UserModel.ANONYMOUS;
+               
+               // all permissions, except fork
+               assertTrue("anonymous CAN NOT view!", user.canView(repository));
+               assertTrue("anonymous CAN NOT clone!", user.canClone(repository));
+               assertTrue("anonymous CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("anonymous CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("anonymous CAN NOT delete ref!", user.canDeleteRef(repository));
+               assertTrue("anonymous CAN NOT rewind ref!", user.canRewindRef(repository));
+
+               repository.allowForks = false;
+               assertFalse("anonymous CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertFalse("anonymous CAN fork!", user.canFork(repository));
+               
+               assertFalse("anonymous CAN delete!", user.canDelete(repository));
+               assertFalse("anonymous CAN edit!", user.canEdit(repository));
+       }
+       
+       @Test
+       public void testAnonymous_PUSH() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.PUSH;
+
+               UserModel user = UserModel.ANONYMOUS;
+
+               assertTrue("anonymous CAN NOT view!", user.canView(repository));
+               assertTrue("anonymous CAN NOT clone!", user.canClone(repository));
+               assertFalse("anonymous CAN push!", user.canPush(repository));
+               
+               assertFalse("anonymous CAN create ref!", user.canCreateRef(repository));
+               assertFalse("anonymous CAN delete ref!", user.canDeleteRef(repository));
+               assertFalse("anonymous CAN rewind ref!", user.canRewindRef(repository));
+               
+               repository.allowForks = false;
+               assertFalse("anonymous CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertFalse("anonymous CAN fork!", user.canFork(repository));
+       }
+       
+       @Test
+       public void testAnonymous_CLONE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.CLONE;
+
+               UserModel user = UserModel.ANONYMOUS;
+
+               assertTrue("anonymous CAN NOT view!", user.canView(repository));
+               assertFalse("anonymous CAN clone!", user.canClone(repository));
+               assertFalse("anonymous CAN push!", user.canPush(repository));
+               
+               assertFalse("anonymous CAN create ref!", user.canCreateRef(repository));
+               assertFalse("anonymous CAN delete ref!", user.canDeleteRef(repository));
+               assertFalse("anonymous CAN rewind ref!", user.canRewindRef(repository));
+
+               repository.allowForks = false;
+               assertFalse("anonymous CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertFalse("anonymous CAN fork!", user.canFork(repository));
+       }
+       
+       @Test
+       public void testAnonymous_VIEW() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.VIEW;
+               
+               UserModel user = UserModel.ANONYMOUS;
+
+               assertFalse("anonymous CAN view!", user.canView(repository));
+               assertFalse("anonymous CAN clone!", user.canClone(repository));
+               assertFalse("anonymous CAN push!", user.canPush(repository));
+               
+               assertFalse("anonymous CAN create ref!", user.canCreateRef(repository));
+               assertFalse("anonymous CAN delete ref!", user.canDeleteRef(repository));
+               assertFalse("anonymous CAN rewind ref!", user.canRewindRef(repository));
+
+               repository.allowForks = false;
+               assertFalse("anonymous CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertFalse("anonymous CAN fork!", user.canFork(repository));
+       }
+       
+       /**
+        * Authenticated access rights/permissions 
+        */
+       @Test
+       public void testAuthenticated_NONE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.AUTHENTICATED;
+               repository.accessRestriction = AccessRestrictionType.NONE;
+               
+               UserModel user = new UserModel("test");
+               
+               // all permissions, except fork
+               assertTrue("authenticated CAN NOT view!", user.canView(repository));
+               assertTrue("authenticated CAN NOT clone!", user.canClone(repository));
+               assertTrue("authenticated CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("authenticated CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("authenticated CAN NOT delete ref!", user.canDeleteRef(repository));
+               assertTrue("authenticated CAN NOT rewind ref!", user.canRewindRef(repository));
+
+               user.canFork = false;
+               repository.allowForks = false;
+               assertFalse("authenticated CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertFalse("authenticated CAN fork!", user.canFork(repository));
+               user.canFork = true;
+               assertTrue("authenticated CAN NOT fork!", user.canFork(repository));
+               
+               assertFalse("authenticated CAN delete!", user.canDelete(repository));
+               assertFalse("authenticated CAN edit!", user.canEdit(repository));
+       }
+
+       @Test
+       public void testAuthenticated_PUSH() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.AUTHENTICATED;
+               repository.accessRestriction = AccessRestrictionType.PUSH;
+               
+               UserModel user = new UserModel("test");
+
+               assertTrue("authenticated CAN NOT view!", user.canView(repository));
+               assertTrue("authenticated CAN NOT clone!", user.canClone(repository));
+               assertTrue("authenticated CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("authenticated CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("authenticated CAN NOT delete ref!", user.canDeleteRef(repository));
+               assertTrue("authenticated CAN NOT rewind ref!", user.canRewindRef(repository));
+
+               user.canFork = false;
+               repository.allowForks = false;
+               assertFalse("authenticated CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertFalse("authenticated CAN fork!", user.canFork(repository));
+               user.canFork = true;
+               assertTrue("authenticated CAN NOT fork!", user.canFork(repository));
+       }
+       
+       @Test
+       public void testAuthenticated_CLONE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.AUTHENTICATED;
+               repository.accessRestriction = AccessRestrictionType.CLONE;
+                       
+               UserModel user = new UserModel("test");
+
+               assertTrue("authenticated CAN NOT view!", user.canView(repository));
+               assertTrue("authenticated CAN NOT clone!", user.canClone(repository));
+               assertTrue("authenticated CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("authenticated CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("authenticated CAN NOT delete ref!", user.canDeleteRef(repository));
+               assertTrue("authenticated CAN NOT rewind ref!", user.canRewindRef(repository));
+
+               user.canFork = false;
+               repository.allowForks = false;
+               assertFalse("authenticated CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertFalse("authenticated CAN fork!", user.canFork(repository));
+               user.canFork = true;
+               assertTrue("authenticated CAN NOT fork!", user.canFork(repository));
+       }
+       
+       @Test
+       public void testAuthenticated_VIEW() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.AUTHENTICATED;
+               repository.accessRestriction = AccessRestrictionType.VIEW;
+                       
+               UserModel user = new UserModel("test");
+
+               assertTrue("authenticated CAN NOT view!", user.canView(repository));
+               assertTrue("authenticated CAN NOT clone!", user.canClone(repository));
+               assertTrue("authenticated CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("authenticated CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("authenticated CAN NOT delete ref!", user.canDeleteRef(repository));
+               assertTrue("authenticated CAN NOT rewind ref!", user.canRewindRef(repository));
+
+               user.canFork = false;
+               repository.allowForks = false;
+               assertFalse("authenticated CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertFalse("authenticated CAN fork!", user.canFork(repository));
+               user.canFork = true;
+               assertTrue("authenticated CAN NOT fork!", user.canFork(repository));
+       }
+       
+       /**
+        * NONE_NONE = NO access restriction, NO access permission
+        */
+       @Test
+       public void testNamed_NONE_NONE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.NONE;
+
+               UserModel user = new UserModel("test");
+               
+               assertTrue("named CAN NOT view!", user.canView(repository));
+               assertTrue("named CAN NOT clone!", user.canClone(repository));
+               assertTrue("named CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("named CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("named CAN NOT delete ref!", user.canDeleteRef(repository));
+               assertTrue("named CAN NOT rewind ref!", user.canRewindRef(repository));
+               
+               repository.allowForks = false;
+               user.canFork = false;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               user.canFork = true;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertTrue("named CAN NOT fork!", user.canFork(repository));
+               
+               assertFalse("named CAN delete!", user.canDelete(repository));
+               assertFalse("named CAN edit!", user.canEdit(repository));
+       }
+       
+       /**
+        * PUSH_NONE = PUSH access restriction, NO access permission
+        */
+       @Test
+       public void testNamed_PUSH_NONE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.PUSH;
+               
+               UserModel user = new UserModel("test");
+               
+               assertTrue("named CAN NOT view!", user.canView(repository));
+               assertTrue("named CAN NOT clone!", user.canClone(repository));
+               assertFalse("named CAN push!", user.canPush(repository));
+               
+               assertFalse("named CAN create ref!", user.canCreateRef(repository));
+               assertFalse("named CAN delete ref!", user.canDeleteRef(repository));
+               assertFalse("named CAN rewind ref!", user.canRewindRef(repository));
+
+               repository.allowForks = false;
+               user.canFork = false;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               user.canFork = true;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertTrue("named CAN NOT fork!", user.canFork(repository));
+       }
+       
+       /**
+        * CLONE_NONE = CLONE access restriction, NO access permission
+        */
+       @Test
+       public void testNamed_CLONE_NONE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.CLONE;
+               
+               UserModel user = new UserModel("test");
+               
+               assertTrue("named CAN NOT view!", user.canView(repository));
+               assertFalse("named CAN clone!", user.canClone(repository));
+               assertFalse("named CAN push!", user.canPush(repository));
+               
+               assertFalse("named CAN create ref!", user.canCreateRef(repository));
+               assertFalse("named CAN delete ref!", user.canDeleteRef(repository));
+               assertFalse("named CAN rewind ref!", user.canRewindRef(repository));
+
+               repository.allowForks = false;
+               user.canFork = false;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               user.canFork = true;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertFalse("named CAN NOT fork!", user.canFork(repository));
+       }
+       
+       /**
+        * VIEW_NONE = VIEW access restriction, NO access permission
+        */
+       @Test
+       public void testNamed_VIEW_NONE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.VIEW;
+               
+               UserModel user = new UserModel("test");
+               
+               assertFalse("named CAN view!", user.canView(repository));
+               assertFalse("named CAN clone!", user.canClone(repository));
+               assertFalse("named CAN push!", user.canPush(repository));
+               
+               assertFalse("named CAN create ref!", user.canCreateRef(repository));
+               assertFalse("named CAN delete ref!", user.canDeleteRef(repository));
+               assertFalse("named CAN rewind ref!", user.canRewindRef(repository));
+               
+               repository.allowForks = false;
+               user.canFork = false;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               user.canFork = true;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertFalse("named CAN NOT fork!", user.canFork(repository));
+       }
+
+       
+       /**
+        * NONE_VIEW = NO access restriction, VIEW access permission.
+        * (not useful scenario)
+        */
+       @Test
+       public void testNamed_NONE_VIEW() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.NONE;
+
+               UserModel user = new UserModel("test");
+               user.setRepositoryPermission(repository.name, AccessPermission.VIEW);
+               
+               assertTrue("named CAN NOT view!", user.canView(repository));
+               assertTrue("named CAN NOT clone!", user.canClone(repository));
+               assertTrue("named CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("named CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("named CAN NOT delete ref!", user.canDeleteRef(repository));
+               assertTrue("named CAN NOT rewind ref!", user.canRewindRef(repository));
+               
+               repository.allowForks = false;
+               user.canFork = false;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               user.canFork = true;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertTrue("named CAN NOT fork!", user.canFork(repository));
+       }
+       
+       /**
+        * PUSH_VIEW = PUSH access restriction, VIEW access permission
+        */
+       @Test
+       public void testNamed_PUSH_VIEW() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.PUSH;
+               
+               UserModel user = new UserModel("test");
+               user.setRepositoryPermission(repository.name, AccessPermission.VIEW);
+               
+               assertTrue("named CAN NOT view!", user.canView(repository));
+               assertTrue("named CAN NOT clone!", user.canClone(repository));
+               assertFalse("named CAN push!", user.canPush(repository));
+               
+               assertFalse("named CAN create ref!", user.canCreateRef(repository));
+               assertFalse("named CAN delete ref!", user.canDeleteRef(repository));
+               assertFalse("named CAN rewind ref!", user.canRewindRef(repository));
+
+               repository.allowForks = false;
+               user.canFork = false;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               user.canFork = true;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertTrue("named CAN NOT fork!", user.canFork(repository));
+       }
+       
+       /**
+        * CLONE_VIEW = CLONE access restriction, VIEW access permission
+        */
+       @Test
+       public void testNamed_CLONE_VIEW() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.CLONE;
+               
+               UserModel user = new UserModel("test");
+               user.setRepositoryPermission(repository.name, AccessPermission.VIEW);
+
+               assertTrue("named CAN NOT view!", user.canView(repository));
+               assertFalse("named CAN clone!", user.canClone(repository));
+               assertFalse("named CAN push!", user.canPush(repository));
+               
+               assertFalse("named CAN create ref!", user.canCreateRef(repository));
+               assertFalse("named CAN delete ref!", user.canDeleteRef(repository));
+               assertFalse("named CAN rewind ref!", user.canRewindRef(repository));
+
+               repository.allowForks = false;
+               user.canFork = false;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               user.canFork = true;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertFalse("named CAN NOT fork!", user.canFork(repository));
+       }
+       
+       /**
+        * VIEW_VIEW = VIEW access restriction, VIEW access permission
+        */
+       @Test
+       public void testNamed_VIEW_VIEW() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.VIEW;
+               
+               UserModel user = new UserModel("test");
+               user.setRepositoryPermission(repository.name, AccessPermission.VIEW);
+
+               assertTrue("named CAN NOT view!", user.canView(repository));
+               assertFalse("named CAN clone!", user.canClone(repository));
+               assertFalse("named CAN push!", user.canPush(repository));
+               
+               assertFalse("named CAN create ref!", user.canCreateRef(repository));
+               assertFalse("named CAN delete ref!", user.canDeleteRef(repository));
+               assertFalse("named CAN rewind ref!", user.canRewindRef(repository));
+               
+               repository.allowForks = false;
+               user.canFork = false;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               user.canFork = true;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertFalse("named CAN NOT fork!", user.canFork(repository));
+       }
+       
+       /**
+        * NONE_CLONE = NO access restriction, CLONE access permission.
+        * (not useful scenario)
+        */
+       @Test
+       public void testNamed_NONE_CLONE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.NONE;
+
+               UserModel user = new UserModel("test");
+               user.setRepositoryPermission(repository.name, AccessPermission.CLONE);
+               
+               assertTrue("named CAN NOT view!", user.canView(repository));
+               assertTrue("named CAN NOT clone!", user.canClone(repository));
+               assertTrue("named CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("named CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("named CAN NOT delete ref!", user.canDeleteRef(repository));
+               assertTrue("named CAN NOT rewind ref!", user.canRewindRef(repository));
+               
+               repository.allowForks = false;
+               user.canFork = false;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               user.canFork = true;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertTrue("named CAN NOT fork!", user.canFork(repository));
+       }
+       
+       /**
+        * PUSH_CLONE = PUSH access restriction, CLONE access permission
+        */
+       @Test
+       public void testNamed_PUSH_READ() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.PUSH;
+               
+               UserModel user = new UserModel("test");
+               user.setRepositoryPermission(repository.name, AccessPermission.CLONE);
+               
+               assertTrue("named CAN NOT view!", user.canView(repository));
+               assertTrue("named CAN NOT clone!", user.canClone(repository));
+               assertFalse("named CAN push!", user.canPush(repository));
+               
+               assertFalse("named CAN create ref!", user.canCreateRef(repository));
+               assertFalse("named CAN delete ref!", user.canDeleteRef(repository));
+               assertFalse("named CAN rewind ref!", user.canRewindRef(repository));
+
+               repository.allowForks = false;
+               user.canFork = false;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               user.canFork = true;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertTrue("named CAN NOT fork!", user.canFork(repository));
+       }
+       
+       /**
+        * CLONE_CLONE = CLONE access restriction, CLONE access permission
+        */
+       @Test
+       public void testNamed_CLONE_CLONE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.CLONE;
+               
+               UserModel user = new UserModel("test");
+               user.setRepositoryPermission(repository.name, AccessPermission.CLONE);
+
+               assertTrue("named CAN NOT view!", user.canView(repository));
+               assertTrue("named CAN NOT clone!", user.canClone(repository));
+               assertFalse("named CAN push!", user.canPush(repository));
+               
+               assertFalse("named CAN create ref!", user.canCreateRef(repository));
+               assertFalse("named CAN delete ref!", user.canDeleteRef(repository));
+               assertFalse("named CAN rewind ref!", user.canRewindRef(repository));
+
+               repository.allowForks = false;
+               user.canFork = false;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               user.canFork = true;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertTrue("named CAN NOT fork!", user.canFork(repository));
+       }
+       
+       /**
+        * VIEW_CLONE = VIEW access restriction, CLONE access permission
+        */
+       @Test
+       public void testNamed_VIEW_CLONE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.VIEW;
+               
+               UserModel user = new UserModel("test");
+               user.setRepositoryPermission(repository.name, AccessPermission.CLONE);
+
+               assertTrue("named CAN NOT view!", user.canView(repository));
+               assertTrue("named CAN NOT clone!", user.canClone(repository));
+               assertFalse("named CAN push!", user.canPush(repository));
+               
+               assertFalse("named CAN create ref!", user.canCreateRef(repository));
+               assertFalse("named CAN delete ref!", user.canDeleteRef(repository));
+               assertFalse("named CAN rewind ref!", user.canRewindRef(repository));
+               
+               repository.allowForks = false;
+               user.canFork = false;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               user.canFork = true;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertTrue("named CAN NOT fork!", user.canFork(repository));
+       }       
+
+       /**
+        * NONE_PUSH = NO access restriction, PUSH access permission.
+        * (not useful scenario)
+        */
+       @Test
+       public void testNamed_NONE_PUSH() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.NONE;
+
+               UserModel user = new UserModel("test");
+               user.setRepositoryPermission(repository.name, AccessPermission.PUSH);
+               
+               assertTrue("named CAN NOT view!", user.canView(repository));
+               assertTrue("named CAN NOT clone!", user.canClone(repository));
+               assertTrue("named CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("named CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("named CAN NOT delete ref!", user.canDeleteRef(repository));
+               assertTrue("named CAN NOT rewind ref!", user.canRewindRef(repository));
+               
+               repository.allowForks = false;
+               user.canFork = false;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               user.canFork = true;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertTrue("named CAN NOT fork!", user.canFork(repository));
+       }
+       
+       /**
+        * PUSH_PUSH = PUSH access restriction, PUSH access permission
+        */
+       @Test
+       public void testNamed_PUSH_PUSH() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.PUSH;
+               
+               UserModel user = new UserModel("test");
+               user.setRepositoryPermission(repository.name, AccessPermission.PUSH);
+               
+               assertTrue("named CAN NOT view!", user.canView(repository));
+               assertTrue("named CAN NOT clone!", user.canClone(repository));
+               assertTrue("named CAN NOT push!", user.canPush(repository));
+               
+               assertFalse("named CAN create ref!", user.canCreateRef(repository));
+               assertFalse("named CAN delete ref!", user.canDeleteRef(repository));
+               assertFalse("named CAN rewind ref!", user.canRewindRef(repository));
+
+               repository.allowForks = false;
+               user.canFork = false;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               user.canFork = true;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertTrue("named CAN NOT fork!", user.canFork(repository));
+       }
+       
+       /**
+        * CLONE_PUSH = CLONE access restriction, PUSH access permission
+        */
+       @Test
+       public void testNamed_CLONE_PUSH() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.CLONE;
+               
+               UserModel user = new UserModel("test");
+               user.setRepositoryPermission(repository.name, AccessPermission.PUSH);
+
+               assertTrue("named CAN NOT view!", user.canView(repository));
+               assertTrue("named CAN NOT clone!", user.canClone(repository));
+               assertTrue("named CAN NOT push!", user.canPush(repository));
+               
+               assertFalse("named CAN create ref!", user.canCreateRef(repository));
+               assertFalse("named CAN delete red!", user.canDeleteRef(repository));
+               assertFalse("named CAN rewind ref!", user.canRewindRef(repository));
+
+               repository.allowForks = false;
+               user.canFork = false;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               user.canFork = true;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertTrue("named CAN NOT fork!", user.canFork(repository));
+       }
+       
+       /**
+        * VIEW_PUSH = VIEW access restriction, PUSH access permission
+        */
+       @Test
+       public void testNamed_VIEW_PUSH() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.VIEW;
+               
+               UserModel user = new UserModel("test");
+               user.setRepositoryPermission(repository.name, AccessPermission.PUSH);
+
+               assertTrue("named CAN NOT view!", user.canView(repository));
+               assertTrue("named CAN NOT clone!", user.canClone(repository));
+               assertTrue("named CAN not push!", user.canPush(repository));
+               
+               assertFalse("named CAN create ref!", user.canCreateRef(repository));
+               assertFalse("named CAN delete ref!", user.canDeleteRef(repository));
+               assertFalse("named CAN rewind ref!", user.canRewindRef(repository));
+               
+               repository.allowForks = false;
+               user.canFork = false;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               user.canFork = true;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertTrue("named CAN NOT fork!", user.canFork(repository));
+       }
+
+       /**
+        * NONE_CREATE = NO access restriction, CREATE access permission.
+        * (not useful scenario)
+        */
+       @Test
+       public void testNamed_NONE_CREATE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.NONE;
+
+               UserModel user = new UserModel("test");
+               user.setRepositoryPermission(repository.name, AccessPermission.CREATE);
+               
+               assertTrue("named CAN NOT view!", user.canView(repository));
+               assertTrue("named CAN NOT clone!", user.canClone(repository));
+               assertTrue("named CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("named CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("named CAN NOT delete ref!", user.canDeleteRef(repository));
+               assertTrue("named CAN NOT rewind ref!", user.canRewindRef(repository));
+               
+               repository.allowForks = false;
+               user.canFork = false;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               user.canFork = true;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertTrue("named CAN NOT fork!", user.canFork(repository));
+       }
+       
+       /**
+        * PUSH_CREATE = PUSH access restriction, CREATE access permission
+        */
+       @Test
+       public void testNamed_PUSH_CREATE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.PUSH;
+               
+               UserModel user = new UserModel("test");
+               user.setRepositoryPermission(repository.name, AccessPermission.CREATE);
+               
+               assertTrue("named CAN NOT view!", user.canView(repository));
+               assertTrue("named CAN NOT clone!", user.canClone(repository));
+               assertTrue("named CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("named CAN NOT create ref!", user.canCreateRef(repository));
+               assertFalse("named CAN delete ref!", user.canDeleteRef(repository));
+               assertFalse("named CAN rewind ref!", user.canRewindRef(repository));
+
+               repository.allowForks = false;
+               user.canFork = false;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               user.canFork = true;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertTrue("named CAN NOT fork!", user.canFork(repository));
+       }
+       
+       /**
+        * CLONE_CREATE = CLONE access restriction, CREATE access permission
+        */
+       @Test
+       public void testNamed_CLONE_CREATE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.CLONE;
+               
+               UserModel user = new UserModel("test");
+               user.setRepositoryPermission(repository.name, AccessPermission.CREATE);
+
+               assertTrue("named CAN NOT view!", user.canView(repository));
+               assertTrue("named CAN NOT clone!", user.canClone(repository));
+               assertTrue("named CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("named CAN NOT create ref!", user.canCreateRef(repository));
+               assertFalse("named CAN delete red!", user.canDeleteRef(repository));
+               assertFalse("named CAN rewind ref!", user.canRewindRef(repository));
+
+               repository.allowForks = false;
+               user.canFork = false;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               user.canFork = true;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertTrue("named CAN NOT fork!", user.canFork(repository));
+       }
+       
+       /**
+        * VIEW_CREATE = VIEW access restriction, CREATE access permission
+        */
+       @Test
+       public void testNamed_VIEW_CREATE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.VIEW;
+               
+               UserModel user = new UserModel("test");
+               user.setRepositoryPermission(repository.name, AccessPermission.CREATE);
+
+               assertTrue("named CAN NOT view!", user.canView(repository));
+               assertTrue("named CAN NOT clone!", user.canClone(repository));
+               assertTrue("named CAN not push!", user.canPush(repository));
+               
+               assertTrue("named CAN NOT create ref!", user.canCreateRef(repository));
+               assertFalse("named CAN delete ref!", user.canDeleteRef(repository));
+               assertFalse("named CAN rewind ref!", user.canRewindRef(repository));
+               
+               repository.allowForks = false;
+               user.canFork = false;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               user.canFork = true;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertTrue("named CAN NOT fork!", user.canFork(repository));
+       }
+
+       /**
+        * NONE_DELETE = NO access restriction, DELETE access permission.
+        * (not useful scenario)
+        */
+       @Test
+       public void testNamed_NONE_DELETE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.NONE;
+
+               UserModel user = new UserModel("test");
+               user.setRepositoryPermission(repository.name, AccessPermission.DELETE);
+               
+               assertTrue("named CAN NOT view!", user.canView(repository));
+               assertTrue("named CAN NOT clone!", user.canClone(repository));
+               assertTrue("named CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("named CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("named CAN NOT delete ref!", user.canDeleteRef(repository));
+               assertTrue("named CAN NOT rewind ref!", user.canRewindRef(repository));
+               
+               repository.allowForks = false;
+               user.canFork = false;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               user.canFork = true;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertTrue("named CAN NOT fork!", user.canFork(repository));
+       }
+       
+       /**
+        * PUSH_DELETE = PUSH access restriction, DELETE access permission
+        */
+       @Test
+       public void testNamed_PUSH_DELETE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.PUSH;
+               
+               UserModel user = new UserModel("test");
+               user.setRepositoryPermission(repository.name, AccessPermission.DELETE);
+               
+               assertTrue("named CAN NOT view!", user.canView(repository));
+               assertTrue("named CAN NOT clone!", user.canClone(repository));
+               assertTrue("named CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("named CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("named CAN NOT delete ref!", user.canDeleteRef(repository));
+               assertFalse("named CAN rewind ref!", user.canRewindRef(repository));
+
+               repository.allowForks = false;
+               user.canFork = false;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               user.canFork = true;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertTrue("named CAN NOT fork!", user.canFork(repository));
+       }
+       
+       /**
+        * CLONE_DELETE = CLONE access restriction, DELETE access permission
+        */
+       @Test
+       public void testNamed_CLONE_DELETE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.CLONE;
+               
+               UserModel user = new UserModel("test");
+               user.setRepositoryPermission(repository.name, AccessPermission.DELETE);
+
+               assertTrue("named CAN NOT view!", user.canView(repository));
+               assertTrue("named CAN NOT clone!", user.canClone(repository));
+               assertTrue("named CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("named CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("named CAN NOT delete red!", user.canDeleteRef(repository));
+               assertFalse("named CAN rewind ref!", user.canRewindRef(repository));
+
+               repository.allowForks = false;
+               user.canFork = false;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               user.canFork = true;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertTrue("named CAN NOT fork!", user.canFork(repository));
+       }
+       
+       /**
+        * VIEW_DELETE = VIEW access restriction, DELETE access permission
+        */
+       @Test
+       public void testNamed_VIEW_DELETE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.VIEW;
+               
+               UserModel user = new UserModel("test");
+               user.setRepositoryPermission(repository.name, AccessPermission.DELETE);
+
+               assertTrue("named CAN NOT view!", user.canView(repository));
+               assertTrue("named CAN NOT clone!", user.canClone(repository));
+               assertTrue("named CAN not push!", user.canPush(repository));
+               
+               assertTrue("named CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("named CAN NOT delete ref!", user.canDeleteRef(repository));
+               assertFalse("named CAN rewind ref!", user.canRewindRef(repository));
+               
+               repository.allowForks = false;
+               user.canFork = false;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               user.canFork = true;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertTrue("named CAN NOT fork!", user.canFork(repository));
+       }
+       
+       /**
+        * NONE_REWIND = NO access restriction, REWIND access permission.
+        * (not useful scenario)
+        */
+       @Test
+       public void testNamed_NONE_REWIND() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.NONE;
+
+               UserModel user = new UserModel("test");
+               user.setRepositoryPermission(repository.name, AccessPermission.REWIND);
+               
+               assertTrue("named CAN NOT view!", user.canView(repository));
+               assertTrue("named CAN NOT clone!", user.canClone(repository));
+               assertTrue("named CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("named CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("named CAN NOT delete ref!", user.canDeleteRef(repository));
+               assertTrue("named CAN NOT rewind ref!", user.canRewindRef(repository));
+               
+               repository.allowForks = false;
+               user.canFork = false;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               user.canFork = true;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertTrue("named CAN NOT fork!", user.canFork(repository));
+       }
+       
+       /**
+        * PUSH_REWIND = PUSH access restriction, REWIND access permission
+        */
+       @Test
+       public void testNamed_PUSH_REWIND() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.PUSH;
+               
+               UserModel user = new UserModel("test");
+               user.setRepositoryPermission(repository.name, AccessPermission.REWIND);
+               
+               assertTrue("named CAN NOT view!", user.canView(repository));
+               assertTrue("named CAN NOT clone!", user.canClone(repository));
+               assertTrue("named CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("named CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("named CAN NOT delete ref!", user.canDeleteRef(repository));
+               assertTrue("named CAN NOT rewind ref!", user.canRewindRef(repository));
+
+               repository.allowForks = false;
+               user.canFork = false;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               user.canFork = true;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertTrue("named CAN NOT fork!", user.canFork(repository));
+       }
+       
+       /**
+        * CLONE_REWIND = CLONE access restriction, REWIND access permission
+        */
+       @Test
+       public void testNamed_CLONE_REWIND() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.CLONE;
+               
+               UserModel user = new UserModel("test");
+               user.setRepositoryPermission(repository.name, AccessPermission.REWIND);
+
+               assertTrue("named CAN NOT view!", user.canView(repository));
+               assertTrue("named CAN NOT clone!", user.canClone(repository));
+               assertTrue("named CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("named CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("named CAN NOT delete ref!", user.canDeleteRef(repository));
+               assertTrue("named CAN NOT rewind ref!", user.canRewindRef(repository));
+
+               repository.allowForks = false;
+               user.canFork = false;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               user.canFork = true;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertTrue("named CAN NOT fork!", user.canFork(repository));
+       }
+       
+       /**
+        * VIEW_REWIND = VIEW access restriction, REWIND access permission
+        */
+       @Test
+       public void testNamed_VIEW_REWIND() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.VIEW;
+               
+               UserModel user = new UserModel("test");
+               user.setRepositoryPermission(repository.name, AccessPermission.REWIND);
+
+               assertTrue("named CAN NOT view!", user.canView(repository));
+               assertTrue("named CAN NOT clone!", user.canClone(repository));
+               assertTrue("named CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("named CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("named CAN NOT delete ref!", user.canDeleteRef(repository));
+               assertTrue("named CAN NOT rewind ref!", user.canRewindRef(repository));
+               
+               repository.allowForks = false;
+               user.canFork = false;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               user.canFork = true;
+               assertFalse("named CAN fork!", user.canFork(repository));
+               repository.allowForks = true;
+               assertTrue("named CAN NOT fork!", user.canFork(repository));
+       }
+       
+       /**
+        * NONE_NONE = NO access restriction, NO access permission
+        */
+       @Test
+       public void testTeam_NONE_NONE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.NONE;
+
+               TeamModel team = new TeamModel("test");
+               
+               assertTrue("team CAN NOT view!", team.canView(repository));
+               assertTrue("team CAN NOT clone!", team.canClone(repository));
+               assertTrue("team CAN NOT push!", team.canPush(repository));
+               
+               assertTrue("team CAN NOT create ref!", team.canCreateRef(repository));
+               assertTrue("team CAN NOT delete ref!", team.canDeleteRef(repository));
+               assertTrue("team CAN NOT rewind ref!", team.canRewindRef(repository));
+       }
+       
+       /**
+        * PUSH_NONE = PUSH access restriction, NO access permission
+        */
+       @Test
+       public void testTeam_PUSH_NONE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.PUSH;
+
+               TeamModel team = new TeamModel("test");
+               
+               assertTrue("team CAN NOT view!", team.canView(repository));
+               assertTrue("team CAN NOT clone!", team.canClone(repository));
+               assertFalse("team CAN push!", team.canPush(repository));
+               
+               assertFalse("team CAN create ref!", team.canCreateRef(repository));
+               assertFalse("team CAN delete ref!", team.canDeleteRef(repository));
+               assertFalse("team CAN rewind ref!", team.canRewindRef(repository));
+       }
+
+       /**
+        * CLONE_NONE = CLONE access restriction, NO access permission
+        */
+       @Test
+       public void testTeam_CLONE_NONE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.CLONE;
+
+               TeamModel team = new TeamModel("test");
+               
+               assertTrue("team CAN NOT view!", team.canView(repository));
+               assertFalse("team CAN clone!", team.canClone(repository));
+               assertFalse("team CAN push!", team.canPush(repository));
+               
+               assertFalse("team CAN create ref!", team.canCreateRef(repository));
+               assertFalse("team CAN delete ref!", team.canDeleteRef(repository));
+               assertFalse("team CAN rewind ref!", team.canRewindRef(repository));
+       }
+
+       /**
+        * VIEW_NONE = VIEW access restriction, NO access permission
+        */
+       @Test
+       public void testTeam_VIEW_NONE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.VIEW;
+
+               TeamModel team = new TeamModel("test");
+               
+               assertFalse("team CAN view!", team.canView(repository));
+               assertFalse("team CAN clone!", team.canClone(repository));
+               assertFalse("team CAN push!", team.canPush(repository));
+               
+               assertFalse("team CAN create ref!", team.canCreateRef(repository));
+               assertFalse("team CAN delete ref!", team.canDeleteRef(repository));
+               assertFalse("team CAN rewind ref!", team.canRewindRef(repository));
+       }
+       
+       /**
+        * NONE_PUSH = NO access restriction, PUSH access permission
+        * (not useful scenario)
+        */
+       @Test
+       public void testTeam_NONE_PUSH() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.NONE;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.PUSH);
+               
+               assertTrue("team CAN NOT view!", team.canView(repository));
+               assertTrue("team CAN NOT clone!", team.canClone(repository));
+               assertTrue("team CAN NOT push!", team.canPush(repository));
+               
+               assertTrue("team CAN NOT create ref!", team.canCreateRef(repository));
+               assertTrue("team CAN NOT delete ref!", team.canDeleteRef(repository));
+               assertTrue("team CAN NOT rewind ref!", team.canRewindRef(repository));
+       }
+       
+       /**
+        * PUSH_PUSH = PUSH access restriction, PUSH access permission
+        */
+       @Test
+       public void testTeam_PUSH_PUSH() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.PUSH;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.PUSH);
+               
+               assertTrue("team CAN NOT view!", team.canView(repository));
+               assertTrue("team CAN NOT clone!", team.canClone(repository));
+               assertTrue("team CAN NOT push!", team.canPush(repository));
+               
+               assertFalse("team CAN create ref!", team.canCreateRef(repository));
+               assertFalse("team CAN delete ref!", team.canDeleteRef(repository));
+               assertFalse("team CAN rewind ref!", team.canRewindRef(repository));
+       }
+       
+       /**
+        * CLONE_PUSH = CLONE access restriction, PUSH access permission
+        */
+       @Test
+       public void testTeam_CLONE_PUSH() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.CLONE;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.PUSH);
+               
+               assertTrue("team CAN NOT view!", team.canView(repository));
+               assertTrue("team CAN NOT clone!", team.canClone(repository));
+               assertTrue("team CAN NOT push!", team.canPush(repository));
+               
+               assertFalse("team CAN create ref!", team.canCreateRef(repository));
+               assertFalse("team CAN delete ref!", team.canDeleteRef(repository));
+               assertFalse("team CAN rewind ref!", team.canRewindRef(repository));
+       }
+       
+       /**
+        * VIEW_PUSH = VIEW access restriction, PUSH access permission
+        */
+       @Test
+       public void testTeam_VIEW_PUSH() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.VIEW;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.PUSH);
+               
+               assertTrue("team CAN NOT view!", team.canView(repository));
+               assertTrue("team CAN NOT clone!", team.canClone(repository));
+               assertTrue("team CAN NOT push!", team.canPush(repository));
+               
+               assertFalse("team CAN create ref!", team.canCreateRef(repository));
+               assertFalse("team CAN delete ref!", team.canDeleteRef(repository));
+               assertFalse("team CAN rewind ref!", team.canRewindRef(repository));
+       }
+       
+       /**
+        * NONE_CREATE = NO access restriction, CREATE access permission
+        * (not useful scenario)
+        */
+       @Test
+       public void testTeam_NONE_CREATE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.NONE;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.CREATE);
+               
+               assertTrue("team CAN NOT view!", team.canView(repository));
+               assertTrue("team CAN NOT clone!", team.canClone(repository));
+               assertTrue("team CAN NOT push!", team.canPush(repository));
+               
+               assertTrue("team CAN NOT create ref!", team.canCreateRef(repository));
+               assertTrue("team CAN NOT delete ref!", team.canDeleteRef(repository));
+               assertTrue("team CAN NOT rewind ref!", team.canRewindRef(repository));
+       }
+       
+       /**
+        * PUSH_CREATE = PUSH access restriction, CREATE access permission
+        */
+       @Test
+       public void testTeam_PUSH_CREATE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.PUSH;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.CREATE);
+               
+               assertTrue("team CAN NOT view!", team.canView(repository));
+               assertTrue("team CAN NOT clone!", team.canClone(repository));
+               assertTrue("team CAN NOT push!", team.canPush(repository));
+               
+               assertTrue("team CAN NOT create ref!", team.canCreateRef(repository));
+               assertFalse("team CAN delete ref!", team.canDeleteRef(repository));
+               assertFalse("team CAN rewind ref!", team.canRewindRef(repository));
+       }
+       
+       /**
+        * CLONE_CREATE = CLONE access restriction, CREATE access permission
+        */
+       @Test
+       public void testTeam_CLONE_CREATE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.CLONE;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.CREATE);
+               
+               assertTrue("team CAN NOT view!", team.canView(repository));
+               assertTrue("team CAN NOT clone!", team.canClone(repository));
+               assertTrue("team CAN NOT push!", team.canPush(repository));
+               
+               assertTrue("team CAN NOT create ref!", team.canCreateRef(repository));
+               assertFalse("team CAN delete ref!", team.canDeleteRef(repository));
+               assertFalse("team CAN rewind ref!", team.canRewindRef(repository));
+       }
+       
+       /**
+        * VIEW_CREATE = VIEW access restriction, CREATE access permission
+        */
+       @Test
+       public void testTeam_VIEW_CREATE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.VIEW;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.CREATE);
+               
+               assertTrue("team CAN NOT view!", team.canView(repository));
+               assertTrue("team CAN NOT clone!", team.canClone(repository));
+               assertTrue("team CAN NOT push!", team.canPush(repository));
+               
+               assertTrue("team CAN NOT create ref!", team.canCreateRef(repository));
+               assertFalse("team CAN delete ref!", team.canDeleteRef(repository));
+               assertFalse("team CAN rewind ref!", team.canRewindRef(repository));
+       }
+
+       /**
+        * NONE_DELETE = NO access restriction, DELETE access permission
+        * (not useful scenario)
+        */
+       @Test
+       public void testTeam_NONE_DELETE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.NONE;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.DELETE);
+               
+               assertTrue("team CAN NOT view!", team.canView(repository));
+               assertTrue("team CAN NOT clone!", team.canClone(repository));
+               assertTrue("team CAN NOT push!", team.canPush(repository));
+               
+               assertTrue("team CAN NOT create ref!", team.canCreateRef(repository));
+               assertTrue("team CAN NOT delete ref!", team.canDeleteRef(repository));
+               assertTrue("team CAN NOT rewind ref!", team.canRewindRef(repository));
+       }
+       
+       /**
+        * PUSH_DELETE = PUSH access restriction, DELETE access permission
+        */
+       @Test
+       public void testTeam_PUSH_DELETE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.PUSH;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.DELETE);
+               
+               assertTrue("team CAN NOT view!", team.canView(repository));
+               assertTrue("team CAN NOT clone!", team.canClone(repository));
+               assertTrue("team CAN NOT push!", team.canPush(repository));
+               
+               assertTrue("team CAN NOT create ref!", team.canCreateRef(repository));
+               assertTrue("team CAN NOT delete ref!", team.canDeleteRef(repository));
+               assertFalse("team CAN rewind ref!", team.canRewindRef(repository));
+       }
+       
+       /**
+        * CLONE_DELETE = CLONE access restriction, DELETE access permission
+        */
+       @Test
+       public void testTeam_CLONE_DELETE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.CLONE;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.DELETE);
+               
+               assertTrue("team CAN NOT view!", team.canView(repository));
+               assertTrue("team CAN NOT clone!", team.canClone(repository));
+               assertTrue("team CAN NOT push!", team.canPush(repository));
+               
+               assertTrue("team CAN NOT create ref!", team.canCreateRef(repository));
+               assertTrue("team CAN NOT delete ref!", team.canDeleteRef(repository));
+               assertFalse("team CAN rewind ref!", team.canRewindRef(repository));
+       }
+       
+       /**
+        * VIEW_DELETE = VIEW access restriction, DELETE access permission
+        */
+       @Test
+       public void testTeam_VIEW_DELETE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.VIEW;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.DELETE);
+               
+               assertTrue("team CAN NOT view!", team.canView(repository));
+               assertTrue("team CAN NOT clone!", team.canClone(repository));
+               assertTrue("team CAN NOT push!", team.canPush(repository));
+               
+               assertTrue("team CAN NOT create ref!", team.canCreateRef(repository));
+               assertTrue("team CAN NOT delete ref!", team.canDeleteRef(repository));
+               assertFalse("team CAN rewind ref!", team.canRewindRef(repository));
+       }
+       
+       /**
+        * NONE_REWIND = NO access restriction, REWIND access permission
+        * (not useful scenario)
+        */
+       @Test
+       public void testTeam_NONE_REWIND() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.NONE;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.REWIND);
+               
+               assertTrue("team CAN NOT view!", team.canView(repository));
+               assertTrue("team CAN NOT clone!", team.canClone(repository));
+               assertTrue("team CAN NOT push!", team.canPush(repository));
+               
+               assertTrue("team CAN NOT create ref!", team.canCreateRef(repository));
+               assertTrue("team CAN NOT delete ref!", team.canDeleteRef(repository));
+               assertTrue("team CAN NOT rewind ref!", team.canRewindRef(repository));
+       }
+       
+       /**
+        * PUSH_REWIND = PUSH access restriction, REWIND access permission
+        */
+       @Test
+       public void testTeam_PUSH_REWIND() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.PUSH;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.REWIND);
+               
+               assertTrue("team CAN NOT view!", team.canView(repository));
+               assertTrue("team CAN NOT clone!", team.canClone(repository));
+               assertTrue("team CAN NOT push!", team.canPush(repository));
+               
+               assertTrue("team CAN NOT create ref!", team.canCreateRef(repository));
+               assertTrue("team CAN NOT delete ref!", team.canDeleteRef(repository));
+               assertTrue("team CAN NOT rewind ref!", team.canRewindRef(repository));
+       }
+       
+       /**
+        * CLONE_REWIND = CLONE access restriction, REWIND access permission
+        */
+       @Test
+       public void testTeam_CLONE_REWIND() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.CLONE;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.REWIND);
+               
+               assertTrue("team CAN NOT view!", team.canView(repository));
+               assertTrue("team CAN NOT clone!", team.canClone(repository));
+               assertTrue("team CAN NOT push!", team.canPush(repository));
+               
+               assertTrue("team CAN NOT create ref!", team.canCreateRef(repository));
+               assertTrue("team CAN NOT delete ref!", team.canDeleteRef(repository));
+               assertTrue("team CAN NOT rewind ref!", team.canRewindRef(repository));
+       }
+       
+       /**
+        * VIEW_REWIND = VIEW access restriction, REWIND access permission
+        */
+       @Test
+       public void testTeam_VIEW_REWIND() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.VIEW;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.REWIND);
+               
+               assertTrue("team CAN NOT view!", team.canView(repository));
+               assertTrue("team CAN NOT clone!", team.canClone(repository));
+               assertTrue("team CAN NOT push!", team.canPush(repository));
+               
+               assertTrue("team CAN NOT create ref!", team.canCreateRef(repository));
+               assertTrue("team CAN NOT delete ref!", team.canDeleteRef(repository));
+               assertTrue("team CAN NOT rewind ref!", team.canRewindRef(repository));
+       }
+       
+       /**
+        * NONE_CLONE = NO access restriction, CLONE access permission
+        * (not useful scenario)
+        */
+       @Test
+       public void testTeam_NONE_CLONE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.NONE;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.CLONE);
+               
+               assertTrue("team CAN NOT view!", team.canView(repository));
+               assertTrue("team CAN NOT clone!", team.canClone(repository));
+               assertTrue("team CAN NOT push!", team.canPush(repository));
+               
+               assertTrue("team CAN NOT create ref!", team.canCreateRef(repository));
+               assertTrue("team CAN NOT delete ref!", team.canDeleteRef(repository));
+               assertTrue("team CAN NOT rewind ref!", team.canRewindRef(repository));
+       }
+
+       /**
+        * PUSH_CLONE = PUSH access restriction, CLONE access permission
+        */
+       @Test
+       public void testTeam_PUSH_CLONE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.PUSH;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.CLONE);
+               
+               assertTrue("team CAN NOT view!", team.canView(repository));
+               assertTrue("team CAN NOT clone!", team.canClone(repository));
+               assertFalse("team CAN push!", team.canPush(repository));
+               
+               assertFalse("team CAN create ref!", team.canCreateRef(repository));
+               assertFalse("team CAN delete ref!", team.canDeleteRef(repository));
+               assertFalse("team CAN rewind ref!", team.canRewindRef(repository));
+       }
+
+       /**
+        * CLONE_CLONE = CLONE access restriction, CLONE access permission
+        */
+       @Test
+       public void testTeam_CLONE_CLONE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.CLONE;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.CLONE);
+               
+               assertTrue("team CAN NOT view!", team.canView(repository));
+               assertTrue("team CAN NOT clone!", team.canClone(repository));
+               assertFalse("team CAN push!", team.canPush(repository));
+               
+               assertFalse("team CAN create ref!", team.canCreateRef(repository));
+               assertFalse("team CAN delete ref!", team.canDeleteRef(repository));
+               assertFalse("team CAN rewind ref!", team.canRewindRef(repository));
+       }
+       
+       /**
+        * VIEW_CLONE = VIEW access restriction, CLONE access permission
+        */
+       @Test
+       public void testTeam_VIEW_CLONE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.VIEW;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.CLONE);
+               
+               assertTrue("team CAN NOT view!", team.canView(repository));
+               assertTrue("team CAN NOT clone!", team.canClone(repository));
+               assertFalse("team CAN push!", team.canPush(repository));
+               
+               assertFalse("team CAN create ref!", team.canCreateRef(repository));
+               assertFalse("team CAN delete ref!", team.canDeleteRef(repository));
+               assertFalse("team CAN rewind ref!", team.canRewindRef(repository));
+       }
+
+       /**
+        * NONE_VIEW = NO access restriction, VIEW access permission
+        * (not useful scenario)
+        */
+       @Test
+       public void testTeam_NONE_VIEW() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.NONE;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.VIEW);
+               
+               assertTrue("team CAN NOT view!", team.canView(repository));
+               assertTrue("team CAN NOT clone!", team.canClone(repository));
+               assertTrue("team CAN NOT push!", team.canPush(repository));
+               
+               assertTrue("team CAN NOT create ref!", team.canCreateRef(repository));
+               assertTrue("team CAN NOT delete ref!", team.canDeleteRef(repository));
+               assertTrue("team CAN NOT rewind ref!", team.canRewindRef(repository));
+       }
+
+       /**
+        * PUSH_VIEW = PUSH access restriction, VIEW access permission
+        */
+       @Test
+       public void testTeam_PUSH_VIEW() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.PUSH;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.VIEW);
+               
+               assertTrue("team CAN NOT view!", team.canView(repository));
+               assertTrue("team CAN NOT clone!", team.canClone(repository));
+               assertFalse("team CAN push!", team.canPush(repository));
+               
+               assertFalse("team CAN create ref!", team.canCreateRef(repository));
+               assertFalse("team CAN delete ref!", team.canDeleteRef(repository));
+               assertFalse("team CAN rewind ref!", team.canRewindRef(repository));
+       }
+       
+       /**
+        * CLONE_VIEW = CLONE access restriction, VIEW access permission
+        */
+       @Test
+       public void testTeam_CLONE_VIEW() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.CLONE;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.VIEW);
+               
+               assertTrue("team CAN NOT view!", team.canView(repository));
+               assertFalse("team CAN clone!", team.canClone(repository));
+               assertFalse("team CAN push!", team.canPush(repository));
+               
+               assertFalse("team CAN create ref!", team.canCreateRef(repository));
+               assertFalse("team CAN delete ref!", team.canDeleteRef(repository));
+               assertFalse("team CAN rewind ref!", team.canRewindRef(repository));
+       }
+       
+       /**
+        * VIEW_VIEW = VIEW access restriction, VIEW access permission
+        */
+       @Test
+       public void testTeam_VIEW_VIEW() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.VIEW;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.VIEW);
+               
+               assertTrue("team CAN NOT view!", team.canView(repository));
+               assertFalse("team CAN clone!", team.canClone(repository));
+               assertFalse("team CAN push!", team.canPush(repository));
+               
+               assertFalse("team CAN create ref!", team.canCreateRef(repository));
+               assertFalse("team CAN delete ref!", team.canDeleteRef(repository));
+               assertFalse("team CAN rewind ref!", team.canRewindRef(repository));
+       }
+       
+       /**
+        * NONE_NONE = NO access restriction, NO access permission
+        */
+       @Test
+       public void testTeamMember_NONE_NONE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.NONE;
+
+               TeamModel team = new TeamModel("test");
+               UserModel user = new UserModel("test");
+               user.teams.add(team);
+               
+               assertTrue("team member CAN NOT view!", user.canView(repository));
+               assertTrue("team member CAN NOT clone!", user.canClone(repository));
+               assertTrue("team member CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("team member CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("team member CAN NOT delete ref!", user.canDeleteRef(repository));
+               assertTrue("team member CAN NOT rewind ref!", user.canRewindRef(repository));
+       }
+       
+       /**
+        * PUSH_NONE = PUSH access restriction, NO access permission
+        */
+       @Test
+       public void testTeamMember_PUSH_NONE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.PUSH;
+
+               TeamModel team = new TeamModel("test");
+               UserModel user = new UserModel("test");
+               user.teams.add(team);
+               
+               assertTrue("team member CAN NOT view!", user.canView(repository));
+               assertTrue("team member CAN NOT clone!", user.canClone(repository));
+               assertFalse("team member CAN push!", user.canPush(repository));
+               
+               assertFalse("team member CAN create ref!", user.canCreateRef(repository));
+               assertFalse("team member CAN delete ref!", user.canDeleteRef(repository));
+               assertFalse("team member CAN rewind ref!", user.canRewindRef(repository));
+       }
+
+       /**
+        * CLONE_NONE = CLONE access restriction, NO access permission
+        */
+       @Test
+       public void testTeamMember_CLONE_NONE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.CLONE;
+
+               TeamModel team = new TeamModel("test");
+               UserModel user = new UserModel("test");
+               user.teams.add(team);
+               
+               assertTrue("team member CAN NOT view!", user.canView(repository));
+               assertFalse("team member CAN clone!", user.canClone(repository));
+               assertFalse("team member CAN push!", user.canPush(repository));
+               
+               assertFalse("team member CAN create ref!", user.canCreateRef(repository));
+               assertFalse("team member CAN delete ref!", user.canDeleteRef(repository));
+               assertFalse("team member CAN rewind ref!", user.canRewindRef(repository));
+       }
+
+       /**
+        * VIEW_NONE = VIEW access restriction, NO access permission
+        */
+       @Test
+       public void testTeamMember_VIEW_NONE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.VIEW;
+
+               TeamModel team = new TeamModel("test");
+               UserModel user = new UserModel("test");
+               user.teams.add(team);
+               
+               assertFalse("team member CAN view!", user.canView(repository));
+               assertFalse("team member CAN clone!", user.canClone(repository));
+               assertFalse("team member CAN push!", user.canPush(repository));
+               
+               assertFalse("team member CAN create ref!", user.canCreateRef(repository));
+               assertFalse("team member CAN delete ref!", user.canDeleteRef(repository));
+               assertFalse("team member CAN rewind ref!", user.canRewindRef(repository));
+       }
+       
+       /**
+        * NONE_PUSH = NO access restriction, PUSH access permission
+        * (not useful scenario)
+        */
+       @Test
+       public void testTeamMember_NONE_PUSH() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.NONE;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.PUSH);
+               UserModel user = new UserModel("test");
+               user.teams.add(team);
+               
+               assertTrue("team member CAN NOT view!", user.canView(repository));
+               assertTrue("team member CAN NOT clone!", user.canClone(repository));
+               assertTrue("team member CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("team member CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("team member CAN NOT delete ref!", user.canDeleteRef(repository));
+               assertTrue("team member CAN NOT rewind ref!", user.canRewindRef(repository));
+       }
+       
+       /**
+        * PUSH_PUSH = PUSH access restriction, PUSH access permission
+        */
+       @Test
+       public void testTeamMember_PUSH_PUSH() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.PUSH;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.PUSH);
+               UserModel user = new UserModel("test");
+               user.teams.add(team);
+
+               assertTrue("team member CAN NOT view!", user.canView(repository));
+               assertTrue("team member CAN NOT clone!", user.canClone(repository));
+               assertTrue("team member CAN NOT push!", user.canPush(repository));
+               
+               assertFalse("team member CAN create ref!", user.canCreateRef(repository));
+               assertFalse("team member CAN delete ref!", user.canDeleteRef(repository));
+               assertFalse("team member CAN rewind ref!", user.canRewindRef(repository));
+       }
+       
+       /**
+        * CLONE_PUSH = CLONE access restriction, PUSH access permission
+        */
+       @Test
+       public void testTeamMember_CLONE_PUSH() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.CLONE;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.PUSH);
+               UserModel user = new UserModel("test");
+               user.teams.add(team);
+
+               assertTrue("team member CAN NOT view!", user.canView(repository));
+               assertTrue("team member CAN NOT clone!", user.canClone(repository));
+               assertTrue("team member CAN NOT push!", user.canPush(repository));
+               
+               assertFalse("team member CAN create ref!", user.canCreateRef(repository));
+               assertFalse("team member CAN delete ref!", user.canDeleteRef(repository));
+               assertFalse("team member CAN rewind ref!", user.canRewindRef(repository));
+       }
+       
+       /**
+        * VIEW_PUSH = VIEW access restriction, PUSH access permission
+        */
+       @Test
+       public void testTeamMember_VIEW_PUSH() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.VIEW;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.PUSH);
+               UserModel user = new UserModel("test");
+               user.teams.add(team);
+
+               assertTrue("team member CAN NOT view!", user.canView(repository));
+               assertTrue("team member CAN NOT clone!", user.canClone(repository));
+               assertTrue("team member CAN NOT push!", user.canPush(repository));
+               
+               assertFalse("team member CAN create ref!", user.canCreateRef(repository));
+               assertFalse("team member CAN delete ref!", user.canDeleteRef(repository));
+               assertFalse("team member CAN rewind ref!", user.canRewindRef(repository));
+       }
+       
+       /**
+        * NONE_CREATE = NO access restriction, CREATE access permission
+        * (not useful scenario)
+        */
+       @Test
+       public void testTeamMember_NONE_CREATE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.NONE;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.CREATE);
+               UserModel user = new UserModel("test");
+               user.teams.add(team);
+               
+               assertTrue("team member CAN NOT view!", user.canView(repository));
+               assertTrue("team member CAN NOT clone!", user.canClone(repository));
+               assertTrue("team member CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("team member CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("team member CAN NOT delete ref!", user.canDeleteRef(repository));
+               assertTrue("team member CAN NOT rewind ref!", user.canRewindRef(repository));
+       }
+       
+       /**
+        * PUSH_CREATE = PUSH access restriction, CREATE access permission
+        */
+       @Test
+       public void testTeamMember_PUSH_CREATE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.PUSH;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.CREATE);
+               UserModel user = new UserModel("test");
+               user.teams.add(team);
+
+               assertTrue("team member CAN NOT view!", user.canView(repository));
+               assertTrue("team member CAN NOT clone!", user.canClone(repository));
+               assertTrue("team member CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("team member CAN NOT create ref!", user.canCreateRef(repository));
+               assertFalse("team member CAN delete ref!", user.canDeleteRef(repository));
+               assertFalse("team member CAN rewind ref!", user.canRewindRef(repository));
+       }
+       
+       /**
+        * CLONE_CREATE = CLONE access restriction, CREATE access permission
+        */
+       @Test
+       public void testTeamMember_CLONE_CREATE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.CLONE;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.CREATE);
+               UserModel user = new UserModel("test");
+               user.teams.add(team);
+
+               assertTrue("team member CAN NOT view!", user.canView(repository));
+               assertTrue("team member CAN NOT clone!", user.canClone(repository));
+               assertTrue("team member CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("team member CAN NOT create ref!", user.canCreateRef(repository));
+               assertFalse("team member CAN delete ref!", user.canDeleteRef(repository));
+               assertFalse("team member CAN rewind ref!", user.canRewindRef(repository));
+       }
+       
+       /**
+        * VIEW_CREATE = VIEW access restriction, CREATE access permission
+        */
+       @Test
+       public void testTeamMember_VIEW_CREATE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.VIEW;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.CREATE);
+               UserModel user = new UserModel("test");
+               user.teams.add(team);
+
+               assertTrue("team member CAN NOT view!", user.canView(repository));
+               assertTrue("team member CAN NOT clone!", user.canClone(repository));
+               assertTrue("team member CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("team member CAN NOT create ref!", user.canCreateRef(repository));
+               assertFalse("team member CAN delete ref!", user.canDeleteRef(repository));
+               assertFalse("team member CAN rewind ref!", user.canRewindRef(repository));
+       }
+
+       /**
+        * NONE_DELETE = NO access restriction, DELETE access permission
+        * (not useful scenario)
+        */
+       @Test
+       public void testTeamMember_NONE_DELETE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.NONE;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.DELETE);
+               UserModel user = new UserModel("test");
+               user.teams.add(team);
+               
+               assertTrue("team member CAN NOT view!", user.canView(repository));
+               assertTrue("team member CAN NOT clone!", user.canClone(repository));
+               assertTrue("team member CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("team member CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("team member CAN NOT delete ref!", user.canDeleteRef(repository));
+               assertTrue("team member CAN NOT rewind ref!", user.canRewindRef(repository));
+       }
+       
+       /**
+        * PUSH_DELETE = PUSH access restriction, DELETE access permission
+        */
+       @Test
+       public void testTeamMember_PUSH_DELETE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.PUSH;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.DELETE);
+               UserModel user = new UserModel("test");
+               user.teams.add(team);
+
+               assertTrue("team member CAN NOT view!", user.canView(repository));
+               assertTrue("team member CAN NOT clone!", user.canClone(repository));
+               assertTrue("team member CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("team member CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("team member CAN NOT delete ref!", user.canDeleteRef(repository));
+               assertFalse("team member CAN rewind ref!", user.canRewindRef(repository));
+       }
+       
+       /**
+        * CLONE_DELETE = CLONE access restriction, DELETE access permission
+        */
+       @Test
+       public void testTeamMember_CLONE_DELETE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.CLONE;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.DELETE);
+               UserModel user = new UserModel("test");
+               user.teams.add(team);
+
+               assertTrue("team member CAN NOT view!", user.canView(repository));
+               assertTrue("team member CAN NOT clone!", user.canClone(repository));
+               assertTrue("team member CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("team member CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("team member CAN NOT delete ref!", user.canDeleteRef(repository));
+               assertFalse("team member CAN rewind ref!", user.canRewindRef(repository));
+       }
+       
+       /**
+        * VIEW_DELETE = VIEW access restriction, DELETE access permission
+        */
+       @Test
+       public void testTeamMember_VIEW_DELETE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.VIEW;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.DELETE);
+               UserModel user = new UserModel("test");
+               user.teams.add(team);
+
+               assertTrue("team member CAN NOT view!", user.canView(repository));
+               assertTrue("team member CAN NOT clone!", user.canClone(repository));
+               assertTrue("team member CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("team member CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("team member CAN NOT delete ref!", user.canDeleteRef(repository));
+               assertFalse("team member CAN rewind ref!", user.canRewindRef(repository));
+       }
+
+       /**
+        * NONE_REWIND = NO access restriction, REWIND access permission
+        * (not useful scenario)
+        */
+       @Test
+       public void testTeamMember_NONE_REWIND() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.NONE;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.REWIND);
+               UserModel user = new UserModel("test");
+               user.teams.add(team);
+               
+               assertTrue("team member CAN NOT view!", user.canView(repository));
+               assertTrue("team member CAN NOT clone!", user.canClone(repository));
+               assertTrue("team member CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("team member CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("team member CAN NOT delete ref!", user.canDeleteRef(repository));
+               assertTrue("team member CAN NOT rewind ref!", user.canRewindRef(repository));
+       }
+       
+       /**
+        * PUSH_REWIND = PUSH access restriction, REWIND access permission
+        */
+       @Test
+       public void testTeamMember_PUSH_REWIND() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.PUSH;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.REWIND);
+               UserModel user = new UserModel("test");
+               user.teams.add(team);
+
+               assertTrue("team member CAN NOT view!", user.canView(repository));
+               assertTrue("team member CAN NOT clone!", user.canClone(repository));
+               assertTrue("team member CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("team member CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("team member CAN NOT delete ref!", user.canDeleteRef(repository));
+               assertTrue("team member CAN NOT rewind ref!", user.canRewindRef(repository));
+       }
+       
+       /**
+        * CLONE_REWIND = CLONE access restriction, REWIND access permission
+        */
+       @Test
+       public void testTeamMember_CLONE_REWIND() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.CLONE;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.REWIND);
+               UserModel user = new UserModel("test");
+               user.teams.add(team);
+
+               assertTrue("team member CAN NOT view!", user.canView(repository));
+               assertTrue("team member CAN NOT clone!", user.canClone(repository));
+               assertTrue("team member CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("team member CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("team member CAN NOT delete ref!", user.canDeleteRef(repository));
+               assertTrue("team member CAN NOT rewind ref!", user.canRewindRef(repository));
+       }
+       
+       /**
+        * VIEW_REWIND = VIEW access restriction, REWIND access permission
+        */
+       @Test
+       public void testTeamMember_VIEW_REWIND() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.VIEW;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.REWIND);
+               UserModel user = new UserModel("test");
+               user.teams.add(team);
+
+               assertTrue("team member CAN NOT view!", user.canView(repository));
+               assertTrue("team member CAN NOT clone!", user.canClone(repository));
+               assertTrue("team member CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("team member CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("team member CAN NOT delete ref!", user.canDeleteRef(repository));
+               assertTrue("team member CAN NOT rewind ref!", user.canRewindRef(repository));
+       }
+       
+       /**
+        * NONE_CLONE = NO access restriction, CLONE access permission
+        * (not useful scenario)
+        */
+       @Test
+       public void testTeamMember_NONE_CLONE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.NONE;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.CLONE);
+               UserModel user = new UserModel("test");
+               user.teams.add(team);
+
+               assertTrue("team member CAN NOT view!", user.canView(repository));
+               assertTrue("team member CAN NOT clone!", user.canClone(repository));
+               assertTrue("team member CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("team member CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("team member CAN NOT delete ref!", user.canDeleteRef(repository));
+               assertTrue("team member CAN NOT rewind ref!", user.canRewindRef(repository));
+       }
+
+       /**
+        * PUSH_CLONE = PUSH access restriction, CLONE access permission
+        */
+       @Test
+       public void testTeamMember_PUSH_CLONE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.PUSH;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.CLONE);
+               UserModel user = new UserModel("test");
+               user.teams.add(team);
+
+               assertTrue("team member CAN NOT view!", user.canView(repository));
+               assertTrue("team member CAN NOT clone!", user.canClone(repository));
+               assertFalse("team member CAN push!", user.canPush(repository));
+               
+               assertFalse("team member CAN create ref!", user.canCreateRef(repository));
+               assertFalse("team member CAN delete ref!", user.canDeleteRef(repository));
+               assertFalse("team member CAN rewind ref!", user.canRewindRef(repository));
+       }
+
+       /**
+        * CLONE_CLONE = CLONE access restriction, CLONE access permission
+        */
+       @Test
+       public void testTeamMember_CLONE_CLONE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.CLONE;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.CLONE);
+               UserModel user = new UserModel("test");
+               user.teams.add(team);
+
+               assertTrue("team member CAN NOT view!", user.canView(repository));
+               assertTrue("team member CAN NOT clone!", user.canClone(repository));
+               assertFalse("team member CAN push!", user.canPush(repository));
+               
+               assertFalse("team member CAN create ref!", user.canCreateRef(repository));
+               assertFalse("team member CAN delete ref!", user.canDeleteRef(repository));
+               assertFalse("team member CAN rewind ref!", user.canRewindRef(repository));
+       }
+       
+       /**
+        * VIEW_CLONE = VIEW access restriction, CLONE access permission
+        */
+       @Test
+       public void testTeamMember_VIEW_CLONE() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.VIEW;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.CLONE);
+               UserModel user = new UserModel("test");
+               user.teams.add(team);
+
+               assertTrue("team member CAN NOT view!", user.canView(repository));
+               assertTrue("team member CAN NOT clone!", user.canClone(repository));
+               assertFalse("team member CAN push!", user.canPush(repository));
+               
+               assertFalse("team member CAN create ref!", user.canCreateRef(repository));
+               assertFalse("team member CAN delete ref!", user.canDeleteRef(repository));
+               assertFalse("team member CAN rewind ref!", user.canRewindRef(repository));
+       }
+
+       /**
+        * NONE_VIEW = NO access restriction, VIEW access permission
+        * (not useful scenario)
+        */
+       @Test
+       public void testTeamMember_NONE_VIEW() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.NONE;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.VIEW);
+               UserModel user = new UserModel("test");
+               user.teams.add(team);
+
+               assertTrue("team member CAN NOT view!", user.canView(repository));
+               assertTrue("team member CAN NOT clone!", user.canClone(repository));
+               assertTrue("team member CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("team member CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("team member CAN NOT delete ref!", user.canDeleteRef(repository));
+               assertTrue("team member CAN NOT rewind ref!", user.canRewindRef(repository));
+       }
+
+       /**
+        * PUSH_VIEW = PUSH access restriction, VIEW access permission
+        */
+       @Test
+       public void testTeamMember_PUSH_VIEW() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.PUSH;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.VIEW);
+               UserModel user = new UserModel("test");
+               user.teams.add(team);
+
+               assertTrue("team member CAN NOT view!", user.canView(repository));
+               assertTrue("team member CAN NOT clone!", user.canClone(repository));
+               assertFalse("team member CAN push!", user.canPush(repository));
+               
+               assertFalse("team member CAN create ref!", user.canCreateRef(repository));
+               assertFalse("team member CAN delete ref!", user.canDeleteRef(repository));
+               assertFalse("team member CAN rewind ref!", user.canRewindRef(repository));
+       }
+       
+       /**
+        * CLONE_VIEW = CLONE access restriction, VIEW access permission
+        */
+       @Test
+       public void testTeamMember_CLONE_VIEW() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.CLONE;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.VIEW);
+               UserModel user = new UserModel("test");
+               user.teams.add(team);
+
+               assertTrue("team member CAN NOT view!", user.canView(repository));
+               assertFalse("team member CAN clone!", user.canClone(repository));
+               assertFalse("team member CAN push!", user.canPush(repository));
+               
+               assertFalse("team member CAN create ref!", user.canCreateRef(repository));
+               assertFalse("team member CAN delete ref!", user.canDeleteRef(repository));
+               assertFalse("team member CAN rewind ref!", user.canRewindRef(repository));
+       }
+       
+       /**
+        * VIEW_VIEW = VIEW access restriction, VIEW access permission
+        */
+       @Test
+       public void testTeamMember_VIEW_VIEW() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.VIEW;
+
+               TeamModel team = new TeamModel("test");
+               team.setRepositoryPermission(repository.name, AccessPermission.VIEW);
+               UserModel user = new UserModel("test");
+               user.teams.add(team);
+
+               assertTrue("team member CAN NOT view!", user.canView(repository));
+               assertFalse("team member CAN clone!", user.canClone(repository));
+               assertFalse("team member CAN push!", user.canPush(repository));
+               
+               assertFalse("team member CAN create ref!", user.canCreateRef(repository));
+               assertFalse("team member CAN delete ref!", user.canDeleteRef(repository));
+               assertFalse("team member CAN rewind ref!", user.canRewindRef(repository));
+       }
+       
+       @Test
+       public void testOwner() throws Exception {
+               RepositoryModel repository = new RepositoryModel("myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.VIEW;
+
+               UserModel user = new UserModel("test");
+               repository.owner = user.username;
+
+               assertTrue("owner CAN NOT view!", user.canView(repository));
+               assertTrue("owner CAN NOT clone!", user.canClone(repository));
+               assertTrue("owner CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("owner CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("owner CAN NOT delete ref!", user.canDeleteRef(repository));
+               assertTrue("owner CAN NOT rewind ref!", user.canRewindRef(repository));
+
+               assertTrue("owner CAN NOT fork!", user.canFork(repository));
+               
+               assertFalse("owner CAN NOT delete!", user.canDelete(repository));
+               assertTrue("owner CAN NOT edit!", user.canEdit(repository));
+       }
+       
+       @Test
+       public void testOwnerPersonalRepository() throws Exception {
+               RepositoryModel repository = new RepositoryModel("~test/myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.VIEW;
+
+               UserModel user = new UserModel("test");
+               repository.owner = user.username;
+
+               assertTrue("user CAN NOT view!", user.canView(repository));
+               assertTrue("user CAN NOT clone!", user.canClone(repository));
+               assertTrue("user CAN NOT push!", user.canPush(repository));
+               
+               assertTrue("user CAN NOT create ref!", user.canCreateRef(repository));
+               assertTrue("user CAN NOT delete ref!", user.canDeleteRef(repository));
+               assertTrue("user CAN NOT rewind ref!", user.canRewindRef(repository));
+
+               assertFalse("user CAN fork!", user.canFork(repository));
+               
+               assertTrue("user CAN NOT delete!", user.canDelete(repository));
+               assertTrue("user CAN NOT edit!", user.canEdit(repository));
+       }
+
+       @Test
+       public void testVisitorPersonalRepository() throws Exception {
+               RepositoryModel repository = new RepositoryModel("~test/myrepo.git", null, null, new Date());
+               repository.authorizationControl = AuthorizationControl.NAMED;
+               repository.accessRestriction = AccessRestrictionType.VIEW;
+
+               UserModel user = new UserModel("visitor");
+               repository.owner = "test";
+
+               assertFalse("user CAN view!", user.canView(repository));
+               assertFalse("user CAN clone!", user.canClone(repository));
+               assertFalse("user CAN push!", user.canPush(repository));
+               
+               assertFalse("user CAN create ref!", user.canCreateRef(repository));
+               assertFalse("user CAN delete ref!", user.canDeleteRef(repository));
+               assertFalse("user CAN rewind ref!", user.canRewindRef(repository));
+
+               assertFalse("user CAN fork!", user.canFork(repository));
+               
+               assertFalse("user CAN delete!", user.canDelete(repository));
+               assertFalse("user CAN edit!", user.canEdit(repository));
+       }
+}
index 1080849ce85b90c55e94a017a909553da610f9c7..3ad0ec591e2490088cbdd3b389a670a5748b1975 100644 (file)
@@ -247,7 +247,7 @@ public class RpcTests {
                // Create the A-Team\r
                TeamModel aTeam = new TeamModel("A-Team");\r
                aTeam.users.add("admin");\r
-               aTeam.repositories.add("helloworld.git");\r
+               aTeam.addRepositoryPermission("helloworld.git");\r
                assertTrue(RpcUtils.createTeam(aTeam, url, account, password.toCharArray()));\r
 \r
                aTeam = null;\r
@@ -261,7 +261,7 @@ public class RpcTests {
                }\r
                assertNotNull(aTeam);\r
                assertTrue(aTeam.hasUser("admin"));\r
-               assertTrue(aTeam.hasRepository("helloworld.git"));\r
+               assertTrue(aTeam.hasRepositoryPermission("helloworld.git"));\r
 \r
                RepositoryModel helloworld = null;\r
                Map<String, RepositoryModel> repositories = RpcUtils.getRepositories(url, account,\r
index 03051bdb5887c5a004de0e9522d16c0c792f23b6..710d1f35b00b22834e35aa07c53cdd80b194fb3f 100644 (file)
@@ -25,8 +25,10 @@ import java.io.IOException;
 import org.junit.Test;\r
 \r
 import com.gitblit.ConfigUserService;\r
+import com.gitblit.Constants.AccessRestrictionType;\r
 import com.gitblit.FileUserService;\r
 import com.gitblit.IUserService;\r
+import com.gitblit.models.RepositoryModel;\r
 import com.gitblit.models.TeamModel;\r
 import com.gitblit.models.UserModel;\r
 \r
@@ -74,9 +76,9 @@ public class UserServiceTest {
                // add new user\r
                UserModel newUser = new UserModel("test");\r
                newUser.password = "testPassword";\r
-               newUser.addRepository("repo1");\r
-               newUser.addRepository("repo2");\r
-               newUser.addRepository("sub/repo3");\r
+               newUser.addRepositoryPermission("repo1");\r
+               newUser.addRepositoryPermission("repo2");\r
+               newUser.addRepositoryPermission("sub/repo3");\r
                service.updateUserModel(newUser);\r
 \r
                // add one more new user and then test reload of first new user\r
@@ -93,10 +95,10 @@ public class UserServiceTest {
                // confirm reloaded test user\r
                newUser = service.getUserModel("test");\r
                assertEquals("testPassword", newUser.password);\r
-               assertEquals(3, newUser.repositories.size());\r
-               assertTrue(newUser.hasRepository("repo1"));\r
-               assertTrue(newUser.hasRepository("repo2"));\r
-               assertTrue(newUser.hasRepository("sub/repo3"));\r
+               assertEquals(3, newUser.permissions.size());\r
+               assertTrue(newUser.hasRepositoryPermission("repo1"));\r
+               assertTrue(newUser.hasRepositoryPermission("repo2"));\r
+               assertTrue(newUser.hasRepositoryPermission("sub/repo3"));\r
 \r
                // confirm authentication of test user\r
                UserModel testUser = service.authenticate("test", "testPassword".toCharArray());\r
@@ -106,7 +108,7 @@ public class UserServiceTest {
                // delete a repository role and confirm role removal from test user\r
                service.deleteRepositoryRole("repo2");\r
                testUser = service.getUserModel("test");\r
-               assertEquals(2, testUser.repositories.size());\r
+               assertEquals(2, testUser.permissions.size());\r
 \r
                // delete garbage user and confirm user count\r
                service.deleteUser("garbage");\r
@@ -115,7 +117,7 @@ public class UserServiceTest {
                // rename repository and confirm role change for test user\r
                service.renameRepositoryRole("repo1", "newrepo1");\r
                testUser = service.getUserModel("test");\r
-               assertTrue(testUser.hasRepository("newrepo1"));\r
+               assertTrue(testUser.hasRepositoryPermission("newrepo1"));\r
        }\r
 \r
        protected void testTeams(IUserService service) {\r
@@ -123,41 +125,51 @@ public class UserServiceTest {
                // confirm we have 1 team (admins)\r
                assertEquals(1, service.getAllTeamNames().size());\r
                assertEquals("admins", service.getAllTeamNames().get(0));\r
+               \r
+               RepositoryModel newrepo1 = new RepositoryModel("newrepo1", null, null, null);\r
+               newrepo1.accessRestriction = AccessRestrictionType.VIEW;\r
+               RepositoryModel NEWREPO1 = new RepositoryModel("NEWREPO1", null, null, null);\r
+               NEWREPO1.accessRestriction = AccessRestrictionType.VIEW;\r
 \r
                // remove newrepo1 from test user\r
                // now test user has no repositories\r
                UserModel user = service.getUserModel("test");\r
-               user.repositories.clear();\r
+               user.permissions.clear();\r
                service.updateUserModel(user);\r
                user = service.getUserModel("test");\r
-               assertEquals(0, user.repositories.size());\r
-               assertFalse(user.canAccessRepository("newrepo1"));\r
-               assertFalse(user.canAccessRepository("NEWREPO1"));\r
+               assertEquals(0, user.permissions.size());\r
+               assertFalse(user.canView(newrepo1));\r
+               assertFalse(user.canView(NEWREPO1));\r
 \r
                // create test team and add test user and newrepo1\r
                TeamModel team = new TeamModel("testteam");\r
                team.addUser("test");\r
-               team.addRepository("newrepo1");\r
+               team.addRepositoryPermission(newrepo1.name);\r
                service.updateTeamModel(team);\r
 \r
                // confirm 1 user and 1 repo\r
                team = service.getTeamModel("testteam");\r
-               assertEquals(1, team.repositories.size());\r
+               assertEquals(1, team.permissions.size());\r
                assertEquals(1, team.users.size());\r
 \r
                // confirm team membership\r
                user = service.getUserModel("test");\r
-               assertEquals(0, user.repositories.size());\r
+               assertEquals(0, user.permissions.size());\r
                assertEquals(1, user.teams.size());\r
 \r
                // confirm team access\r
-               assertTrue(team.hasRepository("newrepo1"));\r
-               assertTrue(user.hasTeamAccess("newrepo1"));\r
-               assertTrue(team.hasRepository("NEWREPO1"));\r
-               assertTrue(user.hasTeamAccess("NEWREPO1"));\r
+               assertTrue(team.hasRepositoryPermission(newrepo1.name));\r
+               assertTrue(user.canView(newrepo1));\r
+               assertTrue(team.hasRepositoryPermission(NEWREPO1.name));\r
+               assertTrue(user.canView(NEWREPO1));\r
 \r
                // rename the team and add new repository\r
-               team.addRepository("newrepo2");\r
+               RepositoryModel newrepo2 = new RepositoryModel("newrepo2", null, null, null);\r
+               newrepo2.accessRestriction = AccessRestrictionType.VIEW;\r
+               RepositoryModel NEWREPO2 = new RepositoryModel("NEWREPO2", null, null, null);\r
+               NEWREPO2.accessRestriction = AccessRestrictionType.VIEW;\r
+               \r
+               team.addRepositoryPermission(newrepo2.name);\r
                team.name = "testteam2";\r
                service.updateTeamModel("testteam", team);\r
 \r
@@ -165,11 +177,11 @@ public class UserServiceTest {
                user = service.getUserModel("test");\r
 \r
                // confirm user and team can access newrepo2\r
-               assertEquals(2, team.repositories.size());\r
-               assertTrue(team.hasRepository("newrepo2"));\r
-               assertTrue(user.hasTeamAccess("newrepo2"));\r
-               assertTrue(team.hasRepository("NEWREPO2"));\r
-               assertTrue(user.hasTeamAccess("NEWREPO2"));\r
+               assertEquals(2, team.permissions.size());\r
+               assertTrue(team.hasRepositoryPermission(newrepo2.name));\r
+               assertTrue(user.canView(newrepo2));\r
+               assertTrue(team.hasRepositoryPermission(NEWREPO2.name));\r
+               assertTrue(user.canView(NEWREPO2));\r
 \r
                // delete testteam2\r
                service.deleteTeam("testteam2");\r
@@ -178,28 +190,28 @@ public class UserServiceTest {
 \r
                // confirm team does not exist and user can not access newrepo1 and 2\r
                assertEquals(null, team);\r
-               assertFalse(user.canAccessRepository("newrepo1"));\r
-               assertFalse(user.canAccessRepository("newrepo2"));\r
+               assertFalse(user.canView(newrepo1));\r
+               assertFalse(user.canView(newrepo2));\r
 \r
                // create new team and add it to user\r
                // this tests the inverse team creation/team addition\r
                team = new TeamModel("testteam");\r
-               team.addRepository("NEWREPO1");\r
-               team.addRepository("NEWREPO2");\r
+               team.addRepositoryPermission(NEWREPO1.name);\r
+               team.addRepositoryPermission(NEWREPO2.name);\r
                user.teams.add(team);\r
                service.updateUserModel(user);\r
 \r
                // confirm the inverted team addition\r
                user = service.getUserModel("test");\r
                team = service.getTeamModel("testteam");\r
-               assertTrue(user.hasTeamAccess("newrepo1"));\r
-               assertTrue(user.hasTeamAccess("newrepo2"));\r
+               assertTrue(user.canView(newrepo1));\r
+               assertTrue(user.canView(newrepo2));\r
                assertTrue(team.hasUser("test"));\r
 \r
                // drop testteam from user and add nextteam to user\r
                team = new TeamModel("nextteam");\r
-               team.addRepository("NEWREPO1");\r
-               team.addRepository("NEWREPO2");\r
+               team.addRepositoryPermission(NEWREPO1.name);\r
+               team.addRepositoryPermission(NEWREPO2.name);\r
                user.teams.clear();\r
                user.teams.add(team);\r
                service.updateUserModel(user);\r
@@ -207,8 +219,8 @@ public class UserServiceTest {
                // confirm implicit drop\r
                user = service.getUserModel("test");\r
                team = service.getTeamModel("testteam");\r
-               assertTrue(user.hasTeamAccess("newrepo1"));\r
-               assertTrue(user.hasTeamAccess("newrepo2"));\r
+               assertTrue(user.canView(newrepo1));\r
+               assertTrue(user.canView(newrepo2));\r
                assertFalse(team.hasUser("test"));\r
                team = service.getTeamModel("nextteam");\r
                assertTrue(team.hasUser("test"));\r