| /*=============================================================================# |
| # Copyright (c) 2009, 2019 Stephan Wahlbrink and others. |
| # |
| # This program and the accompanying materials are made available under the |
| # terms of the Eclipse Public License 2.0 which is available at |
| # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 |
| # which is available at https://www.apache.org/licenses/LICENSE-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 |
| # |
| # Contributors: |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.rj.server.srvext.auth; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.nio.CharBuffer; |
| import java.nio.charset.Charset; |
| import java.security.MessageDigest; |
| import java.security.SecureRandom; |
| import java.util.Arrays; |
| import java.util.Map.Entry; |
| import java.util.Properties; |
| import java.util.Set; |
| |
| import javax.security.auth.callback.Callback; |
| import javax.security.auth.callback.NameCallback; |
| import javax.security.auth.callback.PasswordCallback; |
| import javax.security.auth.login.FailedLoginException; |
| import javax.security.auth.login.LoginException; |
| |
| import org.eclipse.statet.rj.RjException; |
| import org.eclipse.statet.rj.server.srvext.ServerAuthMethod; |
| import org.eclipse.statet.rj.server.util.ServerUtils; |
| import org.eclipse.statet.rj.server.util.ServerUtils.ArgKeyValue; |
| |
| |
| /** |
| * Authentication method 'name-pass' |
| * to authenticate against a given name password pair. |
| */ |
| public class SimpleNamePassAuthMethod extends ServerAuthMethod { |
| |
| |
| private Properties users; |
| |
| private byte[] digestSash; |
| private MessageDigest digestService; |
| private Charset digestCharset; |
| |
| |
| public SimpleNamePassAuthMethod() { |
| super("name-pass", true); |
| } |
| |
| |
| @Override |
| public void doInit(final String arg) throws RjException { |
| final ArgKeyValue config= ServerUtils.getArgConfigValue(arg); |
| try { |
| this.digestSash= new byte[8]; |
| final SecureRandom random= SecureRandom.getInstance("SHA1PRNG"); |
| random.nextBytes(this.digestSash); |
| this.digestService= MessageDigest.getInstance("SHA-512"); |
| this.digestCharset= Charset.forName("UTF-8"); |
| } |
| catch (final Exception e) { |
| throw new RjException("", e); |
| } |
| |
| if (config.getKey().equals("file")) { |
| final String fileName= config.getValue(); |
| if (fileName == null || fileName.length() == 0) { |
| throw new RjException("Missing password file name.", null); |
| } |
| final File file= new File(fileName); |
| this.users= new Properties(); |
| try { |
| this.users.load(new FileInputStream(file)); |
| } |
| catch (final IOException e) { |
| throw new RjException("Reading password file failed.", null); |
| } |
| } |
| else { |
| throw new RjException(String.format("Unsupported configuration type '%1$s'.", config.getKey())); |
| } |
| this.digestService.update(this.digestSash); |
| final Set<Entry<Object,Object>> entrySet= this.users.entrySet(); |
| for (final Entry<Object, Object> entry : entrySet) { |
| final byte[] password= this.digestService.digest(this.digestCharset.encode( |
| (String) entry.getValue()).array()); |
| entry.setValue(password); |
| } |
| System.gc(); |
| } |
| |
| @Override |
| protected Callback[] doCreateLogin() throws RjException { |
| return new Callback[] { |
| new NameCallback("Loginname"), |
| new PasswordCallback("Password", false), |
| }; |
| } |
| |
| @Override |
| protected String doPerformLogin(final Callback[] callbacks) throws LoginException, RjException { |
| final String loginName= ((NameCallback) callbacks[0]).getName(); |
| final Object object= this.users.get(loginName); |
| if (object instanceof byte[]) { |
| final byte[] loginPassword= getPass((PasswordCallback) callbacks[1]); |
| if (Arrays.equals((byte[]) object, loginPassword)) { |
| return loginName; |
| } |
| } |
| throw new FailedLoginException("Invalid loginname or password"); |
| } |
| |
| private byte[] getPass(final PasswordCallback callback) { |
| final char[] loginPassword= callback.getPassword(); |
| final byte[] loginBytes; |
| if (loginPassword == null) { |
| return new byte[0]; |
| } |
| this.digestService.update(this.digestSash); |
| loginBytes= this.digestService.digest(this.digestCharset.encode( |
| CharBuffer.wrap(loginPassword)).array()); |
| callback.clearPassword(); |
| Arrays.fill(loginPassword, (char) 0); |
| return loginBytes; |
| } |
| |
| } |