From 37fa664c58df034607edf2485a1414b3417b2755 Mon Sep 17 00:00:00 2001 From: James Moger Date: Mon, 3 Dec 2012 16:59:17 -0500 Subject: Consolidate authentication techniques and support container principals (issue-68) --- src/com/gitblit/GitBlit.java | 103 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 86 insertions(+), 17 deletions(-) (limited to 'src/com/gitblit/GitBlit.java') diff --git a/src/com/gitblit/GitBlit.java b/src/com/gitblit/GitBlit.java index 69135c49..02906ee9 100644 --- a/src/com/gitblit/GitBlit.java +++ b/src/com/gitblit/GitBlit.java @@ -24,6 +24,8 @@ import java.io.InputStreamReader; import java.lang.reflect.Field; import java.net.URI; import java.net.URISyntaxException; +import java.nio.charset.Charset; +import java.security.Principal; import java.text.MessageFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -98,6 +100,7 @@ import com.gitblit.models.SettingModel; import com.gitblit.models.TeamModel; import com.gitblit.models.UserModel; import com.gitblit.utils.ArrayUtils; +import com.gitblit.utils.Base64; import com.gitblit.utils.ByteFormat; import com.gitblit.utils.ContainerUtils; import com.gitblit.utils.DeepCopier; @@ -567,6 +570,20 @@ public class GitBlit implements ServletContextListener { * @return a user object or null */ public UserModel authenticate(HttpServletRequest httpRequest) { + return authenticate(httpRequest, false); + } + + /** + * Authenticate a user based on HTTP request parameters. + * + * Authentication by X509Certificate, servlet container principal, cookie, + * and BASIC header. + * + * @param httpRequest + * @param requiresCertificate + * @return a user object or null + */ + public UserModel authenticate(HttpServletRequest httpRequest, boolean requiresCertificate) { // try to authenticate by certificate boolean checkValidity = settings.getBoolean(Keys.git.enforceCertificateValidity, true); String [] oids = getStrings(Keys.git.certificateUsernameOIDs).toArray(new String[0]); @@ -574,39 +591,85 @@ public class GitBlit implements ServletContextListener { if (model != null) { // grab real user model and preserve certificate serial number UserModel user = getUserModel(model.username); + X509Metadata metadata = HttpUtils.getCertificateMetadata(httpRequest); 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); + flagWicketSession(AuthenticationType.CERTIFICATE); logger.info(MessageFormat.format("{0} authenticated by client certificate {1} from {2}", user.username, metadata.serialNumber, httpRequest.getRemoteAddr())); return user; + } else { + logger.warn(MessageFormat.format("Failed to find UserModel for {0}, attempted client certificate ({1}) authentication from {2}", + model.username, metadata.serialNumber, httpRequest.getRemoteAddr())); + } + } + + if (requiresCertificate) { + // caller requires client certificate authentication (e.g. git servlet) + return null; + } + + // try to authenticate by servlet container principal + Principal principal = httpRequest.getUserPrincipal(); + if (principal != null) { + UserModel user = getUserModel(principal.getName()); + if (user != null) { + flagWicketSession(AuthenticationType.CONTAINER); + logger.info(MessageFormat.format("{0} authenticated by servlet container principal from {1}", + user.username, httpRequest.getRemoteAddr())); + return user; + } else { + logger.warn(MessageFormat.format("Failed to find UserModel for {0}, attempted servlet container authentication from {1}", + principal.getName(), httpRequest.getRemoteAddr())); } } // 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 (allowCookieAuthentication()) { + UserModel user = authenticate(httpRequest.getCookies()); 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; - } + flagWicketSession(AuthenticationType.COOKIE); logger.info(MessageFormat.format("{0} authenticated by cookie from {1}", user.username, httpRequest.getRemoteAddr())); return user; } } + + // try to authenticate by BASIC + final String authorization = httpRequest.getHeader("Authorization"); + if (authorization != null && authorization.startsWith("Basic")) { + // Authorization: Basic base64credentials + String base64Credentials = authorization.substring("Basic".length()).trim(); + String credentials = new String(Base64.decode(base64Credentials), + Charset.forName("UTF-8")); + // credentials = username:password + final String[] values = credentials.split(":",2); + + if (values.length == 2) { + String username = values[0]; + char[] password = values[1].toCharArray(); + UserModel user = authenticate(username, password); + if (user != null) { + flagWicketSession(AuthenticationType.CREDENTIALS); + logger.info(MessageFormat.format("{0} authenticated by BASIC request header from {1}", + user.username, httpRequest.getRemoteAddr())); + return user; + } else { + logger.warn(MessageFormat.format("Failed login attempt for {0}, invalid credentials ({1}) from {2}", + username, credentials, httpRequest.getRemoteAddr())); + } + } + } return null; } + + protected void flagWicketSession(AuthenticationType authenticationType) { + RequestCycle requestCycle = RequestCycle.get(); + if (requestCycle != null) { + // flag the Wicket session, if this is a Wicket request + GitBlitWebSession session = GitBlitWebSession.get(); + session.authenticationType = authenticationType; + } + } /** * Open a file resource using the Servlet container. @@ -693,6 +756,9 @@ public class GitBlit implements ServletContextListener { * @return true if successful */ public boolean deleteUser(String username) { + if (StringUtils.isEmpty(username)) { + return false; + } return userService.deleteUser(username); } @@ -704,6 +770,9 @@ public class GitBlit implements ServletContextListener { * @return a user object or null */ public UserModel getUserModel(String username) { + if (StringUtils.isEmpty(username)) { + return null; + } UserModel user = userService.getUserModel(username); return user; } -- cgit v1.2.3 From 8295dd6cab32df383a30e4bd78e4aff17cfa2187 Mon Sep 17 00:00:00 2001 From: James Moger Date: Wed, 5 Dec 2012 17:20:06 -0500 Subject: Global and per-repository setting to throttle prolific repos in Activity page (issue-173) --- distrib/gitblit.properties | 8 +++++++ docs/04_releases.mkd | 1 + src/com/gitblit/GitBlit.java | 14 +++++++++++- src/com/gitblit/client/EditRepositoryDialog.java | 9 ++++++++ src/com/gitblit/models/RepositoryModel.java | 1 + src/com/gitblit/utils/ActivityUtils.java | 6 +++++- src/com/gitblit/wicket/GitBlitWebApp.properties | 5 ++++- .../gitblit/wicket/pages/EditRepositoryPage.html | 15 +++++++------ .../gitblit/wicket/pages/EditRepositoryPage.java | 25 ++++++++++++++++++++++ 9 files changed, 74 insertions(+), 10 deletions(-) (limited to 'src/com/gitblit/GitBlit.java') diff --git a/distrib/gitblit.properties b/distrib/gitblit.properties index d57f9ba1..e3d72211 100644 --- a/distrib/gitblit.properties +++ b/distrib/gitblit.properties @@ -522,6 +522,14 @@ web.shortCommitIdLength = 6 # SINCE 0.8.0 web.allowFlashCopyToClipboard = true +# Default maximum number of commits that a repository may contribute to the +# activity page, regardless of the selected duration. This setting may be valuable +# for an extremely busy server. This value may also be configed per-repository +# in Edit Repository. 0 disables this throttle. +# +# SINCE 1.2.0 +web.maxActivityCommits = 0 + # Default number of entries to include in RSS Syndication links # # SINCE 0.5.0 diff --git a/docs/04_releases.mkd b/docs/04_releases.mkd index 7dd4e747..52bd51e7 100644 --- a/docs/04_releases.mkd +++ b/docs/04_releases.mkd @@ -73,6 +73,7 @@ This is extreme and should be considered carefully since it affects every https #### changes +- Added optional global and per-repository activity page commit contribution throttle to help tame *really* active repositories (issue 173) - Added support for symlinks in tree page and commit page (issue 171) - All access restricted servlets (e.g. DownloadZip, RSS, etc) will try to authenticate using X509 certificates, container principals, cookies, and BASIC headers, in that order. - Added *groovy* and *scala* to *web.prettyPrintExtensions* diff --git a/src/com/gitblit/GitBlit.java b/src/com/gitblit/GitBlit.java index 02906ee9..c2d4a85a 100644 --- a/src/com/gitblit/GitBlit.java +++ b/src/com/gitblit/GitBlit.java @@ -1602,6 +1602,7 @@ public class GitBlit implements ServletContextListener { } catch (Exception e) { model.lastGC = new Date(0); } + model.maxActivityCommits = getConfig(config, "maxActivityCommits", settings.getInteger(Keys.web.maxActivityCommits, 0)); model.origin = config.getString("remote", "origin", "url"); if (model.origin != null) { model.origin = model.origin.replace('\\', '/'); @@ -2068,10 +2069,21 @@ public class GitBlit implements ServletContextListener { repository.federationStrategy.name()); config.setBoolean(Constants.CONFIG_GITBLIT, null, "isFederated", repository.isFederated); config.setString(Constants.CONFIG_GITBLIT, null, "gcThreshold", repository.gcThreshold); - config.setInt(Constants.CONFIG_GITBLIT, null, "gcPeriod", repository.gcPeriod); + if (repository.gcPeriod == settings.getInteger(Keys.git.defaultGarbageCollectionPeriod, 7)) { + // use default from config + config.unset(Constants.CONFIG_GITBLIT, null, "gcPeriod"); + } else { + config.setInt(Constants.CONFIG_GITBLIT, null, "gcPeriod", repository.gcPeriod); + } if (repository.lastGC != null) { config.setString(Constants.CONFIG_GITBLIT, null, "lastGC", new SimpleDateFormat(Constants.ISO8601).format(repository.lastGC)); } + if (repository.maxActivityCommits == settings.getInteger(Keys.web.maxActivityCommits, 0)) { + // use default from config + config.unset(Constants.CONFIG_GITBLIT, null, "maxActivityCommits"); + } else { + config.setInt(Constants.CONFIG_GITBLIT, null, "maxActivityCommits", repository.maxActivityCommits); + } updateList(config, "federationSets", repository.federationSets); updateList(config, "preReceiveScript", repository.preReceiveScripts); diff --git a/src/com/gitblit/client/EditRepositoryDialog.java b/src/com/gitblit/client/EditRepositoryDialog.java index a9274964..aa6ad58d 100644 --- a/src/com/gitblit/client/EditRepositoryDialog.java +++ b/src/com/gitblit/client/EditRepositoryDialog.java @@ -124,6 +124,8 @@ public class EditRepositoryDialog extends JDialog { private JComboBox gcPeriod; private JTextField gcThreshold; + + private JComboBox maxActivityCommits; private RegistrantPermissionsPanel usersPalette; @@ -225,6 +227,10 @@ public class EditRepositoryDialog extends JDialog { isFrozen = new JCheckBox(Translation.get("gb.isFrozenDescription"), anRepository.isFrozen); + maxActivityCommits = new JComboBox(new Integer [] { 0, 25, 50, 75, 100, 150, 250, 500 }); + maxActivityCommits.setSelectedItem(anRepository.maxActivityCommits); + + mailingListsField = new JTextField( ArrayUtils.isEmpty(anRepository.mailingLists) ? "" : StringUtils.flattenStrings(anRepository.mailingLists, @@ -314,6 +320,8 @@ public class EditRepositoryDialog extends JDialog { skipSizeCalculation)); fieldsPanel.add(newFieldPanel(Translation.get("gb.skipSummaryMetrics"), skipSummaryMetrics)); + fieldsPanel.add(newFieldPanel(Translation.get("gb.maxActivityCommits"), + maxActivityCommits)); fieldsPanel.add(newFieldPanel(Translation.get("gb.mailingLists"), mailingListsField)); @@ -561,6 +569,7 @@ public class EditRepositoryDialog extends JDialog { repository.showReadme = showReadme.isSelected(); repository.skipSizeCalculation = skipSizeCalculation.isSelected(); repository.skipSummaryMetrics = skipSummaryMetrics.isSelected(); + repository.maxActivityCommits = (Integer) maxActivityCommits.getSelectedItem(); repository.isFrozen = isFrozen.isSelected(); repository.allowForks = allowForks.isSelected(); diff --git a/src/com/gitblit/models/RepositoryModel.java b/src/com/gitblit/models/RepositoryModel.java index ed9e7188..5be33a2d 100644 --- a/src/com/gitblit/models/RepositoryModel.java +++ b/src/com/gitblit/models/RepositoryModel.java @@ -78,6 +78,7 @@ public class RepositoryModel implements Serializable, Comparable commits = JGitUtils.getRevLog(repository, branch, thresholdDate); - for (RevCommit commit : commits) { + if (model.maxActivityCommits > 0 && commits.size() > model.maxActivityCommits) { + // trim commits to maximum count + commits = commits.subList(0, model.maxActivityCommits); + } + for (RevCommit commit : commits) { Date date = JGitUtils.getCommitDate(commit); String dateStr = df.format(date); if (!activity.containsKey(dateStr)) { diff --git a/src/com/gitblit/wicket/GitBlitWebApp.properties b/src/com/gitblit/wicket/GitBlitWebApp.properties index 6ee12990..9ee9f397 100644 --- a/src/com/gitblit/wicket/GitBlitWebApp.properties +++ b/src/com/gitblit/wicket/GitBlitWebApp.properties @@ -430,4 +430,7 @@ gb.pleaseGenerateClientCertificate = Please generate a client certificate for {0 gb.clientCertificateBundleSent = Client certificate bundle for {0} sent gb.enterKeystorePassword = Please enter the Gitblit keystore password gb.warning = warning -gb.jceWarning = Your Java Runtime Environment does not have the \"JCE Unlimited Strength Jurisdiction Policy\" files.\nThis will limit the length of passwords you may use to encrypt your keystores to 7 characters.\nThese policy files are an optional download from Oracle.\n\nWould you like to continue and generate the certificate infrastructure anyway?\n\nAnswering No will direct your browser to Oracle's download page so that you may download the policy files. \ No newline at end of file +gb.jceWarning = Your Java Runtime Environment does not have the \"JCE Unlimited Strength Jurisdiction Policy\" files.\nThis will limit the length of passwords you may use to encrypt your keystores to 7 characters.\nThese policy files are an optional download from Oracle.\n\nWould you like to continue and generate the certificate infrastructure anyway?\n\nAnswering No will direct your browser to Oracle's download page so that you may download the policy files. +gb.maxActivityCommits = max activity commits +gb.maxActivityCommitsDescription = maximum number of commits to contribute to the Activity page +gb.noMaximum = no maximum \ No newline at end of file diff --git a/src/com/gitblit/wicket/pages/EditRepositoryPage.html b/src/com/gitblit/wicket/pages/EditRepositoryPage.html index cd3c4662..60893f44 100644 --- a/src/com/gitblit/wicket/pages/EditRepositoryPage.html +++ b/src/com/gitblit/wicket/pages/EditRepositoryPage.html @@ -39,8 +39,9 @@ + + @@ -49,15 +50,15 @@
- + - + - - - + + + @@ -70,7 +71,7 @@





- +
diff --git a/src/com/gitblit/wicket/pages/EditRepositoryPage.java b/src/com/gitblit/wicket/pages/EditRepositoryPage.java index 9de8244a..7f66f688 100644 --- a/src/com/gitblit/wicket/pages/EditRepositoryPage.java +++ b/src/com/gitblit/wicket/pages/EditRepositoryPage.java @@ -414,6 +414,9 @@ public class EditRepositoryPage extends RootSubPage { form.add(new CheckBox("showReadme")); form.add(new CheckBox("skipSizeCalculation")); form.add(new CheckBox("skipSummaryMetrics")); + List maxActivityCommits = Arrays.asList(0, 25, 50, 75, 100, 150, 200, 250, 500 ); + form.add(new DropDownChoice("maxActivityCommits", maxActivityCommits, new MaxActivityCommitsRenderer())); + mailingLists = new Model(ArrayUtils.isEmpty(repositoryModel.mailingLists) ? "" : StringUtils.flattenStrings(repositoryModel.mailingLists, " ")); form.add(new TextField("mailingLists", mailingLists)); @@ -654,4 +657,26 @@ public class EditRepositoryPage extends RootSubPage { } } + private class MaxActivityCommitsRenderer implements IChoiceRenderer { + + private static final long serialVersionUID = 1L; + + public MaxActivityCommitsRenderer() { + } + + @Override + public String getDisplayValue(Integer value) { + if (value == 0) { + return getString("gb.noMaximum"); + } else { + return value + " " + getString("gb.commits"); + } + } + + @Override + public String getIdValue(Integer value, int index) { + return Integer.toString(index); + } + } + } -- cgit v1.2.3