]> source.dussan.org Git - gitblit.git/commitdiff
Add user. Implemented repository view and git access restrictions.
authorJames Moger <james.moger@gitblit.com>
Thu, 12 May 2011 02:07:18 +0000 (22:07 -0400)
committerJames Moger <james.moger@gitblit.com>
Thu, 12 May 2011 02:07:18 +0000 (22:07 -0400)
27 files changed:
gitblit.properties
src/com/gitblit/Constants.java
src/com/gitblit/GitBlit.java
src/com/gitblit/GitBlitServer.java
src/com/gitblit/GitBlitServlet.java [new file with mode: 0644]
src/com/gitblit/ILoginService.java
src/com/gitblit/JettyLoginService.java
src/com/gitblit/wicket/AuthorizationStrategy.java
src/com/gitblit/wicket/GitBlitWebApp.properties
src/com/gitblit/wicket/GitBlitWebSession.java
src/com/gitblit/wicket/LoginPage.java
src/com/gitblit/wicket/RepositoryPage.java
src/com/gitblit/wicket/User.java [deleted file]
src/com/gitblit/wicket/WicketUtils.java
src/com/gitblit/wicket/models/RepositoryModel.java
src/com/gitblit/wicket/models/User.java [new file with mode: 0644]
src/com/gitblit/wicket/pages/EditRepositoryPage.html
src/com/gitblit/wicket/pages/EditRepositoryPage.java
src/com/gitblit/wicket/pages/EditUserPage.html [new file with mode: 0644]
src/com/gitblit/wicket/pages/EditUserPage.java [new file with mode: 0644]
src/com/gitblit/wicket/pages/RepositoriesPage.html
src/com/gitblit/wicket/pages/RepositoriesPage.java
src/com/gitblit/wicket/resources/gitblit.css
src/com/gitblit/wicket/resources/lock_go_16x16.png [new file with mode: 0644]
src/com/gitblit/wicket/resources/lock_pull_16x16.png [new file with mode: 0644]
src/com/gitblit/wicket/resources/shield_16x16.png [new file with mode: 0644]
users.properties

index c51fffd5af6e1a83f6b1078aab97393962a8dc9b..2bdcf2c86e70920460507a18a8bcb0a10712d21c 100644 (file)
@@ -3,7 +3,7 @@
 #\r
 \r
 # Allow push/pull over http/https with JGit servlet\r
-git.allowPushPull = true\r
+git.enableGitServlet = true\r
 \r
 # Base folder for repositories\r
 # Use forward slashes even on Windows!!\r
@@ -24,9 +24,6 @@ git.cloneUrl = https://localhost/git/
 # Authentication Settings\r
 #\r
 \r
-# Require authentication for http/https push/pull access of git repositories\r
-git.authenticate = true\r
-\r
 # Require authentication to see everything but the admin pages\r
 web.authenticateViewPages = false\r
 \r
@@ -131,11 +128,11 @@ server.log4jPattern.linux =
 # Jetty Settings\r
 #\r
 \r
-# use NIO connectors.  If false, socket connectors will be used.\r
+# Use Jetty NIO connectors.  If false, Jetty Socket connectors will be used.\r
 server.useNio = true\r
 \r
 # Standard http port to serve.  <= 0 disables this connector.\r
-server.httpPort = 80\r
+server.httpPort = 0\r
 \r
 # Secure/SSL https port to serve. <= 0 disables this connector.\r
 server.httpsPort = 443\r
@@ -148,7 +145,10 @@ server.httpBindInterface = localhost
 # You may specify an ip or an empty value to bind to all interfaces.\r
 server.httpsBindInterface = localhost\r
 \r
-# Password for SSL keystore (keystore password and certificate password must match)\r
+# Password for SSL keystore.\r
+# Keystore password and certificate password must match.\r
+# This is provided for convenience, its probably more secure to set this value\r
+# using the --storePassword command line parameter.\r
 server.storePassword = dosomegit\r
 \r
 # Port for shutdown monitor to listen on.\r
