- Added FishEye hook script (pr-137)
- Added Redmine Fetch hook script (issue-359)
- Added Subgit hook contributed by TMate Software
+ - Added function to retain a user account but prohibit authentication. This is an alternative to deleting a user account.
dependencyChanges:
- updated to Jetty 8.1.13
- updated to JGit 3.2.0
\r
private static final String ACCOUNTTYPE = "accountType";\r
\r
+ private static final String DISABLED = "disabled";\r
+\r
private final File realmFile;\r
\r
private final Logger logger = LoggerFactory.getLogger(ConfigUserService.class);\r
if (!StringUtils.isEmpty(model.countryCode)) {\r
config.setString(USER, model.username, COUNTRYCODE, model.countryCode);\r
}\r
+ if (model.disabled) {\r
+ config.setBoolean(USER, model.username, DISABLED, true);\r
+ }\r
if (model.getPreferences() != null) {\r
if (!StringUtils.isEmpty(model.getPreferences().locale)) {\r
config.setString(USER, model.username, LOCALE, model.getPreferences().locale);\r
if (Constants.EXTERNAL_ACCOUNT.equals(user.password) && user.accountType.isLocal()) {\r
user.accountType = AccountType.EXTERNAL;\r
}\r
+ user.disabled = config.getBoolean(USER, username, DISABLED, false);\r
user.organizationalUnit = config.getString(USER, username, ORGANIZATIONALUNIT);\r
user.organization = config.getString(USER, username, ORGANIZATION);\r
user.locality = config.getString(USER, username, LOCALITY);\r
\r
private JCheckBox notFederatedCheckbox;\r
\r
+ private JCheckBox disabledCheckbox;\r
+\r
private JTextField organizationalUnitField;\r
\r
private JTextField organizationField;\r
notFederatedCheckbox = new JCheckBox(\r
Translation.get("gb.excludeFromFederationDescription"),\r
anUser.excludeFromFederation);\r
+ disabledCheckbox = new JCheckBox(Translation.get("gb.disableUserDescription"), anUser.disabled);\r
\r
organizationalUnitField = new JTextField(anUser.organizationalUnit == null ? "" : anUser.organizationalUnit, 25);\r
organizationField = new JTextField(anUser.organization == null ? "" : anUser.organization, 25);\r
fieldsPanel.add(newFieldPanel(Translation.get("gb.canCreate"), canCreateCheckbox));\r
fieldsPanel.add(newFieldPanel(Translation.get("gb.excludeFromFederation"),\r
notFederatedCheckbox));\r
+ fieldsPanel.add(newFieldPanel(Translation.get("gb.disableUser"), disabledCheckbox));\r
\r
JPanel attributesPanel = new JPanel(new GridLayout(0, 1, 5, 2));\r
attributesPanel.add(newFieldPanel(Translation.get("gb.organizationalUnit") + " (OU)", organizationalUnitField));\r
user.canFork = canForkCheckbox.isSelected();\r
user.canCreate = canCreateCheckbox.isSelected();\r
user.excludeFromFederation = notFederatedCheckbox.isSelected();\r
+ user.disabled = disabledCheckbox.isSelected();\r
\r
user.organizationalUnit = organizationalUnitField.getText().trim();\r
user.organization = organizationField.getText().trim();\r
flagWicketSession(AuthenticationType.CONTAINER);
logger.debug(MessageFormat.format("{0} authenticated by servlet container principal from {1}",
user.username, httpRequest.getRemoteAddr()));
- return user;
+ return validateAuthentication(user, AuthenticationType.CONTAINER);
} else if (settings.getBoolean(Keys.realm.container.autoCreateAccounts, false)
&& !internalAccount) {
// auto-create user from an authenticated container principal
flagWicketSession(AuthenticationType.CONTAINER);
logger.debug(MessageFormat.format("{0} authenticated and created by servlet container principal from {1}",
user.username, httpRequest.getRemoteAddr()));
- return user;
+ return validateAuthentication(user, AuthenticationType.CONTAINER);
} else if (!internalAccount) {
logger.warn(MessageFormat.format("Failed to find UserModel for {0}, attempted servlet container authentication from {1}",
principal.getName(), httpRequest.getRemoteAddr()));
flagWicketSession(AuthenticationType.CERTIFICATE);
logger.debug(MessageFormat.format("{0} authenticated by client certificate {1} from {2}",
user.username, metadata.serialNumber, httpRequest.getRemoteAddr()));
- return user;
+ return validateAuthentication(user, AuthenticationType.CERTIFICATE);
} else {
logger.warn(MessageFormat.format("Failed to find UserModel for {0}, attempted client certificate ({1}) authentication from {2}",
model.username, metadata.serialNumber, httpRequest.getRemoteAddr()));
flagWicketSession(AuthenticationType.COOKIE);
logger.debug(MessageFormat.format("{0} authenticated by cookie from {1}",
user.username, httpRequest.getRemoteAddr()));
- return user;
+ return validateAuthentication(user, AuthenticationType.COOKIE);
}
}
flagWicketSession(AuthenticationType.CREDENTIALS);
logger.debug(MessageFormat.format("{0} authenticated by BASIC request header from {1}",
user.username, httpRequest.getRemoteAddr()));
- return user;
+ return validateAuthentication(user, AuthenticationType.CREDENTIALS);
} else {
logger.warn(MessageFormat.format("Failed login attempt for {0}, invalid credentials from {1}",
username, httpRequest.getRemoteAddr()));
return null;
}
+ /**
+ * This method allows the authentication manager to reject authentication
+ * attempts. It is called after the username/secret have been verified to
+ * ensure that the authentication technique has been logged.
+ *
+ * @param user
+ * @return
+ */
+ protected UserModel validateAuthentication(UserModel user, AuthenticationType type) {
+ if (user == null) {
+ return null;
+ }
+ if (user.disabled) {
+ // user has been disabled
+ logger.warn("Rejected {} authentication attempt by disabled account \"{}\"",
+ type, user.username);
+ return null;
+ }
+ return user;
+ }
+
protected void flagWicketSession(AuthenticationType authenticationType) {
RequestCycle requestCycle = RequestCycle.get();
if (requestCycle != null) {
// plain-text password
returnedUser = user;
}
- return returnedUser;
+ return validateAuthentication(returnedUser, AuthenticationType.CREDENTIALS);
}
// try registered external authentication providers
if (user != null) {
// user authenticated
user.accountType = provider.getAccountType();
- return user;
+ return validateAuthentication(user, AuthenticationType.CREDENTIALS);
}
}
}
}
- return user;
+ return validateAuthentication(user, AuthenticationType.CREDENTIALS);
}
/**
public boolean canFork;\r
public boolean canCreate;\r
public boolean excludeFromFederation;\r
+ public boolean disabled;\r
// retained for backwards-compatibility with RPC clients\r
@Deprecated\r
public final Set<String> repositories = new HashSet<String>();\r
gb.approve = approve
gb.hasNotReviewed = has not reviewed
gb.about = about
-gb.ticketN = ticket #{0}
\ No newline at end of file
+gb.ticketN = ticket #{0}
+gb.disableUser = disable user
+gb.disableUserDescription = prevent this account from authenticating
\ No newline at end of file
<tr><th><wicket:message key="gb.confirmPassword"></wicket:message></th><td class="edit"><input type="password" wicket:id="confirmPassword" size="30" tabindex="3" /></td></tr>\r
<tr><th><wicket:message key="gb.displayName"></wicket:message></th><td class="edit"><input type="text" wicket:id="displayName" size="30" tabindex="4" /></td></tr>\r
<tr><th><wicket:message key="gb.emailAddress"></wicket:message></th><td class="edit"><input type="text" wicket:id="emailAddress" size="30" tabindex="5" /></td></tr>\r
- <tr><th><wicket:message key="gb.canAdmin"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="canAdmin" tabindex="6" /> <span class="help-inline"><wicket:message key="gb.canAdminDescription"></wicket:message></span></label></td></tr> \r
- <tr><th><wicket:message key="gb.canCreate"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="canCreate" tabindex="7" /> <span class="help-inline"><wicket:message key="gb.canCreateDescription"></wicket:message></span></label></td></tr> \r
- <tr><th><wicket:message key="gb.canFork"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="canFork" tabindex="8" /> <span class="help-inline"><wicket:message key="gb.canForkDescription"></wicket:message></span></label></td></tr> \r
+ <tr><th><wicket:message key="gb.canAdmin"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="canAdmin" tabindex="6" /> <span class="help-inline"><wicket:message key="gb.canAdminDescription"></wicket:message></span></label></td></tr>\r
+ <tr><th><wicket:message key="gb.canCreate"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="canCreate" tabindex="7" /> <span class="help-inline"><wicket:message key="gb.canCreateDescription"></wicket:message></span></label></td></tr>\r
+ <tr><th><wicket:message key="gb.canFork"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="canFork" tabindex="8" /> <span class="help-inline"><wicket:message key="gb.canForkDescription"></wicket:message></span></label></td></tr>\r
<tr><th><wicket:message key="gb.excludeFromFederation"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="excludeFromFederation" tabindex="9" /> <span class="help-inline"><wicket:message key="gb.excludeFromFederationDescription"></wicket:message></span></label></td></tr> \r
+ <tr><th><wicket:message key="gb.disableUser"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="disabled" tabindex="10" /> <span class="help-inline"><wicket:message key="gb.disableUserDescription"></wicket:message></span></label></td></tr>\r
</tbody>\r
</table>\r
</div>\r
form.add(new CheckBox("canFork").setEnabled(app().settings().getBoolean(Keys.web.allowForking, true)));\r
form.add(new CheckBox("canCreate"));\r
form.add(new CheckBox("excludeFromFederation"));\r
+ form.add(new CheckBox("disabled"));\r
+\r
form.add(new RegistrantPermissionsPanel("repositories", RegistrantType.REPOSITORY, repos, permissions, getAccessPermissions()));\r
form.add(teams.setEnabled(editTeams));\r
\r
// any changes to permissions or roles (issue-186)\r
UserModel user = app().users().getUserModel(session.getUser().username);\r
\r
+ if (user.disabled) {\r
+ // user was disabled during session\r
+ HttpServletResponse response = ((WebResponse) getRequestCycle().getResponse())\r
+ .getHttpServletResponse();\r
+ app().authentication().logout(response, user);\r
+ session.setUser(null);\r
+ session.invalidateNow();\r
+ return;\r
+ }\r
+\r
// validate cookie during session (issue-361)\r
if (user != null && app().settings().getBoolean(Keys.web.allowCookieAuthentication, true)) {\r
HttpServletRequest request = ((WebRequest) getRequestCycle().getRequest())\r
@Override\r
public void populateItem(final Item<UserModel> item) {\r
final UserModel entry = item.getModelObject();\r
- LinkPanel editLink = new LinkPanel("username", "list", entry.username,\r
+ String css = "list" + (entry.disabled ? "-strikethrough":"");\r
+ LinkPanel editLink = new LinkPanel("username", css, entry.username,\r
EditUserPage.class, WicketUtils.newUsernameParameter(entry.username));\r
WicketUtils.setHtmlTooltip(editLink, getString("gb.edit") + " " + entry.getDisplayName());\r
item.add(editLink);\r
if (StringUtils.isEmpty(entry.displayName)) {\r
item.add(new Label("displayName").setVisible(false));\r
} else {\r
- editLink = new LinkPanel("displayName", "list", entry.getDisplayName(),\r
+ editLink = new LinkPanel("displayName", css, entry.getDisplayName(),\r
EditUserPage.class, WicketUtils.newUsernameParameter(entry.username));\r
WicketUtils.setHtmlTooltip(editLink, getString("gb.edit") + " " + entry.getDisplayName());\r
item.add(editLink);\r
if (StringUtils.isEmpty(entry.emailAddress)) {\r
item.add(new Label("emailAddress").setVisible(false));\r
} else {\r
- editLink = new LinkPanel("emailAddress", "list", entry.emailAddress,\r
+ editLink = new LinkPanel("emailAddress", css, entry.emailAddress,\r
EditUserPage.class, WicketUtils.newUsernameParameter(entry.username));\r
WicketUtils.setHtmlTooltip(editLink, getString("gb.edit") + " " + entry.getDisplayName());\r
item.add(editLink);\r
white-space: nowrap;
vertical-align: baseline;
}
-
+\r
[class^="icon-"], [class*=" icon-"] i {\r
/* override for a links that look like bootstrap buttons */\r
vertical-align: text-bottom;\r
color: inherit;\r
}\r
\r
+a.list-strikethrough {\r
+ text-decoration: line-through;\r
+ color: inherit;\r
+}\r
+\r
a.list.subject {\r
font-weight: bold;\r
}\r
--- /dev/null
+/*
+ * Copyright 2013 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.tests;
+
+import java.util.HashMap;
+
+import org.junit.Test;
+
+import com.gitblit.manager.AuthenticationManager;
+import com.gitblit.manager.IAuthenticationManager;
+import com.gitblit.manager.IUserManager;
+import com.gitblit.manager.RuntimeManager;
+import com.gitblit.manager.UserManager;
+import com.gitblit.models.UserModel;
+import com.gitblit.tests.mock.MemorySettings;
+
+/**
+ * Class for testing local authentication.
+ *
+ * @author James Moger
+ *
+ */
+public class AuthenticationManagerTest extends GitblitUnitTest {
+
+ IUserManager users;
+
+ MemorySettings getSettings() {
+ return new MemorySettings(new HashMap<String, Object>());
+ }
+
+ IAuthenticationManager newAuthenticationManager() {
+ RuntimeManager runtime = new RuntimeManager(getSettings(), GitBlitSuite.BASEFOLDER).start();
+ users = new UserManager(runtime).start();
+ AuthenticationManager auth = new AuthenticationManager(runtime, users).start();
+ return auth;
+ }
+
+ @Test
+ public void testAuthenticate() throws Exception {
+ IAuthenticationManager auth = newAuthenticationManager();
+
+ UserModel user = new UserModel("sunnyjim");
+ user.password = "password";
+ users.updateUserModel(user);
+
+ assertNotNull(auth.authenticate(user.username, user.password.toCharArray()));
+ user.disabled = true;
+
+ users.updateUserModel(user);
+ assertNull(auth.authenticate(user.username, user.password.toCharArray()));
+ users.deleteUserModel(user);
+ }
+}
GroovyScriptTest.class, LuceneExecutorTest.class, RepositoryModelTest.class,\r
FanoutServiceTest.class, Issue0259Test.class, Issue0271Test.class, HtpasswdAuthenticationTest.class,\r
ModelUtilsTest.class, JnaUtilsTest.class, LdapSyncServiceTest.class, FileTicketServiceTest.class,
- BranchTicketServiceTest.class, RedisTicketServiceTest.class })
+ BranchTicketServiceTest.class, RedisTicketServiceTest.class, AuthenticationManagerTest.class })
public class GitBlitSuite {\r
\r
public static final File BASEFOLDER = new File("data");\r