diff options
33 files changed, 794 insertions, 224 deletions
@@ -34,6 +34,8 @@ <classpathentry kind="lib" path="ext/unboundid-ldapsdk-2.3.0.jar" sourcepath="ext/src/unboundid-ldapsdk-2.3.0-sources.jar" /> <classpathentry kind="lib" path="ext/ivy-2.2.0.jar" sourcepath="ext/src/ivy-2.2.0-sources.jar" /> <classpathentry kind="lib" path="ext/jcalendar-1.3.2.jar" /> + <classpathentry kind="lib" path="ext/commons-compress-1.4.1.jar" sourcepath="ext/src/commons-compress-1.4.1-sources.jar" /> + <classpathentry kind="lib" path="ext/xz-1.0.jar" sourcepath="ext/src/xz-1.0-sources.jar" /> <classpathentry kind="lib" path="ext/junit-4.10.jar" sourcepath="ext/src/junit-4.10-sources.jar" /> <classpathentry kind="lib" path="ext/hamcrest-core-1.1.jar" /> <classpathentry kind="output" path="bin/classes" /> @@ -238,4 +238,20 @@ JCalendar GNU LESSER GENERAL PUBLIC LICENSE. (http://www.unboundid.com/products/ldap-sdk/docs/LICENSE-LGPLv2.1.txt)
http://www.toedter.com/en/jcalendar
-
\ No newline at end of file +
+---------------------------------------------------------------------------
+Commons-Compress
+---------------------------------------------------------------------------
+ Commons-Compress, released under the
+ Apache Software License, Version 2.0.
+
+ http://commons.apache.org/compress
+
+---------------------------------------------------------------------------
+XZ for Java
+---------------------------------------------------------------------------
+ XZ for Java, released under the
+ Public Domain
+
+ http://tukaani.org/xz/java.html
+
@@ -95,6 +95,7 @@ <property name="authority.zipfile" value="authority-${gb.version}.zip" />
<property name="gbapi.zipfile" value="gbapi-${gb.version}.zip" />
<property name="express.zipfile" value="express-${gb.version}.zip" />
+ <property name="distribution.pomfileTmplt" value="tmplt.pom.xml" />
</target>
@@ -495,6 +496,11 @@ <exclude name="com/gitblit/Launcher*.class" />
</fileset>
</copy>
+ <copy todir="${project.jar.dir}/static">
+ <fileset dir="${project.resources.dir}">
+ <exclude name="thumbs.db" />
+ </fileset>
+ </copy>
<!-- Build the JAR file -->
<jar basedir="${project.jar.dir}" destfile="${distribution.jarfile}" compress="true" />
@@ -767,6 +773,7 @@ <resource file="${basedir}/resources/settings_32x32.png" />
<resource file="${basedir}/resources/search-icon.png" />
<resource file="${basedir}/resources/mail_16x16.png" />
+ <resource file="${basedir}/resources/script_16x16.png" />
<resource file="${basedir}/resources/blank.png" />
<resource file="${basedir}/resources/bullet_green.png" />
<resource file="${basedir}/resources/bullet_orange.png" />
diff --git a/distrib/gitblit.properties b/distrib/gitblit.properties index 3419e005..a1ec3053 100644 --- a/distrib/gitblit.properties +++ b/distrib/gitblit.properties @@ -93,7 +93,7 @@ git.certificateUsernameOIDs = CN # Only serve/display bare repositories. # If there are non-bare repositories in git.repositoriesFolder and this setting -# is true, they will be excluded from the ui. +# is true, they will be excluded from the ui. # # SINCE 0.9.0 git.onlyAccessBareRepositories = false @@ -172,7 +172,7 @@ git.defaultGarbageCollectionThreshold = 500k # The default period, in days, between GCs for a repository. If the total filesize # of the loose object exceeds *git.garbageCollectionThreshold* or the repository's -# custom threshold, this period will be short-circuited. +# custom threshold, this period will be short-circuited. # # e.g. if a repository collects 100KB of loose objects every day with a 500KB # threshold and a period of 7 days, it will take 5 days for the loose objects to @@ -261,7 +261,7 @@ git.packedGitOpenFiles = 128 # typically has to deal with ~10-12 MiB XML files, so 15 m would be a reasonable # setting in that environment. Setting this too high may cause the JVM to run out # of heap space when handling very big binary files, such as device firmware or -# CD-ROM ISO images. Make sure to adjust your JVM heap accordingly. +# CD-ROM ISO images. Make sure to adjust your JVM heap accordingly. # # Default is 50 MiB on all platforms. # @@ -278,7 +278,7 @@ git.streamFileThreshold = 50m # a call to munmap() can be made by the JVM native code. # # In server applications (such as Gitblit) that need to access many pack files, -# setting this to true risks artificially running out of virtual address space, +# setting this to true risks artificially running out of virtual address space, # as the garbage collector cannot reclaim unused mapped spaces fast enough. # # Default on JGit is false. Although potentially slower, it yields much more @@ -316,7 +316,7 @@ groovy.grapeFolder = groovy/grape # to change. # # Script names are case-sensitive on case-sensitive file systems. You may omit -# the traditional ".groovy" from this list if your file extension is ".groovy" +# the traditional ".groovy" from this list if your file extension is ".groovy" # # NOTE: # These scripts are only executed when pushing to *Gitblit*, not to other Git @@ -336,7 +336,7 @@ groovy.preReceiveScripts = # You might trigger a continuous-integration build here or send a notification. # # Script names are case-sensitive on case-sensitive file systems. You may omit -# the traditional ".groovy" from this list if your file extension is ".groovy" +# the traditional ".groovy" from this list if your file extension is ".groovy" # # NOTE: # These scripts are only executed when pushing to *Gitblit*, not to other Git @@ -344,7 +344,7 @@ groovy.preReceiveScripts = # repositories. These are NOT repository-specific scripts! Within the script # you may customize the control-flow for a specific repository by checking the # *repository* variable. -# +# # SPACE-DELIMITED # CASE-SENSITIVE # SINCE 0.8.0 @@ -353,7 +353,7 @@ groovy.postReceiveScripts = # Repository custom fields for Groovy Hook mechanism # # List of key=label pairs of custom fields to prompt for in the Edit Repository -# page. These keys are stored in the repository's git config file in the +# page. These keys are stored in the repository's git config file in the # section [gitblit "customFields"]. Key names are alphanumeric only. These # fields are intended to be used for the Groovy hook mechanism where a script # can adjust it's execution based on the custom fields stored in the repository @@ -363,7 +363,7 @@ groovy.postReceiveScripts = # # SPACE-DELIMITED # SINCE 1.0.0 -groovy.customFields = +groovy.customFields = # # Authentication Settings @@ -411,13 +411,13 @@ realm.userService = users.conf # combined-md5 is the hash of username.toLowerCase()+password. # Default is md5. # -# SINCE 0.5.0 +# SINCE 0.5.0 realm.passwordStorage = md5 # Minimum valid length for a plain text password. # Default value is 5. Absolute minimum is 4. # -# SINCE 0.5.0 +# SINCE 0.5.0 realm.minPasswordLength = 5 # @@ -432,30 +432,30 @@ web.siteName = # repositories, create users, and edit repository metadata. # # If *web.authenticateAdminPages*=false, any user can execute the aforementioned -# functions. +# functions. # -# SINCE 0.5.0 +# SINCE 0.5.0 web.allowAdministration = true -# Allows rpc clients to list repositories and possibly manage or administer the +# Allows rpc clients to list repositories and possibly manage or administer the # Gitblit server, if the authenticated account has administrator permissions. # See *web.enableRpcManagement* and *web.enableRpcAdministration*. # -# SINCE 0.7.0 +# SINCE 0.7.0 web.enableRpcServlet = true # Allows rpc clients to manage repositories and users of the Gitblit instance, # if the authenticated account has administrator permissions. # Requires *web.enableRpcServlet=true*. # -# SINCE 0.7.0 +# SINCE 0.7.0 web.enableRpcManagement = false # Allows rpc clients to control the server settings and monitor the health of this # this Gitblit instance, if the authenticated account has administrator permissions. # Requires *web.enableRpcServlet=true* and *web.enableRpcManagement*. # -# SINCE 0.7.0 +# SINCE 0.7.0 web.enableRpcAdministration = false # Full path to a configurable robots.txt file. With this file you can control @@ -463,7 +463,7 @@ web.enableRpcAdministration = false # http://googlewebmastercentral.blogspot.com/2008/06/improving-on-robots-exclusion-protocol.html # # SINCE 1.0.0 -web.robots.txt = +web.robots.txt = # If true, the web ui layout will respond and adapt to the browser's dimensions. # if false, the web ui will use a 940px fixed-width layout. @@ -479,9 +479,22 @@ web.allowGravatar = true # Allow dynamic zip downloads. # -# SINCE 0.5.0 +# SINCE 0.5.0 web.allowZipDownloads = true +# If *web.allowZipDownloads=true* the following formats will be displayed for +# download compressed archive links: +# +# zip = standard .zip +# tar = standard tar format (preserves *nix permissions and symlinks) +# gz = gz-compressed tar +# xz = xz-compressed tar +# bzip2 = bzip2-compressed tar +# +# SPACE-DELIMITED +# SINCE 1.2.0 +web.compressedDownloads = zip gz + # Allow optional Lucene integration. Lucene indexing is an opt-in feature. # A repository may specify branches to index with Lucene instead of using Git # commit traversal. There are scenarios where you may want to completely disable @@ -510,7 +523,7 @@ web.syndicationEntries = 25 # Show the size of each repository on the repositories page. # This requires recursive traversal of each repository folder. This may be -# non-performant on some operating systems and/or filesystems. +# non-performant on some operating systems and/or filesystems. # # SINCE 0.5.2 web.showRepositorySizes = true @@ -519,13 +532,13 @@ web.showRepositorySizes = true # of the Repositories and Activity pages. Keep them very simple because you # are likely to run into encoding issues if they are too complex. # -# Use !!! to separate the filters +# Use !!! to separate the filters # # SINCE 0.8.0 web.customFilters = # Show federation registrations (without token) and the current pull status -# to non-administrator users. +# to non-administrator users. # # SINCE 0.6.0 web.showFederationRegistrations = false @@ -549,12 +562,12 @@ web.repositoriesMessage = gitblit # is always appended to the encoding list. If all encodings fail to cleanly # decode the blob content, UTF-8 will be used with the standard malformed # input/unmappable character replacement strings. -# +# # SPACE-DELIMITED # SINCE 1.0.0 web.blobEncodings = UTF-8 ISO-8859-1 -# Manually set the default timezone to be used by Gitblit for display in the +# Manually set the default timezone to be used by Gitblit for display in the # web ui. This value is independent of the JVM timezone. Specifying a blank # value will default to the JVM timezone. # e.g. America/New_York, US/Pacific, UTC, Europe/Berlin @@ -626,7 +639,7 @@ web.forwardSlashCharacter = / # # SPACE-DELIMITED # SINCE 0.5.0 -web.otherUrls = +web.otherUrls = # Choose how to present the repositories list. # grouped = group nested/subfolder repositories together (no sorting) @@ -642,8 +655,8 @@ web.repositoryListType = grouped # SINCE 0.5.0 web.repositoryRootGroupName = main -# Display the repository swatch color next to the repository name link in the -# repositories list. +# Display the repository swatch color next to the repository name link in the +# repositories list. # # SINCE 0.8.0 web.repositoryListSwatches = true @@ -667,7 +680,7 @@ web.showSearchTypeSelection = false # Generates a line graph of repository activity over time on the Summary page. # This uses the Google Charts API. # -# SINCE 0.5.0 +# SINCE 0.5.0 web.generateActivityGraph = true # The number of days to show on the activity page. @@ -719,7 +732,7 @@ web.markdownExtensions = md mkd markdown MD MKD # # SPACE-DELIMITED # SINCE 0.5.0 -web.imageExtensions = bmp jpg gif png +web.imageExtensions = bmp jpg gif png # Registered extensions for binary blobs # @@ -728,7 +741,7 @@ web.imageExtensions = bmp jpg gif png web.binaryExtensions = jar pdf tar.gz zip # Aggressive heap management will run the garbage collector on every generated -# page. This slows down page generation a little but improves heap consumption. +# page. This slows down page generation a little but improves heap consumption. # # SINCE 0.5.0 web.aggressiveHeapManagement = false @@ -788,13 +801,13 @@ mail.password = # from address for generated emails # # SINCE 0.6.0 -mail.fromAddress = +mail.fromAddress = # List of email addresses for the Gitblit administrators # # SPACE-DELIMITED # SINCE 0.6.0 -mail.adminAddresses = +mail.adminAddresses = # List of email addresses for sending push email notifications. # @@ -858,7 +871,7 @@ federation.proposalsFolder = proposals # SINCE 0.6.0 federation.defaultFrequency = 60 mins -# Federation Sets are named groups of repositories. The Federation Sets are +# Federation Sets are named groups of repositories. The Federation Sets are # available for selection in the repository settings page. You can assign a # repository to one or more sets and then distribute the token for the set. # This allows you to grant federation pull access to a subset of your available @@ -867,7 +880,7 @@ federation.defaultFrequency = 60 mins # SPACE-DELIMITED # CASE-SENSITIVE # SINCE 0.6.0 -federation.sets = +federation.sets = # Federation pull registrations # Registrations are read once, at startup. @@ -899,8 +912,8 @@ federation.sets = # clone from the origin until pushed to or otherwise manipulated. # # mergeAccounts: -# if true, remote accounts and their permissions are merged into your -# users.properties file +# if true, remote accounts and their permissions are merged into your +# users.properties file # # notifyOnError: # if true and the mail configuration is properly set, administrators will be @@ -919,8 +932,8 @@ federation.sets = #federation.example1.token = 6f3b8a24bf970f17289b234284c94f43eb42f0e4 #federation.example1.frequency = 120 mins #federation.example1.folder = -#federation.example1.bare = true -#federation.example1.mirror = true +#federation.example1.bare = true +#federation.example1.mirror = true #federation.example1.mergeAccounts = true # @@ -936,7 +949,7 @@ realm.ldap.server = ldap://localhost # Login username for LDAP searches. # If this value is unspecified, anonymous LDAP login will be used. -# +# # e.g. mydomain\\username # # SINCE 1.0.0 @@ -976,7 +989,7 @@ realm.ldap.accountBase = OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain # Filter criteria for LDAP users # -# Query pattern to use when searching for a user account. This may be any valid +# Query pattern to use when searching for a user account. This may be any valid # LDAP query expression, including the standard (&) and (|) operators. # # Variables may be injected via the ${variableName} syntax. @@ -989,14 +1002,14 @@ realm.ldap.accountPattern = (&(objectClass=person)(sAMAccountName=${username})) # Root node for all LDAP groups to be used as Gitblit Teams # # This is the root node from which subtree team searches will begin. -# If blank, Gitblit will search ALL nodes. +# If blank, Gitblit will search ALL nodes. # # SINCE 1.0.0 realm.ldap.groupBase = OU=Groups,OU=UserControl,OU=MyOrganization,DC=MyDomain # Filter criteria for LDAP groups # -# Query pattern to use when searching for a team. This may be any valid +# Query pattern to use when searching for a team. This may be any valid # LDAP query expression, including the standard (&) and (|) operators. # # Variables may be injected via the ${variableName} syntax. @@ -1005,7 +1018,7 @@ realm.ldap.groupBase = OU=Groups,OU=UserControl,OU=MyOrganization,DC=MyDomain # ${dn} - The Distinguished Name of the user logged in # # All attributes from the LDAP User record are available. For example, if a user -# has an attribute "fullName" set to "John", "(fn=${fullName})" will be +# has an attribute "fullName" set to "John", "(fn=${fullName})" will be # translated to "(fn=John)". # # SINCE 1.0.0 @@ -1027,7 +1040,7 @@ realm.ldap.admins = @Git_Admins # # This may be a single attribute, or a string of multiple attributes. Examples: # displayName - Uses the attribute 'displayName' on the user record -# ${personalTitle}. ${givenName} ${surname} - Will concatenate the 3 +# ${personalTitle}. ${givenName} ${surname} - Will concatenate the 3 # attributes together, with a '.' after personalTitle # # SINCE 1.0.0 @@ -1039,7 +1052,7 @@ realm.ldap.displayName = displayName # This may be a single attribute, or a string of multiple attributes. Examples: # email - Uses the attribute 'email' on the user record # ${givenName}.${surname}@gitblit.com -Will concatenate the 2 attributes -# together with a '.' and '@' creating something like first.last@gitblit.com +# together with a '.' and '@' creating something like first.last@gitblit.com # # SINCE 1.0.0 realm.ldap.email = email @@ -1078,7 +1091,7 @@ realm.redmine.url = http://example.com/redmine # Server Settings # -# The temporary folder to decompress the embedded gitblit webapp. +# The temporary folder to decompress the embedded gitblit webapp. # # SINCE 0.5.0 # RESTART REQUIRED diff --git a/docs/04_design.mkd b/docs/04_design.mkd index c2a73592..622e4a23 100644 --- a/docs/04_design.mkd +++ b/docs/04_design.mkd @@ -43,6 +43,8 @@ The following dependencies are automatically downloaded by Gitblit GO (or alread - [UnboundID](http://www.unboundid.com) (LGPL 2.1)
- [Ivy](http://ant.apache.org/ivy) (Apache 2.0)
- [JCalendar](http://www.toedter.com/en/jcalendar) (LGPL 2.1)
+- [Commons-Compress](http://commons.apache.org/compress) (Apache 2.0)
+- [XZ for Java](http://tukaani.org/xz/java.html) (Public Domain)
### Other Build Dependencies
- [Fancybox image viewer](http://fancybox.net) (MIT and GPL dual-licensed)
diff --git a/docs/04_releases.mkd b/docs/04_releases.mkd index 9704de0f..3f03160a 100644 --- a/docs/04_releases.mkd +++ b/docs/04_releases.mkd @@ -60,6 +60,8 @@ This is extreme and should be considered carefully since it affects every https - Added Gitblit Certificate Authority, an X509 certificate generation tool for Gitblit GO to encourage use of client certificate authentication.
- Added setting to control length of shortened commit ids
**New:** *web.shortCommitIdLength=8*
+- Added alternate compressed download formats: tar.gz, tar.xz, tar.bzip2 (issue 174)
+ **New:** *web.compressedDownloads = zip gz*
- Added simple project pages. A project is a subfolder off the *git.repositoriesFolder*.
- Added support for X-Forwarded-Context for Apache subdomain proxy configurations (issue 135)
- Delete branch feature (issue 121, Github/ajermakovics)
@@ -70,6 +72,7 @@ This is extreme and should be considered carefully since it affects every https #### changes
+- Access restricted servlets (e.g. DownloadZip, RSS, etc) will try to authenticate any Gitblit cookie found in the request before resorting to BASIC authentication.
- Added *groovy* and *scala* to *web.prettyPrintExtensions*
- Added short commit id column to log and history tables (issue 168)
- Teams can now specify the *admin*, *create*, and *fork* roles to simplify user administration
@@ -90,7 +93,8 @@ If *realm.ldap.maintainTeams==true* **AND** *realm.ldap.admins* is not empty, th - updated to Lucene 3.6.1
- updated to BouncyCastle 1.47
- added JCalendar 1.3.2
-
+- added Commons-Compress 1.4.1
+- added XZ for Java 1.0
<hr/>
### Older Releases
diff --git a/resources/gitblit.css b/resources/gitblit.css index e5363c85..811b08a5 100644 --- a/resources/gitblit.css +++ b/resources/gitblit.css @@ -984,11 +984,15 @@ td.date { white-space: nowrap;
}
-span.sha1, span.sha1 a, span.sha1 a span, .commit_message {
+span.sha1, span.sha1 a, span.sha1 a span, .commit_message, span.shortsha1 {
font-family: consolas, monospace;
font-size: 13px;
}
+span.shortsha1 {
+ font-size: 12px;
+}
+
td.mode {
text-align: right;
font-family: monospace;
diff --git a/resources/script_16x16.png b/resources/script_16x16.png Binary files differnew file mode 100644 index 00000000..ec93a22f --- /dev/null +++ b/resources/script_16x16.png diff --git a/src/com/gitblit/Constants.java b/src/com/gitblit/Constants.java index cd6b0709..4669c4c9 100644 --- a/src/com/gitblit/Constants.java +++ b/src/com/gitblit/Constants.java @@ -397,7 +397,15 @@ public class Constants { return ordinal() > s.ordinal();
}
}
-
+
+ public static enum AuthenticationType {
+ CREDENTIALS, COOKIE, CERTIFICATE;
+
+ public boolean isStandard() {
+ return ordinal() <= COOKIE.ordinal();
+ }
+ }
+
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Unused {
diff --git a/src/com/gitblit/DownloadZipServlet.java b/src/com/gitblit/DownloadZipServlet.java index 26559344..0feee879 100644 --- a/src/com/gitblit/DownloadZipServlet.java +++ b/src/com/gitblit/DownloadZipServlet.java @@ -29,6 +29,7 @@ import org.eclipse.jgit.revwalk.RevCommit; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.gitblit.utils.CompressionUtils;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.MarkdownUtils;
import com.gitblit.utils.StringUtils;
@@ -45,6 +46,25 @@ public class DownloadZipServlet extends HttpServlet { private static final long serialVersionUID = 1L;
private transient Logger logger = LoggerFactory.getLogger(DownloadZipServlet.class);
+
+ public static enum Format {
+ zip(".zip"), tar(".tar"), gz(".tar.gz"), xz(".tar.xz"), bzip2(".tar.bzip2");
+
+ public final String extension;
+
+ Format(String ext) {
+ this.extension = ext;
+ }
+
+ public static Format fromName(String name) {
+ for (Format format : values()) {
+ if (format.name().equalsIgnoreCase(name)) {
+ return format;
+ }
+ }
+ return zip;
+ }
+ }
public DownloadZipServlet() {
super();
@@ -57,15 +77,17 @@ public class DownloadZipServlet extends HttpServlet { * @param repository
* @param objectId
* @param path
+ * @param format
* @return an url
*/
- public static String asLink(String baseURL, String repository, String objectId, String path) {
+ public static String asLink(String baseURL, String repository, String objectId, String path, Format format) {
if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') {
baseURL = baseURL.substring(0, baseURL.length() - 1);
}
return baseURL + Constants.ZIP_PATH + "?r=" + repository
+ (path == null ? "" : ("&p=" + path))
- + (objectId == null ? "" : ("&h=" + objectId));
+ + (objectId == null ? "" : ("&h=" + objectId))
+ + (format == null ? "" : ("&format=" + format.name()));
}
/**
@@ -84,16 +106,22 @@ public class DownloadZipServlet extends HttpServlet { response.sendError(HttpServletResponse.SC_FORBIDDEN);
return;
}
-
+
+ Format format = Format.zip;
String repository = request.getParameter("r");
String basePath = request.getParameter("p");
String objectId = request.getParameter("h");
-
+ String f = request.getParameter("format");
+ if (!StringUtils.isEmpty(f)) {
+ format = Format.fromName(f);
+ }
+
try {
String name = repository;
if (name.indexOf('/') > -1) {
name = name.substring(name.lastIndexOf('/') + 1);
}
+ name = StringUtils.stripDotGit(name);
if (!StringUtils.isEmpty(basePath)) {
name += "-" + basePath.replace('/', '_');
@@ -122,15 +150,31 @@ public class DownloadZipServlet extends HttpServlet { String contentType = "application/octet-stream";
response.setContentType(contentType + "; charset=" + response.getCharacterEncoding());
- response.setHeader("Content-Disposition", "attachment; filename=\"" + name + ".zip"
- + "\"");
+ response.setHeader("Content-Disposition", "attachment; filename=\"" + name + format.extension + "\"");
response.setDateHeader("Last-Modified", date.getTime());
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
try {
- JGitUtils.zip(r, basePath, objectId, response.getOutputStream());
+ switch (format) {
+ case zip:
+ CompressionUtils.zip(r, basePath, objectId, response.getOutputStream());
+ break;
+ case tar:
+ CompressionUtils.tar(r, basePath, objectId, response.getOutputStream());
+ break;
+ case gz:
+ CompressionUtils.gz(r, basePath, objectId, response.getOutputStream());
+ break;
+ case xz:
+ CompressionUtils.xz(r, basePath, objectId, response.getOutputStream());
+ break;
+ case bzip2:
+ CompressionUtils.bzip2(r, basePath, objectId, response.getOutputStream());
+ break;
+ }
+
response.flushBuffer();
} catch (Throwable t) {
logger.error("Failed to write attachment to client", t);
diff --git a/src/com/gitblit/GitBlit.java b/src/com/gitblit/GitBlit.java index c05a9248..69135c49 100644 --- a/src/com/gitblit/GitBlit.java +++ b/src/com/gitblit/GitBlit.java @@ -58,6 +58,7 @@ import javax.servlet.ServletContextListener; import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
+import org.apache.wicket.RequestCycle;
import org.apache.wicket.protocol.http.WebResponse;
import org.apache.wicket.resource.ContextRelativeResource;
import org.apache.wicket.util.resource.ResourceStreamNotFoundException;
@@ -75,6 +76,7 @@ import org.slf4j.LoggerFactory; import com.gitblit.Constants.AccessPermission;
import com.gitblit.Constants.AccessRestrictionType;
+import com.gitblit.Constants.AuthenticationType;
import com.gitblit.Constants.AuthorizationControl;
import com.gitblit.Constants.FederationRequest;
import com.gitblit.Constants.FederationStrategy;
@@ -107,6 +109,8 @@ import com.gitblit.utils.MetricUtils; import com.gitblit.utils.ObjectCache;
import com.gitblit.utils.StringUtils;
import com.gitblit.utils.TimeUtils;
+import com.gitblit.utils.X509Utils.X509Metadata;
+import com.gitblit.wicket.GitBlitWebSession;
import com.gitblit.wicket.WicketUtils;
/**
@@ -537,7 +541,7 @@ public class GitBlit implements ServletContextListener { * @param cookies
* @return a user object or null
*/
- public UserModel authenticate(Cookie[] cookies) {
+ protected UserModel authenticate(Cookie[] cookies) {
if (userService == null) {
return null;
}
@@ -555,21 +559,51 @@ public class GitBlit implements ServletContextListener { }
/**
- * Authenticate a user based on HTTP request paramters.
- * This method is inteded to be used as fallback when other
- * means of authentication are failing (username / password or cookies).
+ * Authenticate a user based on HTTP request parameters.
+ *
+ * Authentication by X509Certificate is tried first and then by cookie.
+ *
* @param httpRequest
* @return a user object or null
*/
public UserModel authenticate(HttpServletRequest httpRequest) {
+ // try to authenticate by certificate
boolean checkValidity = settings.getBoolean(Keys.git.enforceCertificateValidity, true);
String [] oids = getStrings(Keys.git.certificateUsernameOIDs).toArray(new String[0]);
UserModel model = HttpUtils.getUserModelFromCertificate(httpRequest, checkValidity, oids);
if (model != null) {
- UserModel user = GitBlit.self().getUserModel(model.username);
- logger.info(MessageFormat.format("{0} authenticated by client certificate from {1}",
- user.username, httpRequest.getRemoteAddr()));
- return user;
+ // grab real user model and preserve certificate serial number
+ UserModel user = getUserModel(model.username);
+ if (user != null) {
+ RequestCycle requestCycle = RequestCycle.get();
+ if (requestCycle != null) {
+ // flag the Wicket session, if this is a Wicket request
+ GitBlitWebSession session = GitBlitWebSession.get();
+ session.authenticationType = AuthenticationType.CERTIFICATE;
+ }
+ X509Metadata metadata = HttpUtils.getCertificateMetadata(httpRequest);
+ logger.info(MessageFormat.format("{0} authenticated by client certificate {1} from {2}",
+ user.username, metadata.serialNumber, httpRequest.getRemoteAddr()));
+ return user;
+ }
+ }
+
+ // try to authenticate by cookie
+ Cookie[] cookies = httpRequest.getCookies();
+ if (allowCookieAuthentication() && cookies != null && cookies.length > 0) {
+ // Grab cookie from Browser Session
+ UserModel user = authenticate(cookies);
+ if (user != null) {
+ RequestCycle requestCycle = RequestCycle.get();
+ if (requestCycle != null) {
+ // flag the Wicket session, if this is a Wicket request
+ GitBlitWebSession session = GitBlitWebSession.get();
+ session.authenticationType = AuthenticationType.COOKIE;
+ }
+ logger.info(MessageFormat.format("{0} authenticated by cookie from {1}",
+ user.username, httpRequest.getRemoteAddr()));
+ return user;
+ }
}
return null;
}
diff --git a/src/com/gitblit/authority/GitblitAuthority.java b/src/com/gitblit/authority/GitblitAuthority.java index 5ee6af59..d97a8e39 100644 --- a/src/com/gitblit/authority/GitblitAuthority.java +++ b/src/com/gitblit/authority/GitblitAuthority.java @@ -67,6 +67,7 @@ import javax.swing.JPasswordField; import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTable;
+import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.RowFilter;
import javax.swing.SwingConstants;
@@ -92,6 +93,7 @@ import com.gitblit.client.HeaderPanel; import com.gitblit.client.Translation;
import com.gitblit.models.UserModel;
import com.gitblit.utils.ArrayUtils;
+import com.gitblit.utils.FileUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.utils.TimeUtils;
import com.gitblit.utils.X509Utils;
@@ -343,6 +345,7 @@ public class GitblitAuthority extends JFrame implements X509Log { X509Metadata metadata = new X509Metadata("localhost", caKeystorePassword);
setMetadataDefaults(metadata);
+ metadata.notAfter = new Date(System.currentTimeMillis() + 10*TimeUtils.ONEYEAR);
X509Utils.prepareX509Infrastructure(metadata, folder, this);
return true;
}
@@ -681,6 +684,23 @@ public class GitblitAuthority extends JFrame implements X509Log { }
});
+ JButton logButton = new JButton(new ImageIcon(getClass().getResource("/script_16x16.png")));
+ logButton.setFocusable(false);
+ logButton.setToolTipText(Translation.get("gb.log"));
+ logButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ File log = new File(folder, X509Utils.CERTS + File.separator + "log.txt");
+ if (log.exists()) {
+ String content = FileUtils.readContent(log, "\n");
+ JTextArea textarea = new JTextArea(content);
+ JScrollPane scrollPane = new JScrollPane(textarea);
+ scrollPane.setPreferredSize(new Dimension(700, 400));
+ JOptionPane.showMessageDialog(GitblitAuthority.this, scrollPane, log.getAbsolutePath(), JOptionPane.INFORMATION_MESSAGE);
+ }
+ }
+ });
+
final JTextField filterTextfield = new JTextField(15);
filterTextfield.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
@@ -697,6 +717,7 @@ public class GitblitAuthority extends JFrame implements X509Log { buttonControls.add(certificateDefaultsButton);
buttonControls.add(newSSLCertificate);
buttonControls.add(emailBundle);
+ buttonControls.add(logButton);
JPanel userControls = new JPanel(new FlowLayout(FlowLayout.RIGHT, Utils.MARGIN, Utils.MARGIN));
userControls.add(new JLabel(Translation.get("gb.filter")));
diff --git a/src/com/gitblit/build/Build.java b/src/com/gitblit/build/Build.java index 4c5fa195..e8e6b45d 100644 --- a/src/com/gitblit/build/Build.java +++ b/src/com/gitblit/build/Build.java @@ -106,6 +106,8 @@ public class Build { downloadFromApache(MavenObject.UNBOUND_ID, BuildType.RUNTIME);
downloadFromApache(MavenObject.IVY, BuildType.RUNTIME);
downloadFromApache(MavenObject.JCALENDAR, BuildType.RUNTIME);
+ downloadFromApache(MavenObject.COMMONS_COMPRESS, BuildType.RUNTIME);
+ downloadFromApache(MavenObject.XZ, BuildType.RUNTIME);
downloadFromEclipse(MavenObject.JGIT, BuildType.RUNTIME);
downloadFromEclipse(MavenObject.JGIT_HTTP, BuildType.RUNTIME);
@@ -143,7 +145,9 @@ public class Build { downloadFromApache(MavenObject.UNBOUND_ID, BuildType.COMPILETIME);
downloadFromApache(MavenObject.IVY, BuildType.COMPILETIME);
downloadFromApache(MavenObject.JCALENDAR, BuildType.COMPILETIME);
-
+ downloadFromApache(MavenObject.COMMONS_COMPRESS, BuildType.COMPILETIME);
+ downloadFromApache(MavenObject.XZ, BuildType.COMPILETIME);
+
downloadFromEclipse(MavenObject.JGIT, BuildType.COMPILETIME);
downloadFromEclipse(MavenObject.JGIT_HTTP, BuildType.COMPILETIME);
@@ -779,7 +783,19 @@ public class Build { 127000, 0, 0,
"323a672aeacb5f5f4461be3b7f7d9d3e4bda80d4",
null, "");
-
+
+ public static final MavenObject COMMONS_COMPRESS = new MavenObject(
+ "commons-compress", "org/apache/commons", "commons-compress", "1.4.1",
+ 242000, 265000, 0,
+ "b02e84a993d88568417536240e970c4b809126fd",
+ "277d39267403965a7a192474794a29bac6760a25", "");
+
+ public static final MavenObject XZ = new MavenObject(
+ "xz", "org/tukaani", "xz", "1.0",
+ 95000, 120000, 0,
+ "ecff5cb8b1189514c9d1d8d68eb77ac372e000c9",
+ "f95e32a5d2dd8da643c4419814415b9704312993", "");
+
public final String name;
public final String group;
public final String artifact;
diff --git a/src/com/gitblit/utils/CompressionUtils.java b/src/com/gitblit/utils/CompressionUtils.java new file mode 100644 index 00000000..7b0d0471 --- /dev/null +++ b/src/com/gitblit/utils/CompressionUtils.java @@ -0,0 +1,315 @@ +/*
+ * 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.utils;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
+import org.apache.commons.compress.compressors.CompressorException;
+import org.apache.commons.compress.compressors.CompressorStreamFactory;
+import org.apache.commons.compress.utils.IOUtils;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevBlob;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.filter.PathFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Collection of static methods for retrieving information from a repository.
+ *
+ * @author James Moger
+ *
+ */
+public class CompressionUtils {
+
+ static final Logger LOGGER = LoggerFactory.getLogger(CompressionUtils.class);
+
+ /**
+ * Log an error message and exception.
+ *
+ * @param t
+ * @param repository
+ * if repository is not null it MUST be the {0} parameter in the
+ * pattern.
+ * @param pattern
+ * @param objects
+ */
+ private static void error(Throwable t, Repository repository, String pattern, Object... objects) {
+ List<Object> parameters = new ArrayList<Object>();
+ if (objects != null && objects.length > 0) {
+ for (Object o : objects) {
+ parameters.add(o);
+ }
+ }
+ if (repository != null) {
+ parameters.add(0, repository.getDirectory().getAbsolutePath());
+ }
+ LOGGER.error(MessageFormat.format(pattern, parameters.toArray()), t);
+ }
+
+ /**
+ * Zips the contents of the tree at the (optionally) specified revision and
+ * the (optionally) specified basepath to the supplied outputstream.
+ *
+ * @param repository
+ * @param basePath
+ * if unspecified, entire repository is assumed.
+ * @param objectId
+ * if unspecified, HEAD is assumed.
+ * @param os
+ * @return true if repository was successfully zipped to supplied output
+ * stream
+ */
+ public static boolean zip(Repository repository, String basePath, String objectId,
+ OutputStream os) {
+ RevCommit commit = JGitUtils.getCommit(repository, objectId);
+ if (commit == null) {
+ return false;
+ }
+ boolean success = false;
+ RevWalk rw = new RevWalk(repository);
+ TreeWalk tw = new TreeWalk(repository);
+ try {
+ tw.addTree(commit.getTree());
+ ZipOutputStream zos = new ZipOutputStream(os);
+ zos.setComment("Generated by Gitblit");
+ if (!StringUtils.isEmpty(basePath)) {
+ PathFilter f = PathFilter.create(basePath);
+ tw.setFilter(f);
+ }
+ tw.setRecursive(true);
+ while (tw.next()) {
+ if (tw.getFileMode(0) == FileMode.GITLINK) {
+ continue;
+ }
+ ZipEntry entry = new ZipEntry(tw.getPathString());
+ entry.setSize(tw.getObjectReader().getObjectSize(tw.getObjectId(0),
+ Constants.OBJ_BLOB));
+ entry.setComment(commit.getName());
+ zos.putNextEntry(entry);
+
+ ObjectId entid = tw.getObjectId(0);
+ FileMode entmode = tw.getFileMode(0);
+ RevBlob blob = (RevBlob) rw.lookupAny(entid, entmode.getObjectType());
+ rw.parseBody(blob);
+
+ ObjectLoader ldr = repository.open(blob.getId(), Constants.OBJ_BLOB);
+ byte[] tmp = new byte[4096];
+ InputStream in = ldr.openStream();
+ int n;
+ while ((n = in.read(tmp)) > 0) {
+ zos.write(tmp, 0, n);
+ }
+ in.close();
+ }
+ zos.finish();
+ success = true;
+ } catch (IOException e) {
+ error(e, repository, "{0} failed to zip files from commit {1}", commit.getName());
+ } finally {
+ tw.release();
+ rw.dispose();
+ }
+ return success;
+ }
+
+ /**
+ * tar the contents of the tree at the (optionally) specified revision and
+ * the (optionally) specified basepath to the supplied outputstream.
+ *
+ * @param repository
+ * @param basePath
+ * if unspecified, entire repository is assumed.
+ * @param objectId
+ * if unspecified, HEAD is assumed.
+ * @param os
+ * @return true if repository was successfully zipped to supplied output
+ * stream
+ */
+ public static boolean tar(Repository repository, String basePath, String objectId,
+ OutputStream os) {
+ return tar(null, repository, basePath, objectId, os);
+ }
+
+ /**
+ * tar.gz the contents of the tree at the (optionally) specified revision and
+ * the (optionally) specified basepath to the supplied outputstream.
+ *
+ * @param repository
+ * @param basePath
+ * if unspecified, entire repository is assumed.
+ * @param objectId
+ * if unspecified, HEAD is assumed.
+ * @param os
+ * @return true if repository was successfully zipped to supplied output
+ * stream
+ */
+ public static boolean gz(Repository repository, String basePath, String objectId,
+ OutputStream os) {
+ return tar(CompressorStreamFactory.GZIP, repository, basePath, objectId, os);
+ }
+
+ /**
+ * tar.xz the contents of the tree at the (optionally) specified revision and
+ * the (optionally) specified basepath to the supplied outputstream.
+ *
+ * @param repository
+ * @param basePath
+ * if unspecified, entire repository is assumed.
+ * @param objectId
+ * if unspecified, HEAD is assumed.
+ * @param os
+ * @return true if repository was successfully zipped to supplied output
+ * stream
+ */
+ public static boolean xz(Repository repository, String basePath, String objectId,
+ OutputStream os) {
+ return tar(CompressorStreamFactory.XZ, repository, basePath, objectId, os);
+ }
+
+ /**
+ * tar.bzip2 the contents of the tree at the (optionally) specified revision and
+ * the (optionally) specified basepath to the supplied outputstream.
+ *
+ * @param repository
+ * @param basePath
+ * if unspecified, entire repository is assumed.
+ * @param objectId
+ * if unspecified, HEAD is assumed.
+ * @param os
+ * @return true if repository was successfully zipped to supplied output
+ * stream
+ */
+ public static boolean bzip2(Repository repository, String basePath, String objectId,
+ OutputStream os) {
+
+ return tar(CompressorStreamFactory.BZIP2, repository, basePath, objectId, os);
+ }
+
+ /**
+ * Compresses/archives the contents of the tree at the (optionally)
+ * specified revision and the (optionally) specified basepath to the
+ * supplied outputstream.
+ *
+ * @param algorithm
+ * compression algorithm for tar (optional)
+ * @param repository
+ * @param basePath
+ * if unspecified, entire repository is assumed.
+ * @param objectId
+ * if unspecified, HEAD is assumed.
+ * @param os
+ * @return true if repository was successfully zipped to supplied output
+ * stream
+ */
+ private static boolean tar(String algorithm, Repository repository, String basePath, String objectId,
+ OutputStream os) {
+ RevCommit commit = JGitUtils.getCommit(repository, objectId);
+ if (commit == null) {
+ return false;
+ }
+
+ OutputStream cos = os;
+ if (!StringUtils.isEmpty(algorithm)) {
+ try {
+ cos = new CompressorStreamFactory().createCompressorOutputStream(algorithm, os);
+ } catch (CompressorException e1) {
+ error(e1, repository, "{0} failed to open {1} stream", algorithm);
+ }
+ }
+ boolean success = false;
+ RevWalk rw = new RevWalk(repository);
+ TreeWalk tw = new TreeWalk(repository);
+ try {
+ tw.addTree(commit.getTree());
+ TarArchiveOutputStream tos = new TarArchiveOutputStream(cos);
+ tos.setAddPaxHeadersForNonAsciiNames(true);
+ tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);
+ if (!StringUtils.isEmpty(basePath)) {
+ PathFilter f = PathFilter.create(basePath);
+ tw.setFilter(f);
+ }
+ tw.setRecursive(true);
+ while (tw.next()) {
+ FileMode mode = tw.getFileMode(0);
+ if (mode == FileMode.GITLINK) {
+ continue;
+ }
+ ObjectId id = tw.getObjectId(0);
+
+ // new entry
+ TarArchiveEntry entry = new TarArchiveEntry(tw.getPathString());
+ entry.setSize(tw.getObjectReader().getObjectSize(id, Constants.OBJ_BLOB));
+
+ if (FileMode.SYMLINK.equals(mode)) {
+ // symlink
+ entry.setMode(mode.getBits());
+
+ // read the symlink target
+ ByteArrayOutputStream bs = new ByteArrayOutputStream();
+ RevBlob blob = (RevBlob) rw.lookupAny(id, mode.getObjectType());
+ rw.parseBody(blob);
+ ObjectLoader ldr = repository.open(blob.getId(), Constants.OBJ_BLOB);
+ IOUtils.copy(ldr.openStream(), bs);
+ entry.setLinkName(bs.toString("UTF-8"));
+ } else {
+ // regular file or executable file
+ entry.setMode(mode.getBits());
+ }
+ entry.setModTime(commit.getAuthorIdent().getWhen());
+
+ tos.putArchiveEntry(entry);
+
+ if (!FileMode.SYMLINK.equals(mode)) {
+ // write the blob
+ RevBlob blob = (RevBlob) rw.lookupAny(id, mode.getObjectType());
+ rw.parseBody(blob);
+ ObjectLoader ldr = repository.open(blob.getId(), Constants.OBJ_BLOB);
+ IOUtils.copy(ldr.openStream(), tos);
+ }
+
+ // close entry
+ tos.closeArchiveEntry();
+ }
+ tos.finish();
+ tos.close();
+ cos.close();
+ success = true;
+ } catch (IOException e) {
+ error(e, repository, "{0} failed to {1} stream files from commit {2}", algorithm, commit.getName());
+ } finally {
+ tw.release();
+ rw.dispose();
+ }
+ return success;
+ }
+}
diff --git a/src/com/gitblit/utils/HttpUtils.java b/src/com/gitblit/utils/HttpUtils.java index 68a35066..b40088c8 100644 --- a/src/com/gitblit/utils/HttpUtils.java +++ b/src/com/gitblit/utils/HttpUtils.java @@ -20,14 +20,13 @@ import java.security.cert.CertificateNotYetValidException; import java.security.cert.X509Certificate;
import java.text.MessageFormat;
import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.LoggerFactory;
import com.gitblit.models.UserModel;
+import com.gitblit.utils.X509Utils.X509Metadata;
/**
* Collection of utility methods for http requests.
@@ -145,21 +144,11 @@ public class HttpUtils { * @return
*/
public static UserModel getUserModelFromCertificate(X509Certificate cert, String... usernameOIDs) {
- UserModel user = new UserModel(null);
- user.isAuthenticated = false;
+ X509Metadata metadata = X509Utils.getMetadata(cert);
- // manually split DN into OID components
- // this is instead of parsing with LdapName which:
- // (1) I don't trust the order of values
- // (2) it filters out values like EMAILADDRESS
- String dn = cert.getSubjectDN().getName();
- Map<String, String> oids = new HashMap<String, String>();
- for (String kvp : dn.split(",")) {
- String [] val = kvp.trim().split("=");
- String oid = val[0].toUpperCase().trim();
- String data = val[1].trim();
- oids.put(oid, data);
- }
+ UserModel user = new UserModel(metadata.commonName);
+ user.emailAddress = metadata.emailAddress;
+ user.isAuthenticated = false;
if (usernameOIDs == null || usernameOIDs.length == 0) {
// use default usename<->CN mapping
@@ -169,24 +158,23 @@ public class HttpUtils { // determine username from OID fingerprint
StringBuilder an = new StringBuilder();
for (String oid : usernameOIDs) {
- String val = getOIDValue(oid.toUpperCase(), oids);
+ String val = metadata.getOID(oid.toUpperCase(), null);
if (val != null) {
an.append(val).append(' ');
}
}
- user.username = an.toString().trim();
-
- // extract email address, if available
- user.emailAddress = getOIDValue("E", oids);
- if (user.emailAddress == null) {
- user.emailAddress = getOIDValue("EMAILADDRESS", oids);
- }
+ user.username = an.toString().trim();
return user;
}
- private static String getOIDValue(String oid, Map<String, String> oids) {
- if (oids.containsKey(oid)) {
- return oids.get(oid);
+ public static X509Metadata getCertificateMetadata(HttpServletRequest httpRequest) {
+ if (httpRequest.getAttribute("javax.servlet.request.X509Certificate") != null) {
+ X509Certificate[] certChain = (X509Certificate[]) httpRequest
+ .getAttribute("javax.servlet.request.X509Certificate");
+ if (certChain != null) {
+ X509Certificate cert = certChain[0];
+ return X509Utils.getMetadata(cert);
+ }
}
return null;
}
diff --git a/src/com/gitblit/utils/JGitUtils.java b/src/com/gitblit/utils/JGitUtils.java index bc44f00f..beaa27da 100644 --- a/src/com/gitblit/utils/JGitUtils.java +++ b/src/com/gitblit/utils/JGitUtils.java @@ -19,7 +19,6 @@ import java.io.ByteArrayOutputStream; import java.io.File;
import java.io.IOException;
import java.io.InputStream;
-import java.io.OutputStream;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -30,8 +29,6 @@ import java.util.List; import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Pattern;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipOutputStream;
import org.eclipse.jgit.api.CloneCommand;
import org.eclipse.jgit.api.FetchCommand;
@@ -1531,7 +1528,7 @@ public class JGitUtils { try {
// search for the branch in local heads
for (RefModel ref : JGitUtils.getLocalBranches(repository, false, -1)) {
- if (ref.displayName.endsWith(name)) {
+ if (ref.reference.getName().endsWith(name)) {
branch = ref;
break;
}
@@ -1540,7 +1537,7 @@ public class JGitUtils { // search for the branch in remote heads
if (branch == null) {
for (RefModel ref : JGitUtils.getRemoteBranches(repository, false, -1)) {
- if (ref.displayName.endsWith(name)) {
+ if (ref.reference.getName().endsWith(name)) {
branch = ref;
break;
}
@@ -1723,70 +1720,4 @@ public class JGitUtils { }
return success;
}
-
- /**
- * Zips the contents of the tree at the (optionally) specified revision and
- * the (optionally) specified basepath to the supplied outputstream.
- *
- * @param repository
- * @param basePath
- * if unspecified, entire repository is assumed.
- * @param objectId
- * if unspecified, HEAD is assumed.
- * @param os
- * @return true if repository was successfully zipped to supplied output
- * stream
- */
- public static boolean zip(Repository repository, String basePath, String objectId,
- OutputStream os) {
- RevCommit commit = getCommit(repository, objectId);
- if (commit == null) {
- return false;
- }
- boolean success = false;
- RevWalk rw = new RevWalk(repository);
- TreeWalk tw = new TreeWalk(repository);
- try {
- tw.addTree(commit.getTree());
- ZipOutputStream zos = new ZipOutputStream(os);
- zos.setComment("Generated by Gitblit");
- if (!StringUtils.isEmpty(basePath)) {
- PathFilter f = PathFilter.create(basePath);
- tw.setFilter(f);
- }
- tw.setRecursive(true);
- while (tw.next()) {
- if (tw.getFileMode(0) == FileMode.GITLINK) {
- continue;
- }
- ZipEntry entry = new ZipEntry(tw.getPathString());
- entry.setSize(tw.getObjectReader().getObjectSize(tw.getObjectId(0),
- Constants.OBJ_BLOB));
- entry.setComment(commit.getName());
- zos.putNextEntry(entry);
-
- ObjectId entid = tw.getObjectId(0);
- FileMode entmode = tw.getFileMode(0);
- RevBlob blob = (RevBlob) rw.lookupAny(entid, entmode.getObjectType());
- rw.parseBody(blob);
-
- ObjectLoader ldr = repository.open(blob.getId(), Constants.OBJ_BLOB);
- byte[] tmp = new byte[4096];
- InputStream in = ldr.openStream();
- int n;
- while ((n = in.read(tmp)) > 0) {
- zos.write(tmp, 0, n);
- }
- in.close();
- }
- zos.finish();
- success = true;
- } catch (IOException e) {
- error(e, repository, "{0} failed to zip files from commit {1}", commit.getName());
- } finally {
- tw.release();
- rw.dispose();
- }
- return success;
- }
}
diff --git a/src/com/gitblit/utils/X509Utils.java b/src/com/gitblit/utils/X509Utils.java index 24afb8d9..cfad9ec0 100644 --- a/src/com/gitblit/utils/X509Utils.java +++ b/src/com/gitblit/utils/X509Utils.java @@ -111,6 +111,12 @@ public class X509Utils { private static final String BC = org.bouncycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME;
+ private static final int KEY_LENGTH = 2048;
+
+ private static final String KEY_ALGORITHM = "RSA";
+
+ private static final String SIGNING_ALGORITHM = "SHA512withRSA";
+
public static final boolean unlimitedStrength;
private static final Logger logger = LoggerFactory.getLogger(X509Utils.class);
@@ -183,6 +189,9 @@ public class X509Utils { // displayname of user for README in bundle
public String userDisplayname;
+ // serialnumber of generated or read certificate
+ public String serialNumber;
+
public X509Metadata(String cn, String pwd) {
if (StringUtils.isEmpty(cn)) {
throw new RuntimeException("Common name required!");
@@ -472,8 +481,8 @@ public class X509Utils { * @throws Exception
*/
private static KeyPair newKeyPair() throws Exception {
- KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", BC);
- kpGen.initialize(2048, new SecureRandom());
+ KeyPairGenerator kpGen = KeyPairGenerator.getInstance(KEY_ALGORITHM, BC);
+ kpGen.initialize(KEY_LENGTH, new SecureRandom());
return kpGen.generateKeyPair();
}
@@ -547,7 +556,7 @@ public class X509Utils { certBuilder.addExtension(X509Extension.basicConstraints, false, new BasicConstraints(false));
certBuilder.addExtension(X509Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(caCert.getPublicKey()));
- ContentSigner caSigner = new JcaContentSignerBuilder("SHA256WithRSAEncryption")
+ ContentSigner caSigner = new JcaContentSignerBuilder(SIGNING_ALGORITHM)
.setProvider(BC).build(caPrivateKey);
X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC)
.getCertificate(certBuilder.build(caSigner));
@@ -562,6 +571,10 @@ public class X509Utils { saveKeyStore(targetStoreFile, serverStore, sslMetadata.password);
x509log.log(MessageFormat.format("New SSL certificate {0,number,0} [{1}]", cert.getSerialNumber(), cert.getSubjectDN().getName()));
+
+ // update serial number in metadata object
+ sslMetadata.serialNumber = cert.getSerialNumber().toString();
+
return cert;
} catch (Throwable t) {
throw new RuntimeException("Failed to generate SSL certificate!", t);
@@ -582,7 +595,7 @@ public class X509Utils { try {
KeyPair caPair = newKeyPair();
- ContentSigner caSigner = new JcaContentSignerBuilder("SHA1WithRSA").setProvider(BC).build(caPair.getPrivate());
+ ContentSigner caSigner = new JcaContentSignerBuilder(SIGNING_ALGORITHM).setProvider(BC).build(caPair.getPrivate());
// clone metadata
X509Metadata caMetadata = metadata.clone(CA_CN, metadata.password);
@@ -623,6 +636,9 @@ public class X509Utils { x509log.log(MessageFormat.format("New CA certificate {0,number,0} [{1}]", cert.getSerialNumber(), cert.getIssuerDN().getName()));
+ // update serial number in metadata object
+ caMetadata.serialNumber = cert.getSerialNumber().toString();
+
return cert;
} catch (Throwable t) {
throw new RuntimeException("Failed to generate Gitblit CA certificate!", t);
@@ -649,7 +665,7 @@ public class X509Utils { X509v2CRLBuilder crlBuilder = new X509v2CRLBuilder(issuerDN, new Date());
// build and sign CRL with CA private key
- ContentSigner signer = new JcaContentSignerBuilder("SHA1WithRSA").setProvider(BC).build(caPrivateKey);
+ ContentSigner signer = new JcaContentSignerBuilder(SIGNING_ALGORITHM).setProvider(BC).build(caPrivateKey);
X509CRLHolder crl = crlBuilder.build(signer);
File tmpFile = new File(caRevocationList.getParentFile(), Long.toHexString(System.currentTimeMillis()) + ".tmp");
@@ -743,6 +759,17 @@ public class X509Utils { zos.write(FileUtils.readContent(pemFile));
zos.closeEntry();
}
+
+ // include user's public certificate
+ zos.putNextEntry(new ZipEntry(clientMetadata.commonName + ".cer"));
+ zos.write(cert.getEncoded());
+ zos.closeEntry();
+
+ // include CA public certificate
+ zos.putNextEntry(new ZipEntry("ca.cer"));
+ zos.write(caCert.getEncoded());
+ zos.closeEntry();
+
if (readme != null) {
zos.putNextEntry(new ZipEntry("README.TXT"));
zos.write(readme.getBytes("UTF-8"));
@@ -799,7 +826,7 @@ public class X509Utils { certBuilder.addExtension(X509Extension.subjectAlternativeName, false, subjectAltName);
}
- ContentSigner signer = new JcaContentSignerBuilder("SHA1WithRSA").setProvider(BC).build(caPrivateKey);
+ ContentSigner signer = new JcaContentSignerBuilder(SIGNING_ALGORITHM).setProvider(BC).build(caPrivateKey);
X509Certificate userCert = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certBuilder.build(signer));
PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier)pair.getPrivate();
@@ -852,6 +879,9 @@ public class X509Utils { // save certificate after successfully creating the key stores
saveCertificate(userCert, certFile);
+ // update serial number in metadata object
+ clientMetadata.serialNumber = userCert.getSerialNumber().toString();
+
return userCert;
} catch (Throwable t) {
throw new RuntimeException("Failed to generate client certificate!", t);
@@ -1065,4 +1095,30 @@ public class X509Utils { }
return false;
}
+
+ public static X509Metadata getMetadata(X509Certificate cert) {
+ // manually split DN into OID components
+ // this is instead of parsing with LdapName which:
+ // (1) I don't trust the order of values
+ // (2) it filters out values like EMAILADDRESS
+ String dn = cert.getSubjectDN().getName();
+ Map<String, String> oids = new HashMap<String, String>();
+ for (String kvp : dn.split(",")) {
+ String [] val = kvp.trim().split("=");
+ String oid = val[0].toUpperCase().trim();
+ String data = val[1].trim();
+ oids.put(oid, data);
+ }
+
+ X509Metadata metadata = new X509Metadata(oids.get("CN"), "whocares");
+ metadata.oids.putAll(oids);
+ metadata.serialNumber = cert.getSerialNumber().toString();
+ metadata.notAfter = cert.getNotAfter();
+ metadata.notBefore = cert.getNotBefore();
+ metadata.emailAddress = metadata.getOID("E", null);
+ if (metadata.emailAddress == null) {
+ metadata.emailAddress = metadata.getOID("EMAILADDRESS", null);
+ }
+ return metadata;
+ }
}
diff --git a/src/com/gitblit/wicket/GitBlitWebSession.java b/src/com/gitblit/wicket/GitBlitWebSession.java index 015d97ad..5195a1fd 100644 --- a/src/com/gitblit/wicket/GitBlitWebSession.java +++ b/src/com/gitblit/wicket/GitBlitWebSession.java @@ -29,6 +29,7 @@ import org.apache.wicket.protocol.http.WebRequestCycle; import org.apache.wicket.protocol.http.WebSession;
import org.apache.wicket.protocol.http.request.WebClientInfo;
+import com.gitblit.Constants.AuthenticationType;
import com.gitblit.models.UserModel;
public final class GitBlitWebSession extends WebSession {
@@ -45,9 +46,12 @@ public final class GitBlitWebSession extends WebSession { private AtomicBoolean isForking;
+ public AuthenticationType authenticationType;
+
public GitBlitWebSession(Request request) {
super(request);
isForking = new AtomicBoolean();
+ authenticationType = AuthenticationType.CREDENTIALS;
}
public void invalidate() {
diff --git a/src/com/gitblit/wicket/pages/BasePage.java b/src/com/gitblit/wicket/pages/BasePage.java index 05640ad0..d1ee2710 100644 --- a/src/com/gitblit/wicket/pages/BasePage.java +++ b/src/com/gitblit/wicket/pages/BasePage.java @@ -29,7 +29,6 @@ import java.util.Set; import java.util.TimeZone;
import java.util.regex.Pattern;
-import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import org.apache.wicket.Application;
@@ -131,22 +130,18 @@ public abstract class BasePage extends WebPage { }
private void login() {
- // try to authenticate by servlet request
- UserModel user = GitBlit.self().authenticate(((WebRequest) getRequestCycle().getRequest()).getHttpServletRequest());
-
- if (user == null) {
- // try to authenticate by cookie
- Cookie[] cookies = ((WebRequest) getRequestCycle().getRequest()).getCookies();
- if (GitBlit.self().allowCookieAuthentication() && cookies != null && cookies.length > 0) {
- // Grab cookie from Browser Session
- user = GitBlit.self().authenticate(cookies);
- }
+ GitBlitWebSession session = GitBlitWebSession.get();
+ if (session.isLoggedIn() && !session.isSessionInvalidated()) {
+ // already have a session
+ return;
}
+
+ // try to authenticate by servlet request
+ HttpServletRequest httpRequest = ((WebRequest) getRequestCycle().getRequest()).getHttpServletRequest();
+ UserModel user = GitBlit.self().authenticate(httpRequest);
// Login the user
if (user != null) {
- // Set the user into the session
- GitBlitWebSession session = GitBlitWebSession.get();
// issue 62: fix session fixation vulnerability
session.replaceSession();
session.setUser(user);
@@ -431,14 +426,19 @@ public abstract class BasePage extends WebPage { public UserFragment(String id, String markupId, MarkupContainer markupProvider) {
super(id, markupId, markupProvider);
- if (GitBlitWebSession.get().isLoggedIn()) {
+ GitBlitWebSession session = GitBlitWebSession.get();
+ if (session.isLoggedIn()) {
+ UserModel user = session.getUser();
+ boolean editCredentials = GitBlit.self().supportsCredentialChanges();
+ boolean standardLogin = session.authenticationType.isStandard();
+
// username, logout, and change password
- add(new Label("username", GitBlitWebSession.get().getUser().getDisplayName() + ":"));
+ add(new Label("username", user.getDisplayName() + ":"));
add(new LinkPanel("loginLink", null, markupProvider.getString("gb.logout"),
- LogoutPage.class));
- boolean editCredentials = GitBlit.self().supportsCredentialChanges();
+ LogoutPage.class).setVisible(standardLogin));
+
// quick and dirty hack for showing a separator
- add(new Label("separator", "|").setVisible(editCredentials));
+ add(new Label("separator", "|").setVisible(standardLogin && editCredentials));
add(new BookmarkablePageLink<Void>("changePasswordLink",
ChangePasswordPage.class).setVisible(editCredentials));
} else {
diff --git a/src/com/gitblit/wicket/pages/CommitPage.html b/src/com/gitblit/wicket/pages/CommitPage.html index 20e6b607..79a038c9 100644 --- a/src/com/gitblit/wicket/pages/CommitPage.html +++ b/src/com/gitblit/wicket/pages/CommitPage.html @@ -30,7 +30,7 @@ <tr class="hidden-phone"><th><wicket:message key="gb.tree">tree</wicket:message></th>
<td><span class="sha1" wicket:id="commitTree">[commit tree]</span>
<span class="link">
- <a wicket:id="treeLink"><wicket:message key="gb.tree"></wicket:message></a> | <a wicket:id="zipLink"><wicket:message key="gb.zip"></wicket:message></a>
+ <a wicket:id="treeLink"><wicket:message key="gb.tree"></wicket:message></a> | <span wicket:id="compressedLinks"></span>
</span>
</td></tr>
<tr class="hidden-phone"><th valign="top"><wicket:message key="gb.parent">parent</wicket:message></th>
diff --git a/src/com/gitblit/wicket/pages/CommitPage.java b/src/com/gitblit/wicket/pages/CommitPage.java index 7bc6b41d..b2a8112b 100644 --- a/src/com/gitblit/wicket/pages/CommitPage.java +++ b/src/com/gitblit/wicket/pages/CommitPage.java @@ -22,7 +22,6 @@ import java.util.List; import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
-import org.apache.wicket.markup.html.link.ExternalLink;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.DataView;
import org.apache.wicket.markup.repeater.data.ListDataProvider;
@@ -32,16 +31,15 @@ import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit;
import com.gitblit.Constants;
-import com.gitblit.DownloadZipServlet;
import com.gitblit.GitBlit;
-import com.gitblit.Keys;
import com.gitblit.models.GitNote;
-import com.gitblit.models.SubmoduleModel;
import com.gitblit.models.PathModel.PathChangeModel;
+import com.gitblit.models.SubmoduleModel;
import com.gitblit.utils.JGitUtils;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.panels.CommitHeaderPanel;
import com.gitblit.wicket.panels.CommitLegendPanel;
+import com.gitblit.wicket.panels.CompressedDownloadsPanel;
import com.gitblit.wicket.panels.GravatarImage;
import com.gitblit.wicket.panels.LinkPanel;
import com.gitblit.wicket.panels.RefsPanel;
@@ -95,8 +93,8 @@ public class CommitPage extends RepositoryPage { newCommitParameter()));
add(new BookmarkablePageLink<Void>("treeLink", TreePage.class, newCommitParameter()));
final String baseUrl = WicketUtils.getGitblitURL(getRequest());
- add(new ExternalLink("zipLink", DownloadZipServlet.asLink(baseUrl, repositoryName,
- objectId, null)).setVisible(GitBlit.getBoolean(Keys.web.allowZipDownloads, true)));
+
+ add(new CompressedDownloadsPanel("compressedLinks", baseUrl, repositoryName, objectId, null));
// Parent Commits
ListDataProvider<String> parentsDp = new ListDataProvider<String>(parents);
diff --git a/src/com/gitblit/wicket/pages/RepositoryPage.java b/src/com/gitblit/wicket/pages/RepositoryPage.java index c90e3530..346edc3e 100644 --- a/src/com/gitblit/wicket/pages/RepositoryPage.java +++ b/src/com/gitblit/wicket/pages/RepositoryPage.java @@ -115,7 +115,7 @@ public abstract class RepositoryPage extends BasePage { boolean canAccess = user.hasBranchPermission(repositoryName,
branch.reference.getName());
if (!canAccess) {
- error(getString("gb.accessDeined"), true);
+ error(getString("gb.accessDenied"), true);
}
}
}
diff --git a/src/com/gitblit/wicket/pages/TreePage.html b/src/com/gitblit/wicket/pages/TreePage.html index 0047ff0d..b7e55ed6 100644 --- a/src/com/gitblit/wicket/pages/TreePage.html +++ b/src/com/gitblit/wicket/pages/TreePage.html @@ -9,7 +9,7 @@ <!-- blob nav links -->
<div class="page_nav2">
- <a wicket:id="historyLink"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="headLink"><wicket:message key="gb.head"></wicket:message></a> | <a wicket:id="zipLink"><wicket:message key="gb.zip"></wicket:message></a>
+ <a wicket:id="historyLink"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="headLink"><wicket:message key="gb.head"></wicket:message></a> | <span wicket:id="compressedLinks"></span>
</div>
<!-- commit header -->
@@ -32,14 +32,14 @@ <!-- submodule links -->
<wicket:fragment wicket:id="submoduleLinks">
<span class="link">
- <a wicket:id="view"><wicket:message key="gb.view"></wicket:message></a> | <span class="hidden-phone"><a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a> | </span><a wicket:id="history"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="zip"><wicket:message key="gb.zip"></wicket:message></a>
+ <a wicket:id="view"><wicket:message key="gb.view"></wicket:message></a> | <span class="hidden-phone"><a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a> | </span><a wicket:id="history"><wicket:message key="gb.history"></wicket:message></a> | <span wicket:id="compressedLinks"></span>
</span>
</wicket:fragment>
<!-- tree links -->
<wicket:fragment wicket:id="treeLinks">
<span class="link">
- <span class="hidden-phone"><a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a> | </span><a wicket:id="history"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="zip"><wicket:message key="gb.zip"></wicket:message></a>
+ <span class="hidden-phone"><a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a> | </span><a wicket:id="history"><wicket:message key="gb.history"></wicket:message></a> | <span wicket:id="compressedLinks"></span>
</span>
</wicket:fragment>
diff --git a/src/com/gitblit/wicket/pages/TreePage.java b/src/com/gitblit/wicket/pages/TreePage.java index 973634b7..345814f4 100644 --- a/src/com/gitblit/wicket/pages/TreePage.java +++ b/src/com/gitblit/wicket/pages/TreePage.java @@ -20,7 +20,6 @@ import java.util.List; import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
-import org.apache.wicket.markup.html.link.ExternalLink;
import org.apache.wicket.markup.html.panel.Fragment;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.DataView;
@@ -30,15 +29,13 @@ import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
-import com.gitblit.DownloadZipServlet;
-import com.gitblit.GitBlit;
-import com.gitblit.Keys;
import com.gitblit.models.PathModel;
import com.gitblit.models.SubmoduleModel;
import com.gitblit.utils.ByteFormat;
import com.gitblit.utils.JGitUtils;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.panels.CommitHeaderPanel;
+import com.gitblit.wicket.panels.CompressedDownloadsPanel;
import com.gitblit.wicket.panels.LinkPanel;
import com.gitblit.wicket.panels.PathBreadcrumbsPanel;
@@ -58,9 +55,8 @@ public class TreePage extends RepositoryPage { WicketUtils.newPathParameter(repositoryName, objectId, path)));
add(new BookmarkablePageLink<Void>("headLink", TreePage.class,
WicketUtils.newPathParameter(repositoryName, Constants.HEAD, path)));
- add(new ExternalLink("zipLink", DownloadZipServlet.asLink(getRequest()
- .getRelativePathPrefixToContextRoot(), repositoryName, objectId, path))
- .setVisible(GitBlit.getBoolean(Keys.web.allowZipDownloads, true)));
+ add(new CompressedDownloadsPanel("compressedLinks", getRequest()
+ .getRelativePathPrefixToContextRoot(), repositoryName, objectId, path));
add(new CommitHeaderPanel("commitHeader", repositoryName, commit));
@@ -114,10 +110,10 @@ public class TreePage extends RepositoryPage { entry.path)));
links.add(new BookmarkablePageLink<Void>("history", HistoryPage.class,
WicketUtils.newPathParameter(repositoryName, entry.commitId,
- entry.path)));
- links.add(new ExternalLink("zip", DownloadZipServlet.asLink(baseUrl,
- repositoryName, objectId, entry.path)).setVisible(GitBlit
- .getBoolean(Keys.web.allowZipDownloads, true)));
+ entry.path)));
+ links.add(new CompressedDownloadsPanel("compressedLinks", baseUrl,
+ repositoryName, objectId, entry.path));
+
item.add(links);
} else if (entry.isSubmodule()) {
// submodule
@@ -143,9 +139,8 @@ public class TreePage extends RepositoryPage { links.add(new BookmarkablePageLink<Void>("history", HistoryPage.class,
WicketUtils.newPathParameter(submodulePath, submoduleId,
"")).setEnabled(hasSubmodule));
- links.add(new ExternalLink("zip", DownloadZipServlet.asLink(baseUrl,
- submodulePath, submoduleId, "")).setVisible(GitBlit
- .getBoolean(Keys.web.allowZipDownloads, true)).setEnabled(hasSubmodule));
+ links.add(new CompressedDownloadsPanel("compressedLinks", baseUrl,
+ submodulePath, submoduleId, "").setEnabled(hasSubmodule));
item.add(links);
} else {
// blob link
diff --git a/src/com/gitblit/wicket/panels/BranchesPanel.java b/src/com/gitblit/wicket/panels/BranchesPanel.java index cfe9f5fe..26fedc1e 100644 --- a/src/com/gitblit/wicket/panels/BranchesPanel.java +++ b/src/com/gitblit/wicket/panels/BranchesPanel.java @@ -36,8 +36,10 @@ import com.gitblit.GitBlit; import com.gitblit.SyndicationServlet;
import com.gitblit.models.RefModel;
import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.UserModel;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.GitBlitWebSession;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.pages.BranchesPage;
import com.gitblit.wicket.pages.CommitPage;
@@ -58,9 +60,24 @@ public class BranchesPanel extends BasePanel { // branches
List<RefModel> branches = new ArrayList<RefModel>();
- branches.addAll(JGitUtils.getLocalBranches(r, false, maxCount));
+ UserModel user = GitBlitWebSession.get().getUser();
+ if (user == null) {
+ user = UserModel.ANONYMOUS;
+ }
+
+ List<RefModel> localBranches = JGitUtils.getLocalBranches(r, false, -1);
+ for (RefModel refModel : localBranches) {
+ if (user.hasBranchPermission(model.name, refModel.reference.getName())) {
+ branches.add(refModel);
+ }
+ }
if (model.showRemoteBranches) {
- branches.addAll(JGitUtils.getRemoteBranches(r, false, maxCount));
+ List<RefModel> remoteBranches = JGitUtils.getRemoteBranches(r, false, -1);
+ for (RefModel refModel : remoteBranches) {
+ if (user.hasBranchPermission(model.name, refModel.reference.getName())) {
+ branches.add(refModel);
+ }
+ }
}
Collections.sort(branches);
Collections.reverse(branches);
diff --git a/src/com/gitblit/wicket/panels/CompressedDownloadsPanel.html b/src/com/gitblit/wicket/panels/CompressedDownloadsPanel.html new file mode 100644 index 00000000..7123d0a7 --- /dev/null +++ b/src/com/gitblit/wicket/panels/CompressedDownloadsPanel.html @@ -0,0 +1,12 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<wicket:panel>
+ <span wicket:id="compressedLinks">
+ <span wicket:id="linkSep">|</span><span wicket:id="compressedLink">ref</span>
+ </span>
+</wicket:panel>
+</html>
\ No newline at end of file diff --git a/src/com/gitblit/wicket/panels/CompressedDownloadsPanel.java b/src/com/gitblit/wicket/panels/CompressedDownloadsPanel.java new file mode 100644 index 00000000..b22c7587 --- /dev/null +++ b/src/com/gitblit/wicket/panels/CompressedDownloadsPanel.java @@ -0,0 +1,77 @@ +/*
+ * 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.wicket.panels;
+
+import java.util.List;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.markup.repeater.data.DataView;
+import org.apache.wicket.markup.repeater.data.ListDataProvider;
+
+import com.gitblit.DownloadZipServlet;
+import com.gitblit.DownloadZipServlet.Format;
+import com.gitblit.GitBlit;
+import com.gitblit.Keys;
+
+public class CompressedDownloadsPanel extends Panel {
+
+ private static final long serialVersionUID = 1L;
+
+ public CompressedDownloadsPanel(String id, final String baseUrl, final String repositoryName, final String objectId, final String path) {
+ super(id);
+
+ List<String> types = GitBlit.getStrings(Keys.web.compressedDownloads);
+ if (types.isEmpty()) {
+ types.add(Format.zip.name());
+ types.add(Format.gz.name());
+ }
+
+ ListDataProvider<String> refsDp = new ListDataProvider<String>(types);
+ DataView<String> refsView = new DataView<String>("compressedLinks", refsDp) {
+ private static final long serialVersionUID = 1L;
+ int counter;
+
+ @Override
+ protected void onBeforeRender() {
+ super.onBeforeRender();
+ counter = 0;
+ }
+
+ @Override
+ public void populateItem(final Item<String> item) {
+ String compressionType = item.getModelObject();
+ Format format = Format.fromName(compressionType);
+
+ String href = DownloadZipServlet.asLink(baseUrl, repositoryName,
+ objectId, path, format);
+ Component c = new LinkPanel("compressedLink", null, format.name(), href);
+ item.add(c);
+ Label lb = new Label("linkSep", "|");
+ lb.setVisible(counter > 0);
+ lb.setRenderBodyOnly(true);
+ item.add(lb.setEscapeModelStrings(false));
+ item.setRenderBodyOnly(true);
+ counter++;
+ }
+ };
+ add(refsView);
+
+ setVisible(GitBlit.getBoolean(Keys.web.allowZipDownloads, true));
+ }
+}
\ No newline at end of file diff --git a/src/com/gitblit/wicket/panels/HistoryPanel.html b/src/com/gitblit/wicket/panels/HistoryPanel.html index fed08076..811eeed1 100644 --- a/src/com/gitblit/wicket/panels/HistoryPanel.html +++ b/src/com/gitblit/wicket/panels/HistoryPanel.html @@ -20,7 +20,7 @@ <td class="icon"><img wicket:id="commitIcon" /></td>
<td class="hidden-phone author"><span wicket:id="commitAuthor">[commit author]</span></td>
<td class="message"><table class="nestedTable"><tr><td><span style="vertical-align:middle;" wicket:id="commitShortMessage">[commit short message]</span></td><td><div style="text-align:right;" wicket:id="commitRefs">[commit refs]</div></td></tr></table></td>
- <td class="hidden-phone hidden-tablet"><span class="link" wicket:id="hashLabel">[hash label]</span><span wicket:id="hashLink">[hash link]</span></td>
+ <td class="hidden-phone hidden-tablet rightAlign"><span class="link" wicket:id="hashLabel">[hash label]</span><span wicket:id="hashLink">[hash link]</span></td>
<td class="hidden-phone rightAlign">
<span wicket:id="historyLinks">[history links]</span>
</td>
diff --git a/src/com/gitblit/wicket/panels/HistoryPanel.java b/src/com/gitblit/wicket/panels/HistoryPanel.java index 152bbae6..0f586031 100644 --- a/src/com/gitblit/wicket/panels/HistoryPanel.java +++ b/src/com/gitblit/wicket/panels/HistoryPanel.java @@ -171,7 +171,7 @@ public class HistoryPanel extends BasePanel { LinkPanel commitHash = new LinkPanel("hashLink", null, entry.getName().substring(0, hashLen),
TreePage.class, WicketUtils.newObjectParameter(
repositoryName, entry.getName()));
- WicketUtils.setCssClass(commitHash, "sha1");
+ WicketUtils.setCssClass(commitHash, "shortsha1");
WicketUtils.setHtmlTooltip(commitHash, entry.getName());
item.add(commitHash);
diff --git a/src/com/gitblit/wicket/panels/LogPanel.html b/src/com/gitblit/wicket/panels/LogPanel.html index d0770a35..2b2605ac 100644 --- a/src/com/gitblit/wicket/panels/LogPanel.html +++ b/src/com/gitblit/wicket/panels/LogPanel.html @@ -16,7 +16,7 @@ <td class="hidden-phone author"><span wicket:id="commitAuthor">[commit author]</span></td>
<td class="hidden-phone icon"><img wicket:id="commitIcon" /></td>
<td class="message"><table class="nestedTable"><tr><td><span style="vertical-align:middle;" wicket:id="commitShortMessage">[commit short message]</span></td><td><div style="text-align:right;" wicket:id="commitRefs">[commit refs]</div></td></tr></table></td>
- <td class="hidden-phone hidden-tablet"><span wicket:id="hashLink">[hash link]</span></td>
+ <td class="hidden-phone hidden-tablet rightAlign"><span wicket:id="hashLink">[hash link]</span></td>
<td class="hidden-phone hidden-tablet rightAlign">
<span class="link">
<a wicket:id="diff"><wicket:message key="gb.diff"></wicket:message></a> | <a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a>
diff --git a/src/com/gitblit/wicket/panels/LogPanel.java b/src/com/gitblit/wicket/panels/LogPanel.java index 0686dee6..05397642 100644 --- a/src/com/gitblit/wicket/panels/LogPanel.java +++ b/src/com/gitblit/wicket/panels/LogPanel.java @@ -131,7 +131,7 @@ public class LogPanel extends BasePanel { LinkPanel commitHash = new LinkPanel("hashLink", null, entry.getName().substring(0, hashLen),
CommitPage.class, WicketUtils.newObjectParameter(
repositoryName, entry.getName()));
- WicketUtils.setCssClass(commitHash, "sha1");
+ WicketUtils.setCssClass(commitHash, "shortsha1");
WicketUtils.setHtmlTooltip(commitHash, entry.getName());
item.add(commitHash);
diff --git a/tests/com/gitblit/tests/JGitUtilsTest.java b/tests/com/gitblit/tests/JGitUtilsTest.java index 7e4d6309..ce72a46f 100644 --- a/tests/com/gitblit/tests/JGitUtilsTest.java +++ b/tests/com/gitblit/tests/JGitUtilsTest.java @@ -50,6 +50,7 @@ import com.gitblit.models.GitNote; import com.gitblit.models.PathModel;
import com.gitblit.models.PathModel.PathChangeModel;
import com.gitblit.models.RefModel;
+import com.gitblit.utils.CompressionUtils;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.StringUtils;
@@ -446,16 +447,16 @@ public class JGitUtilsTest { @Test
public void testZip() throws Exception {
- assertFalse(JGitUtils.zip(null, null, null, null));
+ assertFalse(CompressionUtils.zip(null, null, null, null));
Repository repository = GitBlitSuite.getHelloworldRepository();
File zipFileA = new File(GitBlitSuite.REPOSITORIES, "helloworld.zip");
FileOutputStream fosA = new FileOutputStream(zipFileA);
- boolean successA = JGitUtils.zip(repository, null, Constants.HEAD, fosA);
+ boolean successA = CompressionUtils.zip(repository, null, Constants.HEAD, fosA);
fosA.close();
File zipFileB = new File(GitBlitSuite.REPOSITORIES, "helloworld-java.zip");
FileOutputStream fosB = new FileOutputStream(zipFileB);
- boolean successB = JGitUtils.zip(repository, "java.java", Constants.HEAD, fosB);
+ boolean successB = CompressionUtils.zip(repository, "java.java", Constants.HEAD, fosB);
fosB.close();
repository.close();
diff --git a/tests/com/gitblit/tests/X509UtilsTest.java b/tests/com/gitblit/tests/X509UtilsTest.java index 85d30deb..5d17e18d 100644 --- a/tests/com/gitblit/tests/X509UtilsTest.java +++ b/tests/com/gitblit/tests/X509UtilsTest.java @@ -118,7 +118,12 @@ public class X509UtilsTest extends Assert { File zip = X509Utils.newClientBundle(userMetadata, storeFile, caPassword, log);
assertTrue(zip.exists());
- List<String> expected = Arrays.asList(userMetadata.commonName + ".pem", userMetadata.commonName + ".p12", "README.TXT");
+ List<String> expected = Arrays.asList(
+ userMetadata.commonName + ".pem",
+ userMetadata.commonName + ".p12",
+ userMetadata.commonName + ".cer",
+ "ca.cer",
+ "README.TXT");
ZipInputStream zis = new ZipInputStream(new FileInputStream(zip));
ZipEntry entry = null;
|