From 366bec6ae90ef4adadb5df0e2e9232ba7b954f8e Mon Sep 17 00:00:00 2001 From: James Moger Date: Wed, 15 May 2013 15:55:19 -0400 Subject: Allow client apps to specify a minimum required access permission --- src/main/distrib/data/clientapps.json | 24 +++- src/main/java/com/gitblit/GitBlit.java | 3 +- .../com/gitblit/SparkleShareInviteServlet.java | 151 ++++----------------- .../com/gitblit/models/GitClientApplication.java | 2 + .../gitblit/wicket/panels/RepositoryUrlPanel.html | 52 +++---- .../gitblit/wicket/panels/RepositoryUrlPanel.java | 109 +++++++-------- 6 files changed, 128 insertions(+), 213 deletions(-) (limited to 'src') diff --git a/src/main/distrib/data/clientapps.json b/src/main/distrib/data/clientapps.json index 0c83d107..12d14b18 100644 --- a/src/main/distrib/data/clientapps.json +++ b/src/main/distrib/data/clientapps.json @@ -4,7 +4,7 @@ "title": "Git", "description": "a fast, open-source, distributed VCS", "legal": "released under the GPLv2 open source license", - "command": "git clone {0}", + "command": "git clone ${repoUrl}", "productUrl": "http://git-scm.com", "icon": "git-black_32x32.png", "isActive": true @@ -14,7 +14,7 @@ "title": "syntevo SmartGit/Hg\u2122", "description": "a Git client for Windows, Mac, & Linux", "legal": "\u00a9 2013 syntevo GmbH. All rights reserved.", - "cloneUrl": "smartgit://cloneRepo/{0}", + "cloneUrl": "smartgit://cloneRepo/${repoUrl}", "productUrl": "http://www.syntevo.com/smartgithg", "platforms": [ "windows", "macintosh", "linux" ], "icon": "smartgithg_32x32.png", @@ -25,7 +25,7 @@ "title": "Atlassian SourceTree\u2122", "description": "a free Git client for Windows or Mac", "legal": "\u00a9 2013 Atlassian. All rights reserved.", - "cloneUrl": "sourcetree://cloneRepo/{0}", + "cloneUrl": "sourcetree://cloneRepo/${repoUrl}", "productUrl": "http://sourcetreeapp.com", "platforms": [ "windows", "macintosh" ], "icon": "sourcetree_32x32.png", @@ -36,7 +36,7 @@ "title": "fournova Tower\u2122", "description": "a Git client for Mac", "legal": "\u00a9 2013 fournova Software GmbH. All rights reserved.", - "cloneUrl": "gittower://openRepo/{0}", + "cloneUrl": "gittower://openRepo/${repoUrl}", "productUrl": "http://www.git-tower.com", "platforms": [ "macintosh" ], "icon": "tower_32x32.png", @@ -47,7 +47,7 @@ "title": "GitHub\u2122 for Macintosh", "description": "a free Git client for Mac OS X", "legal": "\u00a9 2013 GitHub. All rights reserved.", - "cloneUrl": "github-mac://openRepo/{0}", + "cloneUrl": "github-mac://openRepo/${repoUrl}", "productUrl": "http://mac.github.com", "platforms": [ "macintosh" ], "isActive": false @@ -57,9 +57,21 @@ "title": "GitHub\u2122 for Windows", "description": "a free Git client for Windows", "legal": "\u00a9 2013 GitHub. All rights reserved.", - "cloneUrl": "github-windows://openRepo/{0}", + "cloneUrl": "github-windows://openRepo/${repoUrl}", "productUrl": "http://windows.github.com", "platforms": [ "windows" ], "isActive": false + }, + { + "name": "SparkleShare", + "title": "SparkleShare\u2122", + "description": "an open source collaboration and sharing tool", + "legal": "released under the GPLv3 open source license", + "cloneUrl": "sparkleshare://inviteRepo/${baseUrl}/sparkleshare/${repoUrl}.xml", + "productUrl": "http://sparkleshare.org", + "platforms": [ "windows", "macintosh", "linux" ], + "icon": "sparkleshare_32x32.png", + "minimumPermission" : "RW+", + "isActive": false } ] \ No newline at end of file diff --git a/src/main/java/com/gitblit/GitBlit.java b/src/main/java/com/gitblit/GitBlit.java index f017d218..2d3b7fd2 100644 --- a/src/main/java/com/gitblit/GitBlit.java +++ b/src/main/java/com/gitblit/GitBlit.java @@ -128,7 +128,6 @@ import com.gitblit.utils.X509Utils.X509Metadata; import com.gitblit.wicket.GitBlitWebSession; import com.gitblit.wicket.WicketUtils; import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import com.google.gson.JsonIOException; import com.google.gson.JsonSyntaxException; import com.google.gson.reflect.TypeToken; @@ -615,7 +614,7 @@ public class GitBlit implements ServletContextListener { Type type = new TypeToken>() { }.getType(); InputStreamReader reader = new InputStreamReader(is); - Gson gson = new GsonBuilder().create(); + Gson gson = JsonUtils.gson(); Collection links = gson.fromJson(reader, type); return links; } catch (JsonIOException e) { diff --git a/src/main/java/com/gitblit/SparkleShareInviteServlet.java b/src/main/java/com/gitblit/SparkleShareInviteServlet.java index 3cabb411..14d281a3 100644 --- a/src/main/java/com/gitblit/SparkleShareInviteServlet.java +++ b/src/main/java/com/gitblit/SparkleShareInviteServlet.java @@ -17,14 +17,12 @@ package com.gitblit; import java.io.IOException; import java.text.MessageFormat; -import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import com.gitblit.Constants.AccessRestrictionType; import com.gitblit.models.RepositoryModel; import com.gitblit.models.UserModel; import com.gitblit.utils.StringUtils; @@ -43,27 +41,6 @@ public class SparkleShareInviteServlet extends HttpServlet { super(); } - /** - * Returns an Sparkleshare invite url to this servlet for the repository. - * https://github.com/hbons/SparkleShare/wiki/Invites - * - * @param baseURL - * @param repository - * @param username - * @return an url - */ - public static String asLink(String baseURL, String repository, String username) { - if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') { - baseURL = baseURL.substring(0, baseURL.length() - 1); - } - String url = baseURL + Constants.SPARKLESHARE_INVITE_PATH - + ((StringUtils.isEmpty(username) ? "" : (username + "@"))) - + repository + ".xml"; - url = url.replace("https://", "sparkleshare://"); - url = url.replace("http://", "sparkleshare-unsafe://"); - return url; - } - @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { @@ -81,22 +58,22 @@ public class SparkleShareInviteServlet extends HttpServlet { java.io.IOException { // extract repo name from request - String path = request.getPathInfo(); - if (path != null && path.length() > 1) { - if (path.charAt(0) == '/') { - path = path.substring(1); - } - } + String repoUrl = request.getPathInfo().substring(1); + // trim trailing .xml - if (path.endsWith(".xml")) { - path = path.substring(0, path.length() - 4); + if (repoUrl.endsWith(".xml")) { + repoUrl = repoUrl.substring(0, repoUrl.length() - 4); } + String servletPath = Constants.GIT_PATH; + + int schemeIndex = repoUrl.indexOf("://") + 3; + String host = repoUrl.substring(0, repoUrl.indexOf('/', schemeIndex)); + String path = repoUrl.substring(repoUrl.indexOf(servletPath) + servletPath.length()); String username = null; - int fetch = path.indexOf('@'); - if (fetch > -1) { - username = path.substring(0, fetch); - path = path.substring(fetch + 1); + int fetchIndex = repoUrl.indexOf('@'); + if (fetchIndex > -1) { + username = repoUrl.substring(schemeIndex, fetchIndex); } UserModel user; if (StringUtils.isEmpty(username)) { @@ -109,102 +86,28 @@ public class SparkleShareInviteServlet extends HttpServlet { username = ""; } - // ensure that the requested repository exists and is sparkleshared + // ensure that the requested repository exists RepositoryModel model = GitBlit.self().getRepositoryModel(path); if (model == null) { response.setStatus(HttpServletResponse.SC_NOT_FOUND); response.getWriter().append(MessageFormat.format("Repository \"{0}\" not found!", path)); return; - } else if (!model.isSparkleshared()) { - response.setStatus(HttpServletResponse.SC_FORBIDDEN); - response.getWriter().append(MessageFormat.format("Repository \"{0}\" is not sparkleshared!", path)); - return; } - if (GitBlit.getBoolean(Keys.git.enableGitServlet, true) - || GitBlit.getInteger(Keys.git.daemonPort, 0) > 0) { - // Gitblit as server - // determine username for repository url - if (model.accessRestriction.exceeds(AccessRestrictionType.NONE)) { - if (!user.canRewindRef(model)) { - response.setStatus(HttpServletResponse.SC_FORBIDDEN); - response.getWriter().append(MessageFormat.format("\"{0}\" does not have RW+ permissions for {1}!", user.username, path)); - return; - } - } - - if (model.accessRestriction.exceeds(AccessRestrictionType.NONE)) { - username = user.username + "@"; - } else { - username = ""; - } - - String serverPort = ""; - if (request.getScheme().equals("https")) { - if (request.getServerPort() != 443) { - serverPort = ":" + request.getServerPort(); - } - } else if (request.getScheme().equals("http")) { - if (request.getServerPort() != 80) { - serverPort = ":" + request.getServerPort(); - } - } - - // assume http/https serving - String scheme = request.getScheme(); - String servletPath = Constants.GIT_PATH; - - // try to switch to git://, if git servlet disabled and repo has no restrictions - if (!GitBlit.getBoolean(Keys.git.enableGitServlet, true) - && (GitBlit.getInteger(Keys.git.daemonPort, 0) > 0) - && AccessRestrictionType.NONE == model.accessRestriction) { - scheme = "git"; - servletPath = "/"; - serverPort = GitBlit.getString(Keys.git.daemonPort, ""); - } - - // construct Sparkleshare invite - StringBuilder sb = new StringBuilder(); - sb.append("\n"); - sb.append("\n"); - sb.append(MessageFormat.format("
{0}://{1}{2}{3}{4}
\n", scheme, username, request.getServerName(), serverPort, request.getContextPath())); - sb.append(MessageFormat.format("{0}{1}\n", servletPath, model.name)); - if (GitBlit.getInteger(Keys.fanout.port, 0) > 0) { - // Gitblit is running it's own fanout service for pubsub notifications - sb.append(MessageFormat.format("tcp://{0}:{1}\n", request.getServerName(), GitBlit.getString(Keys.fanout.port, ""))); - } - sb.append("
\n"); - - // write invite to client - response.setContentType("application/xml"); - response.setContentLength(sb.length()); - response.getWriter().append(sb.toString()); - } else { - // Gitblit as viewer, repository access handled externally so - // assume RW+ permission - List others = GitBlit.getStrings(Keys.web.otherUrls); - if (others.size() == 0) { - return; - } - - String address = MessageFormat.format(others.get(0), "", username); - - StringBuilder sb = new StringBuilder(); - sb.append("\n"); - sb.append("\n"); - - sb.append(MessageFormat.format("
{0}
\n", address)); - sb.append(MessageFormat.format("{0}\n", model.name)); - if (GitBlit.getInteger(Keys.fanout.port, 0) > 0) { - // Gitblit is running it's own fanout service for pubsub notifications - sb.append(MessageFormat.format("tcp://{0}:{1}\n", request.getServerName(), GitBlit.getString(Keys.fanout.port, ""))); - } - sb.append("
\n"); - - // write invite to client - response.setContentType("application/xml"); - response.setContentLength(sb.length()); - response.getWriter().append(sb.toString()); + StringBuilder sb = new StringBuilder(); + sb.append("\n"); + sb.append("\n"); + sb.append(MessageFormat.format("
{0}
\n", host)); + sb.append(MessageFormat.format("{0}{1}\n", servletPath, model.name)); + if (GitBlit.getInteger(Keys.fanout.port, 0) > 0) { + // Gitblit is running it's own fanout service for pubsub notifications + sb.append(MessageFormat.format("tcp://{0}:{1}\n", request.getServerName(), GitBlit.getString(Keys.fanout.port, ""))); } + sb.append("
\n"); + + // write invite to client + response.setContentType("application/xml"); + response.setContentLength(sb.length()); + response.getWriter().append(sb.toString()); } } diff --git a/src/main/java/com/gitblit/models/GitClientApplication.java b/src/main/java/com/gitblit/models/GitClientApplication.java index fd530593..8225da4a 100644 --- a/src/main/java/com/gitblit/models/GitClientApplication.java +++ b/src/main/java/com/gitblit/models/GitClientApplication.java @@ -17,6 +17,7 @@ package com.gitblit.models; import java.io.Serializable; +import com.gitblit.Constants.AccessPermission; import com.gitblit.utils.ArrayUtils; import com.gitblit.utils.StringUtils; @@ -39,6 +40,7 @@ public class GitClientApplication implements Serializable { public String command; public String productUrl; public String[] platforms; + public AccessPermission minimumPermission; public boolean isActive; public boolean allowsPlatform(String p) { diff --git a/src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.html b/src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.html index 2663f88e..c3b13fa6 100644 --- a/src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.html +++ b/src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.html @@ -15,10 +15,10 @@
- +
[repository primary url] - +
[repository primary url permission]
@@ -27,32 +27,36 @@
- - - - - +
+ + + + + + + + URLs diff --git a/src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.java b/src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.java index 942f8d51..7f43d63c 100644 --- a/src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.java +++ b/src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.java @@ -38,12 +38,12 @@ import com.gitblit.Constants.AccessPermission; import com.gitblit.Constants.AccessRestrictionType; import com.gitblit.GitBlit; import com.gitblit.Keys; -import com.gitblit.SparkleShareInviteServlet; import com.gitblit.models.GitClientApplication; import com.gitblit.models.RepositoryModel; import com.gitblit.models.RepositoryUrl; import com.gitblit.models.UserModel; import com.gitblit.utils.StringUtils; +import com.gitblit.wicket.ExternalImage; import com.gitblit.wicket.GitBlitWebSession; import com.gitblit.wicket.WicketUtils; @@ -200,7 +200,7 @@ public class RepositoryUrlPanel extends BasePanel { return urlPanel; } - protected Fragment createApplicationMenus(String wicketId, UserModel user, final RepositoryModel repository, List repositoryUrls) { + protected Fragment createApplicationMenus(String wicketId, UserModel user, final RepositoryModel repository, final List repositoryUrls) { final List displayedApps = new ArrayList(); final String userAgent = ((WebClientInfo) GitBlitWebSession.get().getClientInfo()).getUserAgent(); @@ -210,14 +210,9 @@ public class RepositoryUrlPanel extends BasePanel { displayedApps.add(app); } } - - GitClientApplication sparkleshare = getSparkleShareAppMenu(user, repository); - if (sparkleshare != null) { - displayedApps.add(sparkleshare); - } } - final ListDataProvider urlsDp = new ListDataProvider(repositoryUrls); + final String baseURL = WicketUtils.getGitblitURL(RequestCycle.get().getRequest()); ListDataProvider displayedAppsDp = new ListDataProvider(displayedApps); DataView appMenus = new DataView("appMenus", displayedAppsDp) { private static final long serialVersionUID = 1L; @@ -225,58 +220,92 @@ public class RepositoryUrlPanel extends BasePanel { public void populateItem(final Item item) { final GitClientApplication clientApp = item.getModelObject(); + // filter the urls for the client app + List urls; + if (clientApp.minimumPermission == null) { + // client app does not specify minimum access permission + urls = repositoryUrls; + } else { + urls = new ArrayList(); + for (RepositoryUrl repoUrl : repositoryUrls) { + if (repoUrl.permission == null) { + // external permissions, assume it is satisfactory + urls.add(repoUrl); + } else if (repoUrl.permission.atLeast(clientApp.minimumPermission)) { + // repo url meets minimum permission requirement + urls.add(repoUrl); + } + } + } + + if (urls.size() == 0) { + // do not show this app menu because there are no urls + item.add(new Label("appMenu").setVisible(false)); + return; + } + + Fragment appMenu = new Fragment("appMenu", "appMenuFragment", this); + appMenu.setRenderBodyOnly(true); + item.add(appMenu); + // menu button - item.add(new Label("applicationName", clientApp.name)); + appMenu.add(new Label("applicationName", clientApp.name)); // application icon Component img; if (StringUtils.isEmpty(clientApp.icon)) { img = WicketUtils.newClearPixel("applicationIcon").setVisible(false); } else { - img = WicketUtils.newImage("applicationIcon", clientApp.icon); + if (clientApp.icon.contains("://")) { + // external image + img = new ExternalImage("applicationIcon", clientApp.icon); + } else { + // context image + img = WicketUtils.newImage("applicationIcon", clientApp.icon); + } } - item.add(img); + appMenu.add(img); // application menu title, may be a link if (StringUtils.isEmpty(clientApp.productUrl)) { - item.add(new Label("applicationTitle", clientApp.toString())); + appMenu.add(new Label("applicationTitle", clientApp.toString())); } else { - item.add(new LinkPanel("applicationTitle", null, clientApp.toString(), clientApp.productUrl, true)); + appMenu.add(new LinkPanel("applicationTitle", null, clientApp.toString(), clientApp.productUrl, true)); } // brief application description if (StringUtils.isEmpty(clientApp.description)) { - item.add(new Label("applicationDescription").setVisible(false)); + appMenu.add(new Label("applicationDescription").setVisible(false)); } else { - item.add(new Label("applicationDescription", clientApp.description)); + appMenu.add(new Label("applicationDescription", clientApp.description)); } // brief application legal info, copyright, license, etc if (StringUtils.isEmpty(clientApp.legal)) { - item.add(new Label("applicationLegal").setVisible(false)); + appMenu.add(new Label("applicationLegal").setVisible(false)); } else { - item.add(new Label("applicationLegal", clientApp.legal)); + appMenu.add(new Label("applicationLegal", clientApp.legal)); } // a nested repeater for all action items + ListDataProvider urlsDp = new ListDataProvider(urls); DataView actionItems = new DataView("actionItems", urlsDp) { private static final long serialVersionUID = 1L; public void populateItem(final Item repoLinkItem) { RepositoryUrl repoUrl = repoLinkItem.getModelObject(); - Fragment fragment = new Fragment("actionItem", "actionFragment", this); fragment.add(createPermissionBadge("permission", repoUrl)); if (!StringUtils.isEmpty(clientApp.cloneUrl)) { // custom registered url - String url = MessageFormat.format(clientApp.cloneUrl, repoUrl); + String url = substitute(clientApp.cloneUrl, repoUrl.url, baseURL); fragment.add(new LinkPanel("content", "applicationMenuItem", getString("gb.clone") + " " + repoUrl.url, url)); repoLinkItem.add(fragment); fragment.add(new Label("copyFunction").setVisible(false)); } else if (!StringUtils.isEmpty(clientApp.command)) { // command-line - String command = MessageFormat.format(clientApp.command, repoUrl); + String command = substitute(clientApp.command, repoUrl.url, baseURL); Label content = new Label("content", command); WicketUtils.setCssClass(content, "commandMenuItem"); fragment.add(content); @@ -286,7 +315,7 @@ public class RepositoryUrlPanel extends BasePanel { fragment.add(createCopyFragment(command)); } }}; - item.add(actionItems); + appMenu.add(actionItems); } }; @@ -295,42 +324,8 @@ public class RepositoryUrlPanel extends BasePanel { return applicationMenus; } - protected GitClientApplication getSparkleShareAppMenu(UserModel user, RepositoryModel repository) { - String url = null; - if (repository.isBare && repository.isSparkleshared()) { - String username = null; - if (UserModel.ANONYMOUS != user) { - username = user.username; - } - if (isGitblitServingRepositories()) { - // Gitblit as server - // ensure user can rewind - if (user.canRewindRef(repository)) { - String baseURL = WicketUtils.getGitblitURL(RequestCycle.get().getRequest()); - url = SparkleShareInviteServlet.asLink(baseURL, repository.name, username); - } - } else { - // Gitblit as viewer, assume RW+ permission - String baseURL = WicketUtils.getGitblitURL(RequestCycle.get().getRequest()); - url = SparkleShareInviteServlet.asLink(baseURL, repository.name, username); - } - } - - // sparkleshare invite url - if (!StringUtils.isEmpty(url)) { - GitClientApplication app = new GitClientApplication(); - app.name = "SparkleShare"; - app.title = "SparkleShare\u2122"; - app.description = "an open source collaboration and sharing tool"; - app.legal = "released under the GPLv3 open source license"; - app.cloneUrl = url; - app.platforms = new String [] { "windows", "macintosh", "linux" }; - app.productUrl = "http://sparkleshare.org"; - app.icon = "sparkleshare_32x32.png"; - app.isActive = true; - return app; - } - return null; + protected String substitute(String pattern, String repoUrl, String baseUrl) { + return pattern.replace("${repoUrl}", repoUrl).replace("${baseUrl}", baseUrl); } protected boolean isGitblitServingRepositories() { -- cgit v1.2.3