PageRenderTime 46ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/Source/JNA/waffle-jna/src/main/java/waffle/jaas/WindowsLoginModule.java

http://github.com/dblock/waffle
Java | 357 lines | 194 code | 47 blank | 116 comment | 24 complexity | 6716c06130a07d6ea0f033a641004499 MD5 | raw file
Possible License(s): MIT, MPL-2.0-no-copyleft-exception, Apache-2.0, LGPL-3.0, LGPL-2.1, GPL-2.0
  1. /*
  2. * MIT License
  3. *
  4. * Copyright (c) 2010-2021 The Waffle Project Contributors: https://github.com/Waffle/waffle/graphs/contributors
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to deal
  8. * in the Software without restriction, including without limitation the rights
  9. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in all
  14. * copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  22. * SOFTWARE.
  23. */
  24. package waffle.jaas;
  25. import java.io.IOException;
  26. import java.security.Principal;
  27. import java.util.ArrayList;
  28. import java.util.LinkedHashSet;
  29. import java.util.List;
  30. import java.util.Locale;
  31. import java.util.Map;
  32. import java.util.Set;
  33. import javax.security.auth.Subject;
  34. import javax.security.auth.callback.Callback;
  35. import javax.security.auth.callback.CallbackHandler;
  36. import javax.security.auth.callback.NameCallback;
  37. import javax.security.auth.callback.PasswordCallback;
  38. import javax.security.auth.callback.UnsupportedCallbackException;
  39. import javax.security.auth.login.LoginException;
  40. import javax.security.auth.spi.LoginModule;
  41. import org.slf4j.Logger;
  42. import org.slf4j.LoggerFactory;
  43. import waffle.windows.auth.IWindowsAccount;
  44. import waffle.windows.auth.IWindowsAuthProvider;
  45. import waffle.windows.auth.IWindowsIdentity;
  46. import waffle.windows.auth.PrincipalFormat;
  47. import waffle.windows.auth.impl.WindowsAuthProviderImpl;
  48. /**
  49. * A Java Security login module for Windows authentication.
  50. *
  51. * @author dblock[at]dblock[dot]org
  52. * @see javax.security.auth.spi.LoginModule
  53. */
  54. public class WindowsLoginModule implements LoginModule {
  55. /** The Constant LOGGER. */
  56. private static final Logger LOGGER = LoggerFactory.getLogger(WindowsLoginModule.class);
  57. /** The username. */
  58. private String username;
  59. /** The debug. */
  60. private boolean debug;
  61. /** The subject. */
  62. private Subject subject;
  63. /** The callback handler. */
  64. private CallbackHandler callbackHandler;
  65. /** The auth. */
  66. private IWindowsAuthProvider auth = new WindowsAuthProviderImpl();
  67. /** The principals. */
  68. private Set<Principal> principals;
  69. /** The principal format. */
  70. private PrincipalFormat principalFormat = PrincipalFormat.FQN;
  71. /** The role format. */
  72. private PrincipalFormat roleFormat = PrincipalFormat.FQN;
  73. /** The allow guest login. */
  74. private boolean allowGuestLogin = true;
  75. @Override
  76. public void initialize(final Subject initSubject, final CallbackHandler initCallbackHandler,
  77. final Map<String, ?> initSharedState, final Map<String, ?> initOptions) {
  78. this.subject = initSubject;
  79. this.callbackHandler = initCallbackHandler;
  80. for (final Map.Entry<String, ?> option : initOptions.entrySet()) {
  81. if ("debug".equalsIgnoreCase(option.getKey())) {
  82. this.debug = Boolean.parseBoolean((String) option.getValue());
  83. } else if ("principalFormat".equalsIgnoreCase(option.getKey())) {
  84. this.principalFormat = PrincipalFormat
  85. .valueOf(((String) option.getValue()).toUpperCase(Locale.ENGLISH));
  86. } else if ("roleFormat".equalsIgnoreCase(option.getKey())) {
  87. this.roleFormat = PrincipalFormat.valueOf(((String) option.getValue()).toUpperCase(Locale.ENGLISH));
  88. }
  89. }
  90. }
  91. /**
  92. * Use Windows SSPI to authenticate a username with a password.
  93. *
  94. * @return true, if successful
  95. * @throws LoginException
  96. * the login exception
  97. */
  98. @Override
  99. public boolean login() throws LoginException {
  100. if (this.callbackHandler == null) {
  101. throw new LoginException("Missing callback to gather information from the user.");
  102. }
  103. final NameCallback usernameCallback = new NameCallback("user name: ");
  104. final PasswordCallback passwordCallback = new PasswordCallback("password: ", false);
  105. final Callback[] callbacks = new Callback[2];
  106. callbacks[0] = usernameCallback;
  107. callbacks[1] = passwordCallback;
  108. final String userName;
  109. final String password;
  110. try {
  111. this.callbackHandler.handle(callbacks);
  112. userName = usernameCallback.getName();
  113. password = passwordCallback.getPassword() == null ? "" : new String(passwordCallback.getPassword());
  114. passwordCallback.clearPassword();
  115. } catch (final IOException e) {
  116. WindowsLoginModule.LOGGER.trace("", e);
  117. throw new LoginException(e.toString());
  118. } catch (final UnsupportedCallbackException e) {
  119. WindowsLoginModule.LOGGER.trace("", e);
  120. throw new LoginException("Callback {} not available to gather authentication information from the user."
  121. .replace("{}", e.getCallback().getClass().getName()));
  122. }
  123. IWindowsIdentity windowsIdentity;
  124. try {
  125. windowsIdentity = this.auth.logonUser(userName, password);
  126. } catch (final Exception e) {
  127. WindowsLoginModule.LOGGER.trace("", e);
  128. throw new LoginException(e.getMessage());
  129. }
  130. try {
  131. // disable guest login
  132. if (!this.allowGuestLogin && windowsIdentity.isGuest()) {
  133. WindowsLoginModule.LOGGER.debug("guest login disabled: {}", windowsIdentity.getFqn());
  134. throw new LoginException("Guest login disabled");
  135. }
  136. this.principals = new LinkedHashSet<>();
  137. // add the main user principal to the subject principals
  138. this.principals.addAll(WindowsLoginModule.getUserPrincipals(windowsIdentity, this.principalFormat));
  139. if (this.roleFormat != PrincipalFormat.NONE) {
  140. // add Windows Groups as role principles
  141. for (final IWindowsAccount group : windowsIdentity.getGroups()) {
  142. this.principals.addAll(getRolePrincipals(group, this.roleFormat));
  143. }
  144. }
  145. this.username = windowsIdentity.getFqn();
  146. WindowsLoginModule.LOGGER.debug("successfully logged in {} ({})", this.username,
  147. windowsIdentity.getSidString());
  148. } finally {
  149. windowsIdentity.dispose();
  150. }
  151. return true;
  152. }
  153. /**
  154. * Abort a login process.
  155. *
  156. * @return true, if successful
  157. * @throws LoginException
  158. * the login exception
  159. */
  160. @Override
  161. public boolean abort() throws LoginException {
  162. return this.logout();
  163. }
  164. /**
  165. * Commit principals to the subject.
  166. *
  167. * @return true, if successful
  168. * @throws LoginException
  169. * the login exception
  170. */
  171. @Override
  172. public boolean commit() throws LoginException {
  173. if (this.principals == null) {
  174. return false;
  175. }
  176. if (this.subject.isReadOnly()) {
  177. throw new LoginException("Subject cannot be read-only.");
  178. }
  179. final Set<Principal> principalsSet = this.subject.getPrincipals();
  180. principalsSet.addAll(this.principals);
  181. WindowsLoginModule.LOGGER.debug("committing {} principals",
  182. Integer.valueOf(this.subject.getPrincipals().size()));
  183. if (this.debug) {
  184. for (final Principal principal : principalsSet) {
  185. WindowsLoginModule.LOGGER.debug(" principal: {}", principal.getName());
  186. }
  187. }
  188. return true;
  189. }
  190. /**
  191. * Logout a user.
  192. *
  193. * @return true, if successful
  194. * @throws LoginException
  195. * the login exception
  196. */
  197. @Override
  198. public boolean logout() throws LoginException {
  199. if (this.subject.isReadOnly()) {
  200. throw new LoginException("Subject cannot be read-only.");
  201. }
  202. this.subject.getPrincipals().clear();
  203. if (this.username != null) {
  204. WindowsLoginModule.LOGGER.debug("logging out {}", this.username);
  205. }
  206. return true;
  207. }
  208. /**
  209. * True if Debug is enabled.
  210. *
  211. * @return True or false.
  212. */
  213. public boolean isDebug() {
  214. return this.debug;
  215. }
  216. /**
  217. * Windows auth provider.
  218. *
  219. * @return IWindowsAuthProvider.
  220. */
  221. public IWindowsAuthProvider getAuth() {
  222. return this.auth;
  223. }
  224. /**
  225. * Set Windows auth provider.
  226. *
  227. * @param provider
  228. * Class implements IWindowsAuthProvider.
  229. */
  230. public void setAuth(final IWindowsAuthProvider provider) {
  231. this.auth = provider;
  232. }
  233. /**
  234. * Returns a list of user principal objects.
  235. *
  236. * @param windowsIdentity
  237. * Windows identity.
  238. * @param principalFormat
  239. * Principal format.
  240. * @return A list of user principal objects.
  241. */
  242. private static List<Principal> getUserPrincipals(final IWindowsIdentity windowsIdentity,
  243. final PrincipalFormat principalFormat) {
  244. final List<Principal> principalsList = new ArrayList<>();
  245. switch (principalFormat) {
  246. case FQN:
  247. principalsList.add(new UserPrincipal(windowsIdentity.getFqn()));
  248. break;
  249. case SID:
  250. principalsList.add(new UserPrincipal(windowsIdentity.getSidString()));
  251. break;
  252. case BOTH:
  253. principalsList.add(new UserPrincipal(windowsIdentity.getFqn()));
  254. principalsList.add(new UserPrincipal(windowsIdentity.getSidString()));
  255. break;
  256. case NONE:
  257. default:
  258. break;
  259. }
  260. return principalsList;
  261. }
  262. /**
  263. * Returns a list of role principal objects.
  264. *
  265. * @param group
  266. * Windows group.
  267. * @param principalFormat
  268. * Principal format.
  269. * @return List of role principal objects.
  270. */
  271. private static List<Principal> getRolePrincipals(final IWindowsAccount group,
  272. final PrincipalFormat principalFormat) {
  273. final List<Principal> principalsList = new ArrayList<>();
  274. switch (principalFormat) {
  275. case FQN:
  276. principalsList.add(new RolePrincipal(group.getFqn()));
  277. break;
  278. case SID:
  279. principalsList.add(new RolePrincipal(group.getSidString()));
  280. break;
  281. case BOTH:
  282. principalsList.add(new RolePrincipal(group.getFqn()));
  283. principalsList.add(new RolePrincipal(group.getSidString()));
  284. break;
  285. case NONE:
  286. break;
  287. default:
  288. break;
  289. }
  290. return principalsList;
  291. }
  292. /**
  293. * True if Guest login permitted.
  294. *
  295. * @return True if Guest login permitted, false otherwise.
  296. */
  297. public boolean isAllowGuestLogin() {
  298. return this.allowGuestLogin;
  299. }
  300. /**
  301. * Set whether Guest login is permitted. Default is true, if the Guest account is enabled, an invalid
  302. * username/password results in a Guest login.
  303. *
  304. * @param value
  305. * True or false.
  306. */
  307. public void setAllowGuestLogin(final boolean value) {
  308. this.allowGuestLogin = value;
  309. }
  310. }