index 38f2e7db36f9cd4300e51fa9554d6d674da3b4dd..3ca917dcf7d5618d439f106cbf2961528b09bebb 100644 (file)
@@ -6,14 +6,41 @@ public class Constants {
 \r
        public final static String VERSION = "0.1.0-SNAPSHOT";\r
 \r
-       public final static String ADMIN_ROLE = "admin";\r
-\r
-       public final static String PULL_ROLE = "pull";\r
-\r
-       public final static String PUSH_ROLE = "push";\r
+       public final static String ADMIN_ROLE = "#admin";\r
 \r
        public final static String PROPERTIES_FILE = "gitblit.properties";\r
 \r
+       public static enum AccessRestrictionType {\r
+               NONE, PUSH, CLONE, VIEW;\r
+\r
+               public static AccessRestrictionType fromString(String name) {\r
+                       for (AccessRestrictionType type : values()) {\r
+                               if (type.toString().equalsIgnoreCase(name)) {\r
+                                       return type;\r
+                               }\r
+                       }\r
+                       return NONE;\r
+               }\r
+               \r
+               public boolean atLeast(AccessRestrictionType type) {\r
+                       return this.ordinal() >= type.ordinal();\r
+               }\r
+\r
+               public String toString() {\r
+                       switch (this) {\r
+                       case NONE:\r
+                               return "none";\r
+                       case PUSH:\r
+                               return "push";\r
+                       case CLONE:\r
+                               return "clone";\r
+                       case VIEW:\r
+                               return "view";\r
+                       }\r
+                       return "none";\r
+               }\r
+       }\r
+\r
        public static String getGitBlitVersion() {\r
                return NAME + " v" + VERSION;\r
        }\r
index c633f6e91a22bf9e8f2d65eb89b8198a99e95ee5..bdfa590ebde11f6ffa1928fa2c53a3c35c4d2dea 100644 (file)
@@ -19,9 +19,10 @@ import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
 import org.slf4j.Logger;\r
 import org.slf4j.LoggerFactory;\r
 \r
+import com.gitblit.Constants.AccessRestrictionType;\r
 import com.gitblit.utils.JGitUtils;\r
-import com.gitblit.wicket.User;\r
 import com.gitblit.wicket.models.RepositoryModel;\r
+import com.gitblit.wicket.models.User;\r
 \r
 public class GitBlit implements ServletContextListener {\r
 \r
@@ -94,7 +95,18 @@ public class GitBlit implements ServletContextListener {
                userCookie.setPath("/");\r
                response.addCookie(userCookie);\r
        }\r
-       \r
+\r
+       public User getUser(String username) {\r
+               User user = loginService.getUserModel(username);\r
+               return user;\r
+       }\r
+\r
+       public void editUserModel(User user, boolean isCreate) throws GitBlitException {\r
+               if (!loginService.updateUserModel(user)) {\r
+                       throw new GitBlitException(isCreate ? "Failed to add user!" : "Failed to update user!");\r
+               }\r
+       }\r
+\r
        public List<String> getRepositoryList() {\r
                return JGitUtils.getRepositoryList(repositoriesFolder, exportAll, storedSettings.getBoolean(Keys.git.nestedRepositories, true));\r
        }\r
@@ -112,17 +124,31 @@ public class GitBlit implements ServletContextListener {
                }\r
                return r;\r
        }\r
-       \r
-       public List<RepositoryModel> getRepositoryModels() {\r
+\r
+       public List<RepositoryModel> getRepositoryModels(User user) {\r
                List<String> list = getRepositoryList();\r
                List<RepositoryModel> repositories = new ArrayList<RepositoryModel>();\r
                for (String repo : list) {\r
-                       RepositoryModel model = getRepositoryModel(repo);\r
-                       repositories.add(model);\r
+                       RepositoryModel model = getRepositoryModel(user, repo);\r
+                       if (model != null) {\r
+                               repositories.add(model);\r
+                       }\r
                }\r
                return repositories;\r
        }\r
        \r
+       public RepositoryModel getRepositoryModel(User user, String repositoryName) {\r
+               RepositoryModel model = getRepositoryModel(repositoryName);\r
+               if (model.accessRestriction.atLeast(AccessRestrictionType.VIEW)) {\r
+                       if (user != null && user.canView(model)) {\r
+                               return model;\r
+                       }\r
+                       return null;\r
+               } else {\r
+                       return model;\r
+               }\r
+       }\r
+\r
        public RepositoryModel getRepositoryModel(String repositoryName) {\r
                Repository r = getRepository(repositoryName);\r
                RepositoryModel model = new RepositoryModel();\r
@@ -133,10 +159,9 @@ public class GitBlit implements ServletContextListener {
                if (config != null) {\r
                        model.description = config.getString("gitblit", null, "description");\r
                        model.owner = config.getString("gitblit", null, "owner");\r
-                       model.group = config.getString("gitblit", null, "group");\r
                        model.useTickets = config.getBoolean("gitblit", "useTickets", false);\r
                        model.useDocs = config.getBoolean("gitblit", "useDocs", false);\r
-                       model.useRestrictedAccess = config.getBoolean("gitblit", "restrictedAccess", false);\r
+                       model.accessRestriction = AccessRestrictionType.fromString(config.getString("gitblit", null, "accessRestriction"));\r
                        model.showRemoteBranches = config.getBoolean("gitblit", "showRemoteBranches", false);\r
                }\r
                r.close();\r
@@ -149,7 +174,7 @@ public class GitBlit implements ServletContextListener {
                        if (new File(repositoriesFolder, repository.name).exists()) {\r
                                throw new GitBlitException(MessageFormat.format("Can not create repository {0} because it already exists.", repository.name));\r
                        }\r
-                       // create repository                    \r
+                       // create repository\r
                        logger.info("create repository " + repository.name);\r
                        r = JGitUtils.createRepository(repositoriesFolder, repository.name, true);\r
                } else {\r
@@ -170,7 +195,7 @@ public class GitBlit implements ServletContextListener {
                config.setString("gitblit", null, "owner", repository.owner);\r
                config.setBoolean("gitblit", null, "useTickets", repository.useTickets);\r
                config.setBoolean("gitblit", null, "useDocs", repository.useDocs);\r
-               config.setBoolean("gitblit", null, "restrictedAccess", repository.useRestrictedAccess);\r
+               config.setString("gitblit", null, "accessRestriction", repository.accessRestriction.toString());\r
                config.setBoolean("gitblit", null, "showRemoteBranches", repository.showRemoteBranches);\r
                try {\r
                        config.save();\r
index 0978bc8683923f86b3edaba9ade0f3c2b6b0927a..f5ed91aa4857ffc49d418633d04da6c8e4177df1 100644 (file)
@@ -56,7 +56,6 @@ import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;\r
 import org.eclipse.jetty.util.thread.QueuedThreadPool;\r
 import org.eclipse.jetty.webapp.WebAppContext;\r
-import org.eclipse.jgit.http.server.GitServlet;\r
 \r
 import com.beust.jcommander.JCommander;\r
 import com.beust.jcommander.Parameter;\r
@@ -222,44 +221,42 @@ public class GitBlitServer {
                // Git Servlet\r
                ServletHolder gitServlet = null;\r
                String gitServletPathSpec = "/git/*";\r
-               if (fileSettings.getBoolean(Keys.git.allowPushPull, true)) {\r
-                       gitServlet = rootContext.addServlet(GitServlet.class, gitServletPathSpec);\r
+               if (fileSettings.getBoolean(Keys.git.enableGitServlet, true)) {\r
+                       gitServlet = rootContext.addServlet(GitBlitServlet.class, gitServletPathSpec);\r
                        gitServlet.setInitParameter("base-path", params.repositoriesFolder);\r
-                       gitServlet.setInitParameter("export-all", params.exportAll ? "1" : "0");\r
+                       gitServlet.setInitParameter("export-all", fileSettings.getBoolean(Keys.git.exportAll, true) ? "1" : "0");\r
                }\r
 \r
                // Login Service\r
                LoginService loginService = null;\r
-               String realmUsers = params.realmFile;\r
-               if (realmUsers != null && new File(realmUsers).exists()) {\r
-                       logger.info("Setting up login service from " + realmUsers);\r
-                       JettyLoginService jettyLoginService = new JettyLoginService(realmUsers);\r
-                       GitBlit.self().setLoginService(jettyLoginService);\r
-                       loginService = jettyLoginService;\r
+               String realmUsers = params.realmFile;           \r
+               if (!StringUtils.isEmpty(realmUsers)) { \r
+                       File realmFile = new File(realmUsers);\r
+                       if (realmFile.exists()) {               \r
+                               logger.info("Setting up login service from " + realmUsers);\r
+                               JettyLoginService jettyLoginService = new JettyLoginService(realmFile);\r
+                               GitBlit.self().setLoginService(jettyLoginService);\r
+                               loginService = jettyLoginService;\r
+                       }\r
                }\r
 \r
                // Determine what handler to use\r
                Handler handler;\r
                if (gitServlet != null) {\r
-                       if (loginService != null && params.authenticatePushPull) {\r
-                               // Authenticate Pull/Push\r
-                               String[] roles = new String[] { Constants.PULL_ROLE, Constants.PUSH_ROLE };\r
-                               logger.info("Authentication required for git servlet pull/push access");\r
+                       if (loginService != null) {\r
+                               // Authenticate Clone/Push\r
+                               logger.info("Setting up authenticated git servlet clone/push access");\r
 \r
                                Constraint constraint = new Constraint();\r
-                               constraint.setName("auth");\r
                                constraint.setAuthenticate(true);\r
-                               constraint.setRoles(roles);\r
+                               constraint.setRoles(new String [] { "*" });\r
 \r
                                ConstraintMapping mapping = new ConstraintMapping();\r
                                mapping.setPathSpec(gitServletPathSpec);\r
                                mapping.setConstraint(constraint);\r
 \r
-                               ConstraintSecurityHandler security = new ConstraintSecurityHandler();\r
+                               ConstraintSecurityHandler security = new ConstraintSecurityHandler();                           \r
                                security.addConstraintMapping(mapping);\r
-                               for (String role : roles) {\r
-                                       security.addRole(role);\r
-                               }\r
                                security.setAuthenticator(new BasicAuthenticator());\r
                                security.setLoginService(loginService);\r
                                security.setStrict(false);\r
@@ -273,7 +270,7 @@ public class GitBlitServer {
                                handler = rootContext;\r
                        }\r
                } else {\r
-                       logger.info("Git servlet pull/push disabled");\r
+                       logger.info("Git servlet clone/push disabled");\r
                        handler = rootContext;\r
                }\r
 \r
@@ -448,37 +445,31 @@ public class GitBlitServer {
                @Parameter(names = { "--stop" }, description = "Stop Server")\r
                public Boolean stop = false;\r
 \r
-               @Parameter(names = { "--temp" }, description = "Server temp folder")\r
+               @Parameter(names = { "--tempFolder" }, description = "Server temp folder")\r
                public String temp = fileSettings.getString(Keys.server.tempFolder, "temp");\r
 \r
                /*\r
                 * GIT Servlet Parameters\r
                 */\r
-               @Parameter(names = { "--repos" }, description = "Git Repositories Folder")\r
+               @Parameter(names = { "--repositoriesFolder" }, description = "Git Repositories Folder")\r
                public String repositoriesFolder = fileSettings.getString(Keys.git.repositoriesFolder, "repos");\r
 \r
-               @Parameter(names = { "--exportAll" }, description = "Export All Found Repositories")\r
-               public Boolean exportAll = fileSettings.getBoolean(Keys.git.exportAll, true);\r
-\r
                /*\r
                 * Authentication Parameters\r
                 */\r
-               @Parameter(names = { "--authenticatePushPull" }, description = "Authenticate Git Push/Pull access")\r
-               public Boolean authenticatePushPull = fileSettings.getBoolean(Keys.git.authenticate, true);\r
-\r
-               @Parameter(names = { "--realm" }, description = "Users Realm Hash File")\r
+               @Parameter(names = { "--realmFile" }, description = "Users Realm Hash File")\r
                public String realmFile = fileSettings.getString(Keys.server.realmFile, "users.properties");\r
 \r
                /*\r
                 * JETTY Parameters\r
                 */\r
-               @Parameter(names = { "--nio" }, description = "Use NIO Connector else use Socket Connector.")\r
+               @Parameter(names = { "--useNio" }, description = "Use NIO Connector else use Socket Connector.")\r
                public Boolean useNIO = fileSettings.getBoolean(Keys.server.useNio, true);\r
 \r
-               @Parameter(names = "--port", description = "HTTP port for to serve. (port <= 0 will disable this connector)")\r
+               @Parameter(names = "--httpPort", description = "HTTP port for to serve. (port <= 0 will disable this connector)")\r
                public Integer port = fileSettings.getInteger(Keys.server.httpPort, 80);\r
 \r
-               @Parameter(names = "--securePort", description = "HTTPS port to serve.  (port <= 0 will disable this connector)")\r
+               @Parameter(names = "--httpsPort", description = "HTTPS port to serve.  (port <= 0 will disable this connector)")\r
                public Integer securePort = fileSettings.getInteger(Keys.server.httpsPort, 443);\r
 \r
                @Parameter(names = "--storePassword", description = "Password for SSL (https) keystore.")\r
diff --git a/src/com/gitblit/GitBlitServlet.java b/src/com/gitblit/GitBlitServlet.java
new file mode 100644 (file)
index 0000000..cb23e47
--- /dev/null
@@ -0,0 +1,84 @@
+package com.gitblit;\r
+\r
+import java.io.IOException;\r
+import java.text.MessageFormat;\r
+\r
+import javax.servlet.ServletException;\r
+import javax.servlet.http.HttpServletRequest;\r
+import javax.servlet.http.HttpServletResponse;\r
+\r
+import org.eclipse.jgit.http.server.GitServlet;\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+\r
+import com.gitblit.Constants.AccessRestrictionType;\r
+import com.gitblit.wicket.models.RepositoryModel;\r
+\r
+public class GitBlitServlet extends GitServlet {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       private final Logger logger = LoggerFactory.getLogger(GitBlitServlet.class);\r
+\r
+       public GitBlitServlet() {\r
+               super();\r
+       }\r
+\r
+       @Override\r
+       protected void service(final HttpServletRequest req, final HttpServletResponse rsp) throws ServletException, IOException {\r
+               // admins have full git access to all repositories\r
+               if (req.isUserInRole(Constants.ADMIN_ROLE)) {\r
+                       // admins can do whatever\r
+                       super.service(req, rsp);\r
+                       return;\r
+               }\r
+\r
+               // try to intercept repository names for authenticated access\r
+               String url = req.getRequestURI().substring(req.getServletPath().length());\r
+               if (url.charAt(0) == '/' && url.length() > 1) {\r
+                       url = url.substring(1);\r
+               }\r
+               int forwardSlash = url.indexOf('/');\r
+               if (forwardSlash > -1) {\r
+                       String repository = url.substring(0, forwardSlash);\r
+                       String function = url.substring(forwardSlash + 1);\r
+                       String query = req.getQueryString();\r
+                       RepositoryModel model = GitBlit.self().getRepositoryModel(repository);\r
+                       if (model != null) {\r
+                               if (model.accessRestriction.atLeast(AccessRestrictionType.PUSH)) {\r
+                                       boolean authorizedUser = req.isUserInRole(repository);\r
+                                       if (function.startsWith("git-receive-pack") || (query.indexOf("service=git-receive-pack") > -1)) {\r
+                                               // Push request\r
+                                               boolean pushRestricted = model.accessRestriction.atLeast(AccessRestrictionType.PUSH);\r
+                                               if (!pushRestricted || (pushRestricted && authorizedUser)) {\r
+                                                       // push-unrestricted or push-authorized\r
+                                                       super.service(req, rsp);\r
+                                                       return;\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} ", req.getUserPrincipal().getName(), repository));\r
+                                                       rsp.sendError(HttpServletResponse.SC_FORBIDDEN, MessageFormat.format("you are not authorized to push to {0} ", repository));\r
+                                                       return;\r
+                                               }\r
+                                       } else if (function.startsWith("git-upload-pack") || (query.indexOf("service=git-upload-pack") > -1)) {\r
+                                               // Clone request\r
+                                               boolean cloneRestricted = model.accessRestriction.atLeast(AccessRestrictionType.CLONE);\r
+                                               if (!cloneRestricted || (cloneRestricted && authorizedUser)) {\r
+                                                       // clone-unrestricted or clone-authorized\r
+                                                       super.service(req, rsp);\r
+                                                       return;\r
+                                               } else {\r
+                                                       // user is unauthorized to clone this repository\r
+                                                       logger.warn(MessageFormat.format("user {0} is not authorized to clone {1} ", req.getUserPrincipal().getName(), repository));\r
+                                                       rsp.sendError(HttpServletResponse.SC_FORBIDDEN, MessageFormat.format("you are not authorized to clone {0} ", repository));\r
+                                                       return;\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+\r
+               // pass-through to git servlet\r
+               super.service(req, rsp);\r
+       }\r
+}\r
index b58f4f1f559d9c9dcf0f89a6a0d1befeb2433e25..fc2801d93361f60a8e46e05e238fa32788c08e5c 100644 (file)
@@ -1,10 +1,17 @@
 package com.gitblit;\r
 \r
-import com.gitblit.wicket.User;\r
+import com.gitblit.wicket.models.User;\r
 \r
 public interface ILoginService {\r
 \r
        User authenticate(String username, char[] password);\r
 \r
        User authenticate(char[] cookie);\r
+       \r
+       User getUserModel(String username);\r
+       \r
+       boolean updateUserModel(User model);\r
+       \r
+       boolean deleteUserModel(User model);\r
+       \r
 }\r
index 5173d2133366b02d504f44419890ed1cc0a46bee..ddd3722d868f3c0683b56b3ca39d12fa42def44f 100644 (file)
@@ -1,14 +1,33 @@
 package com.gitblit;\r
 \r
-import org.eclipse.jetty.security.HashLoginService;\r
+import java.io.File;\r
+import java.io.FileReader;\r
+import java.io.FileWriter;\r
+import java.io.IOException;\r
+import java.security.Principal;\r
+import java.util.ArrayList;\r
+import java.util.Map;\r
+import java.util.Properties;\r
+\r
+import javax.security.auth.Subject;\r
+\r
+import org.eclipse.jetty.http.security.Credential;\r
+import org.eclipse.jetty.security.IdentityService;\r
+import org.eclipse.jetty.security.MappedLoginService;\r
 import org.eclipse.jetty.server.UserIdentity;\r
+import org.eclipse.jetty.util.log.Log;\r
+\r
+import com.gitblit.utils.StringUtils;\r
+import com.gitblit.wicket.models.User;\r
 \r
-import com.gitblit.wicket.User;\r
+public class JettyLoginService extends MappedLoginService implements ILoginService {\r
 \r
-public class JettyLoginService extends HashLoginService implements ILoginService {\r
+       private final File realmFile;\r
 \r
-       public JettyLoginService(String realmFile) {\r
-               super(Constants.NAME, realmFile);\r
+       public JettyLoginService(File realmFile) {\r
+               super();\r
+               setName(Constants.NAME);\r
+               this.realmFile = realmFile;\r
        }\r
 \r
        @Override\r
@@ -17,10 +36,19 @@ public class JettyLoginService extends HashLoginService implements ILoginService
                if (identity == null || identity.equals(UserIdentity.UNAUTHENTICATED_IDENTITY)) {\r
                        return null;\r
                }\r
-               User user = new User(username, password);\r
+               User user = new User(username);\r
+               user.setCookie(StringUtils.getSHA1((Constants.NAME + username + new String(password))));\r
                user.canAdmin(identity.isUserInRole(Constants.ADMIN_ROLE, null));\r
-               user.canClone(identity.isUserInRole(Constants.PULL_ROLE, null));\r
-               user.canPush(identity.isUserInRole(Constants.PUSH_ROLE, null));\r
+\r
+               // Add repositories\r
+               for (Principal principal : identity.getSubject().getPrincipals()) {\r
+                       if (principal instanceof RolePrincipal) {\r
+                               RolePrincipal role = (RolePrincipal) principal;\r
+                               if (role.getName().charAt(0) != '#') {\r
+                                       user.addRepository(role.getName().substring(1));\r
+                               }\r
+                       }\r
+               }\r
                return user;\r
        }\r
 \r
@@ -29,4 +57,140 @@ public class JettyLoginService extends HashLoginService implements ILoginService
                // TODO cookie login\r
                return null;\r
        }\r
+\r
+       @Override\r
+       public User getUserModel(String username) {\r
+               User model = new User(username);\r
+               UserIdentity identity = _users.get(username);\r
+               Subject subject = identity.getSubject();\r
+               for (Principal principal : subject.getPrincipals()) {\r
+                       if (principal instanceof RolePrincipal) {\r
+                               RolePrincipal role = (RolePrincipal) principal;\r
+                               String name = role.getName();\r
+                               switch (name.charAt(0)) {\r
+                               case '#':\r
+                                       // Permissions\r
+                                       if (name.equalsIgnoreCase(Constants.ADMIN_ROLE)) {\r
+                                               model.canAdmin(true);\r
+                                       }\r
+                                       break;\r
+                               default:\r
+                                       model.addRepository(name.substring(1));\r
+                               }\r
+                       }\r
+               }\r
+               return model;\r
+       }\r
+\r
+       @Override\r
+       public boolean updateUserModel(User model) {\r
+               try {\r
+                       Properties properties = new Properties();\r
+                       FileReader reader = new FileReader(realmFile);\r
+                       properties.load(reader);\r
+                       reader.close();\r
+\r
+                       ArrayList<String> roles = new ArrayList<String>();\r
+\r
+                       // Repositories\r
+                       roles.addAll(model.getRepositories());\r
+\r
+                       // Permissions\r
+                       if (model.canAdmin()) {\r
+                               roles.add(Constants.ADMIN_ROLE);\r
+                       }\r
+\r
+                       StringBuilder sb = new StringBuilder();\r
+                       sb.append(model.getPassword());\r
+                       sb.append(',');\r
+                       for (String role : roles) {\r
+                               sb.append(role);\r
+                               sb.append(',');\r
+                       }\r
+                       // trim trailing comma\r
+                       sb.setLength(sb.length() - 1);\r
+\r
+                       // Update realm file\r
+                       File realmFileCopy = new File(realmFile.getAbsolutePath() + ".tmp");\r
+                       FileWriter writer = new FileWriter(realmFileCopy);\r
+                       properties.put(model.getUsername(), sb.toString());\r
+                       properties.store(writer, null);\r
+                       writer.close();\r
+                       realmFile.delete();\r
+                       realmFileCopy.renameTo(realmFile);\r
+\r
+                       // Update login service\r
+                       putUser(model.getUsername(), Credential.getCredential(model.getPassword()), roles.toArray(new String[0]));\r
+                       return true;\r
+               } catch (Throwable t) {\r
+                       t.printStackTrace();\r
+               }\r
+               return false;\r
+       }\r
+\r
+       @Override\r
+       public boolean deleteUserModel(User model) {\r
+               try {\r
+                       // Read realm file\r
+                       Properties properties = new Properties();\r
+                       FileReader reader = new FileReader(realmFile);\r
+                       properties.load(reader);\r
+                       reader.close();\r
+                       properties.remove(model.getUsername());\r
+\r
+                       // Update realm file\r
+                       File realmFileCopy = new File(realmFile.getAbsolutePath() + ".tmp");\r
+                       FileWriter writer = new FileWriter(realmFileCopy);\r
+                       properties.store(writer, null);\r
+                       writer.close();\r
+                       realmFile.delete();\r
+                       realmFileCopy.renameTo(realmFile);\r
+\r
+                       // Drop user from map\r
+                       _users.remove(model.getUsername());\r
+                       return true;\r
+               } catch (Throwable t) {\r
+                       t.printStackTrace();\r
+               }\r
+               return false;\r
+       }\r
+\r
+       /* ------------------------------------------------------------ */\r
+       @Override\r
+       public void loadUsers() throws IOException {\r
+               if (realmFile == null)\r
+                       return;\r
+\r
+               if (Log.isDebugEnabled())\r
+                       Log.debug("Load " + this + " from " + realmFile);\r
+               Properties properties = new Properties();\r
+               FileReader reader = new FileReader(realmFile);\r
+               properties.load(reader);\r
+               reader.close();\r
+\r
+               // Map Users\r
+               for (Map.Entry<Object, Object> entry : properties.entrySet()) {\r
+                       String username = ((String) entry.getKey()).trim();\r
+                       String credentials = ((String) entry.getValue()).trim();\r
+                       String roles = null;\r
+                       int c = credentials.indexOf(',');\r
+                       if (c > 0) {\r
+                               roles = credentials.substring(c + 1).trim();\r
+                               credentials = credentials.substring(0, c).trim();\r
+                       }\r
+\r
+                       if (username != null && username.length() > 0 && credentials != null && credentials.length() > 0) {\r
+                               String[] roleArray = IdentityService.NO_ROLES;\r
+                               if (roles != null && roles.length() > 0) {\r
+                                       roleArray = roles.split(",");\r
+                               }\r
+                               putUser(username, Credential.getCredential(credentials), roleArray);\r
+                       }\r
+               }\r
+       }\r
+\r
+       @Override\r
+       protected UserIdentity loadUser(String username) {\r
+               return null;\r
+       }\r
 }\r
index 3e7df36bfd642f15dc3f4ca2221963d44491feee..c4560adcf15c4c846950e506620156feab5d6c45 100644 (file)
@@ -7,6 +7,7 @@ import org.apache.wicket.authorization.strategies.page.AbstractPageAuthorization
 \r
 import com.gitblit.GitBlit;\r
 import com.gitblit.Keys;\r
+import com.gitblit.wicket.models.User;\r
 import com.gitblit.wicket.pages.RepositoriesPage;\r
 \r
 public class AuthorizationStrategy extends AbstractPageAuthorizationStrategy implements IUnauthorizedComponentInstantiationListener {\r
index 39bdd29b9882d5a8d47b95df7045690c29b445fa..f2fe2327ff691498435b5cff673c7c2ae6326e58 100644 (file)
@@ -70,11 +70,19 @@ gb.searchTooltip = Search Git:Blit
 gb.rename = rename\r
 gb.delete = delete\r
 gb.docs = docs\r
-gb.restrictedAccess = restricted access\r
+gb.accessRestriction = access restriction\r
 gb.name = name\r
-gb.group = group\r
 gb.description = description\r
 gb.enableTickets = enable tickets\r
 gb.enableDocs = enable docs\r
 gb.save = save\r
-gb.showRemoteBranches = show remote branches
\ No newline at end of file
+gb.showRemoteBranches = show remote branches\r
+gb.editUsers = edit users\r
+gb.password = password\r
+gb.confirmPassword = confirm password\r
+gb.repositories = repositories\r
+gb.canAdmin can admin\r
+gb.notRestricted = open repository\r
+gb.cloneRestricted = clone-restricted repository\r
+gb.pushRestricted = push-restricted repository\r
+gb.viewRestricted = view-restricted repository
\ No newline at end of file
index 58ba495076e26c3f374db75ddc798723b256a1a6..c6cc36d0f38a68ae8497bd547e9280b0d9c901bc 100644 (file)
@@ -7,6 +7,8 @@ import org.apache.wicket.Session;
 import org.apache.wicket.protocol.http.WebSession;\r
 import org.apache.wicket.protocol.http.request.WebClientInfo;\r
 \r
+import com.gitblit.wicket.models.User;\r
+\r
 public final class GitBlitWebSession extends WebSession {\r
 \r
        private static final long serialVersionUID = 1L;\r
index 3f8206e4012a425decf7c7a8511a688a63c2f6fc..9e26a62ad5aa0dabee4d9baac8634e0642ce1994 100644 (file)
@@ -18,6 +18,7 @@ import org.apache.wicket.protocol.http.WebResponse;
 import com.gitblit.Constants;\r
 import com.gitblit.GitBlit;\r
 import com.gitblit.Keys;\r
+import com.gitblit.wicket.models.User;\r
 \r
 public class LoginPage extends WebPage {\r
 \r
index 1f88075ee0e9df78fe8fa452a82ffffab8a75b52..78fd33ce9501b1df6baffc466fd6feb3a1f50cbc 100644 (file)
@@ -161,11 +161,17 @@ public abstract class RepositoryPage extends BasePage {
 \r
        protected RepositoryModel getRepositoryModel() {\r
                if (m == null) {\r
-                       m = GitBlit.self().getRepositoryModel(repositoryName);\r
+                       RepositoryModel model = GitBlit.self().getRepositoryModel(GitBlitWebSession.get().getUser(), repositoryName);\r
+                       if (model == null) {\r
+                               error("Unauthorized access for repository " + repositoryName);\r
+                               redirectToInterceptPage(new RepositoriesPage());\r
+                               return null;                            \r
+                       }\r
+                       m = model;\r
                }\r
                return m;\r
        }\r
-       \r
+\r
        protected RevCommit getCommit() {\r
                RevCommit commit = JGitUtils.getCommit(r, objectId);\r
                if (commit == null) {\r
diff --git a/src/com/gitblit/wicket/User.java b/src/com/gitblit/wicket/User.java
deleted file mode 100644 (file)
index bd5e8c9..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-package com.gitblit.wicket;\r
-\r
-import java.io.Serializable;\r
-\r
-import com.gitblit.Constants;\r
-import com.gitblit.utils.StringUtils;\r
-\r
-public class User implements Serializable {\r
-\r
-       private static final long serialVersionUID = 1L;\r
-       \r
-       private String username;\r
-       private String cookie;\r
-       private boolean canAdmin = false;\r
-       private boolean canClone = false;\r
-       private boolean canPush = false;\r
-\r
-       public User(String username, char[] password) {\r
-               this.username = username;\r
-               this.cookie = StringUtils.getSHA1((Constants.NAME + username + new String(password)));\r
-       }\r
-\r
-       public void canAdmin(boolean value) {\r
-               canAdmin = value;\r
-       }\r
-\r
-       public boolean canAdmin() {\r
-               return canAdmin;\r
-       }\r
-\r
-       public void canClone(boolean value) {\r
-               canClone = value;\r
-       }\r
-\r
-       public boolean canClone() {\r
-               return canClone;\r
-       }\r
-\r
-       public void canPush(boolean value) {\r
-               canPush = value;\r
-       }\r
-\r
-       public boolean canPush() {\r
-               return canPush;\r
-       }\r
-\r
-       public String getCookie() {\r
-               return cookie;\r
-       }\r
-\r
-       public String toString() {\r
-               return username;\r
-       }\r
-}\r
index bf2bcb9332fde0740395a0d3f572500f5862a2e8..f0ccbf4b5963d0ebe2410c86eac937f31a1e6dbb 100644 (file)
@@ -227,6 +227,10 @@ public class WicketUtils {
        public static int getPage(PageParameters params) {\r
                return params.getInt("page", 1); // index from 1\r
        }\r
+       \r
+       public static String getUsername(PageParameters params) {\r
+               return params.getString("user", "");\r
+       }\r
 \r
        public static Label createDateLabel(String wicketId, Date date, TimeZone timeZone) {\r
                DateFormat df = new SimpleDateFormat(GitBlit.self().settings().getString(Keys.web.datestampShortFormat, "MM/dd/yy"));\r
index d21cff6117d525cbc9f6a2b5d1d48aabad6a12eb..43a7ac184afca99299dc09b6b75951f918bad392 100644 (file)
@@ -3,28 +3,29 @@ package com.gitblit.wicket.models;
 import java.io.Serializable;\r
 import java.util.Date;\r
 \r
+import com.gitblit.Constants.AccessRestrictionType;\r
+\r
 public class RepositoryModel implements Serializable {\r
 \r
        private static final long serialVersionUID = 1L;\r
        public String name;\r
        public String description;\r
        public String owner;\r
-       public String group;\r
        public Date lastChange;\r
        public boolean hasCommits;\r
        public boolean showRemoteBranches;\r
        public boolean useTickets;\r
        public boolean useDocs;\r
-       public boolean useRestrictedAccess;\r
+       public AccessRestrictionType accessRestriction;\r
 \r
        public RepositoryModel() {\r
-       \r
+\r
        }\r
-       \r
+\r
        public RepositoryModel(String name, String description, String owner, Date lastchange) {\r
                this.name = name;\r
                this.description = description;\r
                this.owner = owner;\r
                this.lastChange = lastchange;\r
-       }\r
+       }       \r
 }
\ No newline at end of file
diff --git a/src/com/gitblit/wicket/models/User.java b/src/com/gitblit/wicket/models/User.java
new file mode 100644 (file)
index 0000000..0784839
--- /dev/null
@@ -0,0 +1,89 @@
+package com.gitblit.wicket.models;\r
+\r
+import java.io.Serializable;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+import com.gitblit.Constants.AccessRestrictionType;\r
+\r
+public class User implements Serializable {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       private String username;\r
+       private String password;\r
+       private String cookie;\r
+       private boolean canAdmin = false;\r
+       private List<String> repositories = new ArrayList<String>();\r
+\r
+       public User(String username) {\r
+               this.username = username;\r
+       }\r
+\r
+       public String getUsername() {\r
+               return username;\r
+       }\r
+\r
+       public String getPassword() {\r
+               return password;\r
+       }\r
+\r
+       public void setPassword(String password) {\r
+               this.password = password;\r
+       }\r
+\r
+       public void canAdmin(boolean value) {\r
+               canAdmin = value;\r
+       }\r
+\r
+       public boolean canAdmin() {\r
+               return canAdmin;\r
+       }\r
+       \r
+       public boolean canClone(RepositoryModel repository) {\r
+               return canAccess(repository, AccessRestrictionType.CLONE);\r
+       }\r
+\r
+       public boolean canPush(RepositoryModel repository) {\r
+               return canAccess(repository, AccessRestrictionType.PUSH);\r
+       }\r
+       \r
+       public boolean canView(RepositoryModel repository) {\r
+               return canAccess(repository, AccessRestrictionType.VIEW);\r
+       }\r
+       \r
+       private boolean canAccess(RepositoryModel repository, AccessRestrictionType minimum) {\r
+               if (repository.accessRestriction.atLeast(minimum)) {\r
+                       // repository is restricted, must check roles\r
+                       return canAdmin || repositories.contains(repository.name);\r
+               } else {\r
+                       // repository is not restricted\r
+                       return true;\r
+               }\r
+       }\r
+\r
+       public void setCookie(String cookie) {\r
+               this.cookie = cookie;\r
+       }\r
+\r
+       public String getCookie() {\r
+               return cookie;\r
+       }\r
+\r
+       public void setRepositories(List<String> repositories) {\r
+               this.repositories.clear();\r
+               this.repositories.addAll(repositories);\r
+       }\r
+\r
+       public void addRepository(String name) {\r
+               repositories.add(name.toLowerCase());\r
+       }\r
+\r
+       public List<String> getRepositories() {\r
+               return repositories;\r
+       }\r
+\r
+       public String toString() {\r
+               return username;\r
+       }\r
+}\r
index bc965e7a3f372daa0d337b4e0b22b393b4ab6384..5872347586fc77afdeb03d01286b4a21f7dd336d 100644 (file)
@@ -4,8 +4,8 @@
       xml:lang="en"  \r
       lang="en"> \r
 \r
-<body>\r
 <wicket:extend>\r
+<body onload="document.getElementById('name').focus();">\r
        <!-- Push content down to preserve header image -->\r
        <div style="padding-top:20px"></div>\r
 \r
        <form wicket:id="editForm">\r
                <table class="plain">\r
                        <tbody>\r
-                               <tr><th><wicket:message key="gb.name"></wicket:message></th><td class="edit"><input type="text" wicket:id="name" size="30" tabindex="1" /></td></tr>\r
-                               <tr><th><wicket:message key="gb.description"></wicket:message></th><td class="edit"><input type="text" wicket:id="description" size="80" tabindex="2" /></td></tr>\r
-                               <tr><th><wicket:message key="gb.owner"></wicket:message></th><td class="edit"><input type="text" wicket:id="owner" size="30" tabindex="3" /></td></tr>\r
-                               <tr><th><wicket:message key="gb.group"></wicket:message></th><td class="edit"><input type="text" wicket:id="group" size="30" tabindex="4" /></td></tr>\r
+                               <tr><th><wicket:message key="gb.name"></wicket:message></th><td class="edit"><input type="text" wicket:id="name" id="name" size="40" tabindex="1" /></td></tr>\r
+                               <tr><th><wicket:message key="gb.description"></wicket:message></th><td class="edit"><input type="text" wicket:id="description" size="40" tabindex="2" /></td></tr>\r
+                               <tr><th><wicket:message key="gb.owner"></wicket:message></th><td class="edit"><input type="text" wicket:id="owner" size="40" tabindex="3" /></td></tr>\r
+                               <tr><th><wicket:message key="gb.accessRestriction"></wicket:message></th><td class="edit"><select wicket:id="accessRestriction" tabindex="4" /></td></tr>                               \r
                                <tr><th><wicket:message key="gb.enableTickets"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="useTickets" tabindex="5" /> &nbsp;<i>distributed Ticgit issues</i></td></tr>\r
-                               <tr><th><wicket:message key="gb.enableDocs"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="useDocs" tabindex="6" /> &nbsp;<i>enumerates repository Markdown documentation</i></td></tr>\r
+                               <tr><th><wicket:message key="gb.enableDocs"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="useDocs" tabindex="6" /> &nbsp;<i>enumerates Markdown documentation in repository</i></td></tr>\r
                                <tr><th><wicket:message key="gb.showRemoteBranches"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="showRemoteBranches" tabindex="7" /> &nbsp;<i>show remote branches</i></td></tr>\r
-                               <tr><td class="edit" colspan="2"><input type="submit" value="Save" wicket:message="value:gb.save" tabindex="8" /></td></tr>\r
+                               <tr><th></th><td class="editButton"><input type="submit" value="Save" wicket:message="value:gb.save" tabindex="8" /></td></tr>\r
                        </tbody>\r
                </table>\r
        </form> \r
 \r
-</wicket:extend>\r
 </body>\r
+</wicket:extend>\r
 </html>
\ No newline at end of file
index 6bf16599fe6640b2b059d1a2ba54db1727f6553b..2d2b0ae2b5e0c12b653848ed7bce7e6dc9f9c666 100644 (file)
@@ -1,13 +1,16 @@
 package com.gitblit.wicket.pages;\r
 \r
+import java.util.Arrays;\r
 import java.util.Date;\r
 \r
 import org.apache.wicket.PageParameters;\r
 import org.apache.wicket.markup.html.form.CheckBox;\r
+import org.apache.wicket.markup.html.form.DropDownChoice;\r
 import org.apache.wicket.markup.html.form.Form;\r
 import org.apache.wicket.markup.html.form.TextField;\r
 import org.apache.wicket.model.CompoundPropertyModel;\r
 \r
+import com.gitblit.Constants.AccessRestrictionType;\r
 import com.gitblit.GitBlit;\r
 import com.gitblit.GitBlitException;\r
 import com.gitblit.wicket.AdminPage;\r
@@ -40,7 +43,7 @@ public class EditRepositoryPage extends BasePage {
                if (isCreate) {\r
                        super.setupPage("", getString("gb.newRepository"));\r
                } else {\r
-                       super.setupPage("", getString("gb.edit"));\r
+                       super.setupPage("", getString("gb.edit") + " " + repositoryModel.name);\r
                }\r
                CompoundPropertyModel<RepositoryModel> model = new CompoundPropertyModel<RepositoryModel>(repositoryModel);\r
                Form<RepositoryModel> form = new Form<RepositoryModel>("editForm", model) {\r
@@ -59,12 +62,12 @@ public class EditRepositoryPage extends BasePage {
                                setResponsePage(RepositoriesPage.class);\r
                        }\r
                };\r
-               \r
+\r
                // field names reflective match RepositoryModel fields\r
                form.add(new TextField<String>("name").setEnabled(isCreate));\r
                form.add(new TextField<String>("description"));\r
                form.add(new TextField<String>("owner"));\r
-               form.add(new TextField<String>("group"));\r
+               form.add(new DropDownChoice<AccessRestrictionType>("accessRestriction", Arrays.asList(AccessRestrictionType.values())));\r
                form.add(new CheckBox("useTickets"));\r
                form.add(new CheckBox("useDocs"));\r
                form.add(new CheckBox("showRemoteBranches"));\r
diff --git a/src/com/gitblit/wicket/pages/EditUserPage.html b/src/com/gitblit/wicket/pages/EditUserPage.html
new file mode 100644 (file)
index 0000000..57407d2
--- /dev/null
@@ -0,0 +1,29 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml"  \r
+      xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"  \r
+      xml:lang="en"  \r
+      lang="en"> \r
+\r
+<wicket:extend>\r
+<body onload="document.getElementById('username').focus();">\r
+       <!-- Push content down to preserve header image -->\r
+       <div style="padding-top:20px"></div>\r
+\r
+       <div style="text-align:center;" wicket:id="feedback">[Feedback Panel]</div>     \r
+\r
+       <!-- Repository Table -->\r
+       <form wicket:id="editForm">\r
+               <table class="plain">\r
+                       <tbody>\r
+                               <tr><th><wicket:message key="gb.name"></wicket:message></th><td class="edit"><input type="text" wicket:id="username" id="username" size="30" tabindex="1" /></td></tr>\r
+                               <tr><th><wicket:message key="gb.password"></wicket:message></th><td class="edit"><input type="password" wicket:id="password" size="30" tabindex="2" /></td></tr>\r
+                               <tr><th><wicket:message key="gb.confirmPassword"></wicket:message></th><td class="edit"><input type="password" wicket:id="confirmPassword" size="30" tabindex="3" /></td></tr>\r
+                               <tr><th><wicket:message key="gb.canAdmin"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="canAdmin" tabindex="6" /> &nbsp;<i>can administer Git:Blit server</i></td></tr>                              \r
+                               <tr><th style="vertical-align: top;"><wicket:message key="gb.repositories"></wicket:message></th><td style="padding:2px;"><span wicket:id="repositories"></span></td></tr>\r
+                               <tr><th></th><td class="editButton"><input type="submit" value="Save" wicket:message="value:gb.save" tabindex="7" /></td></tr>\r
+                       </tbody>\r
+               </table>\r
+       </form> \r
+</body>\r
+</wicket:extend>\r
+</html>
\ No newline at end of file
diff --git a/src/com/gitblit/wicket/pages/EditUserPage.java b/src/com/gitblit/wicket/pages/EditUserPage.java
new file mode 100644 (file)
index 0000000..84bf7bc
--- /dev/null
@@ -0,0 +1,97 @@
+package com.gitblit.wicket.pages;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+\r
+import org.apache.wicket.PageParameters;\r
+import org.apache.wicket.extensions.markup.html.form.palette.Palette;\r
+import org.apache.wicket.markup.html.form.CheckBox;\r
+import org.apache.wicket.markup.html.form.ChoiceRenderer;\r
+import org.apache.wicket.markup.html.form.Form;\r
+import org.apache.wicket.markup.html.form.PasswordTextField;\r
+import org.apache.wicket.markup.html.form.TextField;\r
+import org.apache.wicket.model.CompoundPropertyModel;\r
+import org.apache.wicket.model.Model;\r
+import org.apache.wicket.model.util.CollectionModel;\r
+import org.apache.wicket.model.util.ListModel;\r
+import org.eclipse.jetty.http.security.Credential.MD5;\r
+\r
+import com.gitblit.GitBlit;\r
+import com.gitblit.GitBlitException;\r
+import com.gitblit.wicket.AdminPage;\r
+import com.gitblit.wicket.BasePage;\r
+import com.gitblit.wicket.WicketUtils;\r
+import com.gitblit.wicket.models.User;\r
+\r
+@AdminPage\r
+public class EditUserPage extends BasePage {\r
+\r
+       private final boolean isCreate;\r
+\r
+       public EditUserPage() {\r
+               // create constructor\r
+               super();\r
+               isCreate = true;\r
+               setupPage(new User(""));\r
+       }\r
+\r
+       public EditUserPage(PageParameters params) {\r
+               // edit constructor\r
+               super(params);\r
+               isCreate = false;\r
+               String name = WicketUtils.getUsername(params);\r
+               User model = GitBlit.self().getUser(name);\r
+               setupPage(model);\r
+       }\r
+\r
+       protected void setupPage(final User userModel) {\r
+               if (isCreate) {\r
+                       super.setupPage("", getString("gb.newUser"));\r
+               } else {\r
+                       super.setupPage("", getString("gb.edit"));\r
+               }\r
+               final Model<String> confirmPassword = new Model<String>();\r
+               CompoundPropertyModel<User> model = new CompoundPropertyModel<User>(userModel);\r
+\r
+               List<String> repos = GitBlit.self().getRepositoryList();\r
+               repos.add(0, "*"); // all repositories wildcard\r
+               final Palette<String> repositories = new Palette<String>("repositories", new ListModel<String>(userModel.getRepositories()), new CollectionModel<String>(repos), new ChoiceRenderer<String>("", ""), 10, false);                \r
+               Form<User> form = new Form<User>("editForm", model) {\r
+\r
+                       private static final long serialVersionUID = 1L;\r
+\r
+                       @Override\r
+                       protected void onSubmit() {\r
+                               if (!userModel.getPassword().equals(confirmPassword.getObject())) {\r
+                                       error("Passwords do not match!");\r
+                                       return;\r
+                               }\r
+                               userModel.setPassword(MD5.digest(userModel.getPassword()));\r
+                               \r
+                               Iterator<String> selectedRepositories = repositories.getSelectedChoices();\r
+                               List<String> repos = new ArrayList<String>();\r
+                               while (selectedRepositories.hasNext()) {\r
+                                       repos.add(selectedRepositories.next());\r
+                               }\r
+                               userModel.setRepositories(repos);\r
+                               try {\r
+                                       GitBlit.self().editUserModel(userModel, isCreate);\r
+                               } catch (GitBlitException e) {\r
+                                       error(e.getMessage());\r
+                                       return;\r
+                               }\r
+                               setRedirect(true);\r
+                               setResponsePage(EditUserPage.class);\r
+                       }\r
+               };\r
+\r
+               // field names reflective match UserModel fields\r
+               form.add(new TextField<String>("username").setEnabled(isCreate));\r
+               form.add(new PasswordTextField("password"));\r
+               form.add(new PasswordTextField("confirmPassword", confirmPassword));\r
+               form.add(new CheckBox("canAdmin"));\r
+               form.add(repositories);\r
+               add(form);\r
+       }\r
+}\r
index 09de115c68a5a98587b118976ef3a477f89abe01..3016f648fac45497c9c34f2be4929279afc8ab77 100644 (file)
@@ -41,7 +41,7 @@
        <wicket:fragment wicket:id="adminLinks">\r
                <!-- page nav links --> \r
                <div class="page_nav">\r
-                       <a wicket:id="newRepository"><wicket:message key="gb.newRepository"></wicket:message></a> | <a wicket:id="newUser"><wicket:message key="gb.newUser"></wicket:message></a>\r
+                       <a wicket:id="newRepository"><wicket:message key="gb.newRepository"></wicket:message></a> | <a wicket:id="newUser"><wicket:message key="gb.newUser"></wicket:message></a> | <a wicket:id="editUsers"><wicket:message key="gb.editUsers"></wicket:message></a>\r
                </div>  \r
        </wicket:fragment>\r
        \r
index 111084c314426e24f31cbf7dde22f883d4fd1d13..9f3aa2a32b397559119efb9f761ebf0e0e954e96 100644 (file)
@@ -33,6 +33,7 @@ import com.gitblit.wicket.GitBlitWebSession;
 import com.gitblit.wicket.LinkPanel;\r
 import com.gitblit.wicket.WicketUtils;\r
 import com.gitblit.wicket.models.RepositoryModel;\r
+import com.gitblit.wicket.models.User;\r
 \r
 public class RepositoriesPage extends BasePage {\r
 \r
@@ -50,7 +51,8 @@ public class RepositoriesPage extends BasePage {
 \r
                Fragment adminLinks = new Fragment("adminPanel", "adminLinks", this);\r
                adminLinks.add(new BookmarkablePageLink<Void>("newRepository", EditRepositoryPage.class));\r
-               adminLinks.add(new BookmarkablePageLink<Void>("newUser", RepositoriesPage.class));\r
+               adminLinks.add(new BookmarkablePageLink<Void>("newUser", EditUserPage.class));\r
+               adminLinks.add(new BookmarkablePageLink<Void>("editUsers", RepositoriesPage.class));\r
                add(adminLinks.setVisible(showAdmin));\r
 \r
                // display an error message cached from a redirect\r
@@ -59,7 +61,7 @@ public class RepositoriesPage extends BasePage {
                        error(cachedMessage);\r
                        System.out.println("displayed message");\r
                }\r
-               \r
+\r
                // Load the markdown welcome message\r
                String messageSource = GitBlit.self().settings().getString(Keys.web.repositoriesMessage, "gitblit");\r
                String message = "";\r
@@ -97,7 +99,8 @@ public class RepositoriesPage extends BasePage {
                }\r
                add(repositoriesMessage);\r
 \r
-               List<RepositoryModel> rows = GitBlit.self().getRepositoryModels();\r
+               User user = GitBlitWebSession.get().getUser();\r
+               List<RepositoryModel> rows = GitBlit.self().getRepositoryModels(user);\r
                DataProvider dp = new DataProvider(rows);\r
                DataView<RepositoryModel> dataView = new DataView<RepositoryModel>("repository", dp) {\r
                        private static final long serialVersionUID = 1L;\r
@@ -113,27 +116,38 @@ public class RepositoriesPage extends BasePage {
                                } else {\r
                                        // New repository\r
                                        item.add(new Label("repositoryName", entry.name + "<span class='empty'>(empty)</span>").setEscapeModelStrings(false));\r
-                                       item.add(new Label("repositoryDescription", entry.description));                                        \r
+                                       item.add(new Label("repositoryDescription", entry.description));\r
                                }\r
-                               \r
+\r
                                if (entry.useTickets) {\r
                                        item.add(WicketUtils.newImage("ticketsIcon", "bug_16x16.png", getString("gb.tickets")));\r
                                } else {\r
-                                       item.add(WicketUtils.newClearPixel("ticketsIcon"));\r
+                                       item.add(WicketUtils.newBlankImage("ticketsIcon"));\r
                                }\r
-                               \r
+\r
                                if (entry.useDocs) {\r
                                        item.add(WicketUtils.newImage("docsIcon", "book_16x16.png", getString("gb.docs")));\r
                                } else {\r
-                                       item.add(WicketUtils.newClearPixel("docsIcon"));\r
+                                       item.add(WicketUtils.newBlankImage("docsIcon"));\r
                                }\r
-                               \r
-                               if (entry.useRestrictedAccess) {\r
-                                       item.add(WicketUtils.newImage("restrictedAccessIcon", "lock_16x16.png", getString("gb.restrictedAccess")));\r
-                               } else {\r
-                                       item.add(WicketUtils.newClearPixel("restrictedAccessIcon"));\r
+\r
+                               switch (entry.accessRestriction) {\r
+                               case NONE:\r
+                                       item.add(WicketUtils.newBlankImage("restrictedAccessIcon"));\r
+                                       break;\r
+                               case PUSH:\r
+                                       item.add(WicketUtils.newImage("restrictedAccessIcon", "lock_go_16x16.png", getString("gb.pushRestricted")));\r
+                                       break;\r
+                               case CLONE:\r
+                                       item.add(WicketUtils.newImage("restrictedAccessIcon", "lock_pull_16x16.png", getString("gb.cloneRestricted")));\r
+                                       break;\r
+                               case VIEW:\r
+                                       item.add(WicketUtils.newImage("restrictedAccessIcon", "shield_16x16.png", getString("gb.viewRestricted")));\r
+                                       break;\r
+                               default:\r
+                                       item.add(WicketUtils.newBlankImage("restrictedAccessIcon"));\r
                                }\r
-                               \r
+\r
                                item.add(new Label("repositoryOwner", entry.owner));\r
 \r
                                String lastChange = TimeUtils.timeAgo(entry.lastChange);\r
index 3d0a1cd51af28d8069e7b8e8ab95510018321ab2..13e761d482a3d4fdf7bea3f5e6f0a406c964c0d7 100644 (file)
@@ -444,10 +444,15 @@ table.plain {
        padding: 8px;\r
 }\r
 \r
-table.plain td.edit {\r
+table.plain td.edit {  \r
        padding: 3px;\r
 }\r
 \r
+table.plain td.editButton {\r
+       padding:0px;\r
+       padding-top: 10px;\r
+}\r
+\r
 table.plain td.edit input {\r
        margin: 0px;\r
        outline: 1px solid transparent;\r
@@ -518,6 +523,19 @@ table.repositories th.wicket_orderDown a, table.repositories th.wicket_orderUp a
        font-weight: bold;\r
 }\r
 \r
+table.palette { border:0;}\r
+table.palette td.header { \r
+       font-weight: bold; \r
+       background-color: #D2C3AF !important;\r
+       padding: 3px !important;        \r
+       border: 1px solid #808080 !important;\r
+       border-bottom: 0px solid !important;\r
+       border-radius: 3px 3px 0 0;\r
+}\r
+table.palette td.pane {\r
+       padding: 0px;   \r
+} \r
+\r
 tr th a { padding-right: 15px; background-position: right; background-repeat:no-repeat; }\r
 tr th.wicket_orderDown a {background-image: url(arrow_down.png); }\r
 tr th.wicket_orderUp a { background-image: url(arrow_up.png); }\r
@@ -619,22 +637,22 @@ span .tagRef a:hover, span .headRef a:hover, span .remoteRef a:hover, span .othe
 \r
 span .otherRef {\r
        background-color: #ffaaff;\r
-       border-color: #ffccff #ff00ee #ff00ee #ffccff;\r
+       border-color: #ff00ee;\r
 }\r
 \r
 span .remoteRef {\r
        background-color: #cAc2f5;\r
-       border-color: #ccccff #0033cc #0033cc #ccccff;\r
+       border-color: #6c6cbf;\r
 }\r
 \r
 span .tagRef {\r
        background-color: #ffffaa;\r
-       border-color: #ffcc00 #ffcc00 #ffcc00 #ffcc00;\r
+       border-color: #ffcc00;\r
 }\r
 \r
 span .headRef {\r
        background-color: #ccffcc;\r
-       border-color: #ccffcc #00cc33 #00cc33 #ccffcc;\r
+       border-color: #00cc33;\r
 }\r
 \r
 .feedbackPanelERROR {  \r
diff --git a/src/com/gitblit/wicket/resources/lock_go_16x16.png b/src/com/gitblit/wicket/resources/lock_go_16x16.png
new file mode 100644 (file)
index 0000000..63d4285
Binary files /dev/null and b/src/com/gitblit/wicket/resources/lock_go_16x16.png differ
diff --git a/src/com/gitblit/wicket/resources/lock_pull_16x16.png b/src/com/gitblit/wicket/resources/lock_pull_16x16.png
new file mode 100644 (file)
index 0000000..85c5c53
Binary files /dev/null and b/src/com/gitblit/wicket/resources/lock_pull_16x16.png differ
diff --git a/src/com/gitblit/wicket/resources/shield_16x16.png b/src/com/gitblit/wicket/resources/shield_16x16.png
new file mode 100644 (file)
index 0000000..4eb8031
Binary files /dev/null and b/src/com/gitblit/wicket/resources/shield_16x16.png differ
index eddad82585007750eb240aa788694f366c8bc8b6..0b22d827a5fd8a3d12cf033fb000ed06de7546ec 100644 (file)
@@ -1,2 +1,3 @@
-test: test,pull\r
-admin: admin,pull,push,admin\r
+#Wed May 11 21:30:28 EDT 2011\r
+admin=admin,\#admin\r
+test=test\r