Adding validation token generator got login
This commit is contained in:
parent
e9bc481884
commit
de5816adf0
|
@ -0,0 +1,47 @@
|
||||||
|
package org.apache.archiva.redback.authentication;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Exception used by the token manager.
|
||||||
|
*
|
||||||
|
* Created by Martin Stockhammer on 11.02.17.
|
||||||
|
*/
|
||||||
|
public class EncryptionFailedException extends Exception {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -2275802156651048276L;
|
||||||
|
|
||||||
|
public EncryptionFailedException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public EncryptionFailedException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EncryptionFailedException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EncryptionFailedException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package org.apache.archiva.redback.authentication;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception used by the token manager.
|
||||||
|
*
|
||||||
|
* Created by Martin Stockhammer on 11.02.17.
|
||||||
|
*/
|
||||||
|
public class InvalidTokenException extends Exception {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -1148088610607667870L;
|
||||||
|
|
||||||
|
public InvalidTokenException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvalidTokenException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvalidTokenException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvalidTokenException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
package org.apache.archiva.redback.authentication;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Simple Token information class that contains a username and a lifetime.
|
||||||
|
*
|
||||||
|
* The class is not able to detect time manipulations. It is assumed that the
|
||||||
|
* current time of the system is correct.
|
||||||
|
*
|
||||||
|
* This class is immutable.
|
||||||
|
*
|
||||||
|
* Created by Martin Stockhammer on 03.02.17.
|
||||||
|
*/
|
||||||
|
public final class SimpleTokenData implements Serializable, TokenData {
|
||||||
|
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 5907745449771921813L;
|
||||||
|
|
||||||
|
private final String user;
|
||||||
|
private final Date created;
|
||||||
|
private final Date validBefore;
|
||||||
|
private final long nonce;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new token info instance for the given user.
|
||||||
|
* The lifetime in milliseconds defines the invalidation date by
|
||||||
|
* adding the lifetime to the current time of instantiation.
|
||||||
|
*
|
||||||
|
* @param user The user name
|
||||||
|
* @param lifetime The number of milliseconds after that the token is invalid
|
||||||
|
* @param nonce Should be a random number and different for each instance.
|
||||||
|
*/
|
||||||
|
public SimpleTokenData(final String user, final long lifetime, final long nonce) {
|
||||||
|
this.user=user;
|
||||||
|
this.created=new Date();
|
||||||
|
this.validBefore =new Date(created.getTime()+lifetime);
|
||||||
|
this.nonce = nonce;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final Date created() {
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final Date validBefore() {
|
||||||
|
return validBefore;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final long getNonce() {
|
||||||
|
return nonce;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid() {
|
||||||
|
return (new Date().getTime())<validBefore.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
package org.apache.archiva.redback.authentication;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* This contains the token payload that is used for verification of tokens.
|
||||||
|
*
|
||||||
|
* Created by Martin Stockhammer on 11.02.17.
|
||||||
|
*/
|
||||||
|
public interface TokenData {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the user name.
|
||||||
|
*
|
||||||
|
* @return The username property.
|
||||||
|
*/
|
||||||
|
String getUser();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The date the token was created.
|
||||||
|
*
|
||||||
|
* @return The creation date.
|
||||||
|
*/
|
||||||
|
Date created();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The date after that the token is invalid.
|
||||||
|
*
|
||||||
|
* @return The invalidation date.
|
||||||
|
*/
|
||||||
|
Date validBefore();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The nonce that is stored in the token.
|
||||||
|
*
|
||||||
|
* @return The nonce.
|
||||||
|
*/
|
||||||
|
long getNonce();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true, if the token is valid.
|
||||||
|
*
|
||||||
|
* @return True, if valid, otherwise false.
|
||||||
|
*/
|
||||||
|
boolean isValid();
|
||||||
|
}
|
|
@ -0,0 +1,248 @@
|
||||||
|
package org.apache.archiva.redback.authentication;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import javax.crypto.BadPaddingException;
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.IllegalBlockSizeException;
|
||||||
|
import javax.crypto.KeyGenerator;
|
||||||
|
import javax.crypto.NoSuchPaddingException;
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InvalidClassException;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Class that manages tokens that are encrypted with a dynamic key. The tokens
|
||||||
|
* are converted into BASE64 strings.
|
||||||
|
*
|
||||||
|
* Each token contains information about username,
|
||||||
|
*
|
||||||
|
* Created by Martin Stockhammer on 03.02.17.
|
||||||
|
*/
|
||||||
|
@Service("tokenManager#jce")
|
||||||
|
public class TokenManager {
|
||||||
|
|
||||||
|
private static final SecureRandom rd = new SecureRandom();
|
||||||
|
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||||
|
private String algorithm = "AES/ECB/PKCS5Padding";
|
||||||
|
private int keySize = -1;
|
||||||
|
private Cipher deCipher;
|
||||||
|
private Cipher enCipher;
|
||||||
|
|
||||||
|
boolean paddingUsed = true;
|
||||||
|
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void initialize() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, EncryptionFailedException, InvalidAlgorithmParameterException {
|
||||||
|
log.debug("Initializing key for token generator");
|
||||||
|
try {
|
||||||
|
enCipher = Cipher.getInstance(algorithm);
|
||||||
|
deCipher = Cipher.getInstance(algorithm);
|
||||||
|
String[] keyAlg = enCipher.getAlgorithm().split("/");
|
||||||
|
if (keyAlg.length<1) {
|
||||||
|
throw new EncryptionFailedException("Initialization of key failed. Not algorithm found.");
|
||||||
|
}
|
||||||
|
String encryptionAlgorithm = keyAlg[0];
|
||||||
|
KeyGenerator keyGen = KeyGenerator.getInstance(encryptionAlgorithm);
|
||||||
|
if (keySize>0) {
|
||||||
|
keyGen.init(keySize);
|
||||||
|
}
|
||||||
|
if (keyAlg.length==3 && keyAlg[2].equals("NoPadding")) {
|
||||||
|
paddingUsed=false;
|
||||||
|
}
|
||||||
|
SecretKey secretKey = keyGen.generateKey();
|
||||||
|
enCipher.init(Cipher.ENCRYPT_MODE, secretKey);
|
||||||
|
// We have to provide the IV depending on the algorithm used
|
||||||
|
// CBC needs an IV, ECB not.
|
||||||
|
if (enCipher.getIV()==null) {
|
||||||
|
deCipher.init(Cipher.DECRYPT_MODE, secretKey);
|
||||||
|
} else {
|
||||||
|
deCipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(enCipher.getIV()));
|
||||||
|
}
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
log.error("Error occurred during key initialization. Requested algorithm not available. "+e.getMessage());
|
||||||
|
throw e;
|
||||||
|
} catch (NoSuchPaddingException e) {
|
||||||
|
log.error("Error occurred during key initialization. Requested padding not available. "+e.getMessage());
|
||||||
|
throw e;
|
||||||
|
} catch (InvalidKeyException e) {
|
||||||
|
log.error("The key is not valid.");
|
||||||
|
throw e;
|
||||||
|
} catch (InvalidAlgorithmParameterException e) {
|
||||||
|
log.error("Invalid encryption parameters.");
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("SameParameterValue")
|
||||||
|
public String encryptToken(String user, long lifetime) throws EncryptionFailedException {
|
||||||
|
return encryptToken(new SimpleTokenData(user, lifetime, createNonce()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String encryptToken(TokenData tokenData) throws EncryptionFailedException {
|
||||||
|
try {
|
||||||
|
return encode(encrypt(tokenData));
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("Error during object conversion: "+e.getMessage());
|
||||||
|
throw new EncryptionFailedException(e);
|
||||||
|
} catch (BadPaddingException e) {
|
||||||
|
log.error("Padding invalid");
|
||||||
|
throw new EncryptionFailedException(e);
|
||||||
|
} catch (IllegalBlockSizeException e) {
|
||||||
|
log.error("Block size invalid");
|
||||||
|
throw new EncryptionFailedException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TokenData decryptToken(String token) throws InvalidTokenException {
|
||||||
|
try {
|
||||||
|
return decrypt(decode(token));
|
||||||
|
} catch (IOException ex) {
|
||||||
|
log.error("Error during data read. " + ex.getMessage());
|
||||||
|
throw new InvalidTokenException(ex);
|
||||||
|
} catch (ClassNotFoundException ex) {
|
||||||
|
log.error("Token data invalid.");
|
||||||
|
throw new InvalidTokenException(ex);
|
||||||
|
} catch (BadPaddingException ex) {
|
||||||
|
log.error("The encrypted token has the wrong padding.");
|
||||||
|
throw new InvalidTokenException(ex);
|
||||||
|
} catch (IllegalBlockSizeException ex) {
|
||||||
|
log.error("The encrypted token has the wrong block size.");
|
||||||
|
throw new InvalidTokenException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private long createNonce() {
|
||||||
|
return rd.nextLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected byte[] encrypt(TokenData info) throws IOException, BadPaddingException, IllegalBlockSizeException {
|
||||||
|
return doEncrypt(convertToByteArray(info));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected byte[] doEncrypt(byte[] data) throws BadPaddingException, IllegalBlockSizeException {
|
||||||
|
byte[] encData;
|
||||||
|
if (!paddingUsed && (data.length % enCipher.getBlockSize())!=0) {
|
||||||
|
int blocks = data.length / enCipher.getBlockSize();
|
||||||
|
encData = Arrays.copyOf(data, enCipher.getBlockSize()*(blocks+1));
|
||||||
|
} else {
|
||||||
|
encData = data;
|
||||||
|
}
|
||||||
|
return enCipher.doFinal(encData);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected TokenData decrypt(byte[] token) throws BadPaddingException, IllegalBlockSizeException, IOException, ClassNotFoundException {
|
||||||
|
Object result = convertFromByteArray(doDecrypt(token));
|
||||||
|
if (!(result instanceof TokenData)) {
|
||||||
|
throw new InvalidClassException("No TokenData found in decrypted token");
|
||||||
|
}
|
||||||
|
return (TokenData)result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected byte[] doDecrypt(byte[] encryptedData) throws BadPaddingException, IllegalBlockSizeException {
|
||||||
|
return deCipher.doFinal(encryptedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String encode(byte[] token) {
|
||||||
|
return Base64.encodeBase64String(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] decode(String token) {
|
||||||
|
return Base64.decodeBase64(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Object convertFromByteArray(byte[] byteObject) throws IOException,
|
||||||
|
ClassNotFoundException {
|
||||||
|
ByteArrayInputStream bais;
|
||||||
|
ObjectInputStream in;
|
||||||
|
bais = new ByteArrayInputStream(byteObject);
|
||||||
|
in = new ObjectInputStream(bais);
|
||||||
|
Object o = in.readObject();
|
||||||
|
in.close();
|
||||||
|
return o;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private byte[] convertToByteArray(Object complexObject) throws IOException {
|
||||||
|
ByteArrayOutputStream baos;
|
||||||
|
ObjectOutputStream out;
|
||||||
|
baos = new ByteArrayOutputStream();
|
||||||
|
out = new ObjectOutputStream(baos);
|
||||||
|
out.writeObject(complexObject);
|
||||||
|
out.close();
|
||||||
|
return baos.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAlgorithm() {
|
||||||
|
return algorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the encryption algorithm and resets the key size. You may change the key size after
|
||||||
|
* calling this method.
|
||||||
|
* Additionally run the initialize() method after setting algorithm and keysize.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param algorithm The encryption algorithm to use.
|
||||||
|
*/
|
||||||
|
public void setAlgorithm(String algorithm) {
|
||||||
|
if (!this.algorithm.equals(algorithm)) {
|
||||||
|
this.algorithm = algorithm;
|
||||||
|
this.keySize=-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getKeySize() {
|
||||||
|
return keySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the key size for the encryption. This method must be called after
|
||||||
|
* setting the algorithm. The keysize will be reset by calling <code>setAlgorithm()</code>
|
||||||
|
*
|
||||||
|
* The key size must be valid for the given algorithm.
|
||||||
|
*
|
||||||
|
* @param keySize The size of the encryption key
|
||||||
|
*/
|
||||||
|
public void setKeySize(int keySize) {
|
||||||
|
this.keySize = keySize;
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,5 +30,7 @@
|
||||||
<context:annotation-config />
|
<context:annotation-config />
|
||||||
<context:component-scan
|
<context:component-scan
|
||||||
base-package="org.apache.archiva.redback.authentication"/>
|
base-package="org.apache.archiva.redback.authentication"/>
|
||||||
|
|
||||||
|
<alias name="tokenManager#jce" alias="tokenManager#default" />
|
||||||
|
|
||||||
</beans>
|
</beans>
|
|
@ -0,0 +1,153 @@
|
||||||
|
package org.apache.archiva.redback.authentication;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import javax.crypto.BadPaddingException;
|
||||||
|
import javax.crypto.IllegalBlockSizeException;
|
||||||
|
import javax.crypto.NoSuchPaddingException;
|
||||||
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the TokenManager implementation. Uses no spring dependencies.
|
||||||
|
*
|
||||||
|
* Created by Martin Stockhammer on 11.02.17.
|
||||||
|
*/
|
||||||
|
public class TokenManagerTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void initialize() throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, EncryptionFailedException, InvalidAlgorithmParameterException {
|
||||||
|
TokenManager tokenManager = new TokenManager();
|
||||||
|
tokenManager.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void encryptToken() throws Exception {
|
||||||
|
TokenManager tokenManager = new TokenManager();
|
||||||
|
tokenManager.initialize();
|
||||||
|
assertEquals(tokenManager.getAlgorithm(),"AES/ECB/PKCS5Padding");
|
||||||
|
assertEquals(tokenManager.getKeySize(), -1);
|
||||||
|
String token = tokenManager.encryptToken("testuser01",1000);
|
||||||
|
assertNotNull(token);
|
||||||
|
assertTrue("Token size too low",token.length()>300);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void decryptToken() throws Exception {
|
||||||
|
TokenManager tokenManager = new TokenManager();
|
||||||
|
tokenManager.initialize();
|
||||||
|
String token = tokenManager.encryptToken("testuser00003",1000);
|
||||||
|
assertNotNull(token);
|
||||||
|
assertTrue("Token size too low",token.length()>300);
|
||||||
|
TokenData tokenData = tokenManager.decryptToken(token);
|
||||||
|
assertNotNull(tokenData);
|
||||||
|
assertEquals("testuser00003", tokenData.getUser());
|
||||||
|
assertTrue(tokenData.isValid());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void decryptExpiredToken() throws Exception {
|
||||||
|
TokenManager tokenManager = new TokenManager();
|
||||||
|
tokenManager.initialize();
|
||||||
|
SimpleTokenData sToken = new SimpleTokenData("testuser00003", 0, 1345455);
|
||||||
|
String token = tokenManager.encryptToken(sToken);
|
||||||
|
assertNotNull(token);
|
||||||
|
assertTrue("Token size too low",token.length()>300);
|
||||||
|
TokenData tokenData = tokenManager.decryptToken(token);
|
||||||
|
assertNotNull(tokenData);
|
||||||
|
assertEquals("testuser00003", tokenData.getUser());
|
||||||
|
assertFalse(tokenData.isValid());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = InvalidTokenException.class)
|
||||||
|
public void decryptInvalidToken() throws Exception {
|
||||||
|
TokenManager tokenManager = new TokenManager();
|
||||||
|
tokenManager.initialize();
|
||||||
|
SimpleTokenData sToken = new SimpleTokenData("testuser00003", 0, 1345455);
|
||||||
|
String token = tokenManager.encryptToken(sToken);
|
||||||
|
assertNotNull(token);
|
||||||
|
assertTrue("Token size too low",token.length()>300);
|
||||||
|
tokenManager.initialize();
|
||||||
|
tokenManager.decryptToken(token);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void decryptTokenWithDifferentAlgorithm() throws Exception {
|
||||||
|
TokenManager tokenManager = new TokenManager();
|
||||||
|
tokenManager.setAlgorithm("DES/ECB/PKCS5Padding");
|
||||||
|
tokenManager.initialize();
|
||||||
|
String token = tokenManager.encryptToken("testuser00005",2000);
|
||||||
|
assertNotNull(token);
|
||||||
|
assertTrue("Token size too low",token.length()>300);
|
||||||
|
TokenData tokenData = tokenManager.decryptToken(token);
|
||||||
|
assertNotNull(tokenData);
|
||||||
|
assertEquals("testuser00005", tokenData.getUser());
|
||||||
|
assertTrue(tokenData.isValid());
|
||||||
|
|
||||||
|
|
||||||
|
tokenManager.setAlgorithm("DES/ECB/NoPadding");
|
||||||
|
tokenManager.initialize();
|
||||||
|
token = tokenManager.encryptToken("testuser00006",2000);
|
||||||
|
assertNotNull(token);
|
||||||
|
assertTrue("Token size too low",token.length()>300);
|
||||||
|
tokenData = tokenManager.decryptToken(token);
|
||||||
|
assertNotNull(tokenData);
|
||||||
|
assertEquals("testuser00006", tokenData.getUser());
|
||||||
|
assertTrue(tokenData.isValid());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void nativeEncryption() throws EncryptionFailedException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidTokenException, BadPaddingException, IllegalBlockSizeException {
|
||||||
|
TokenManager tokenManager = new TokenManager();
|
||||||
|
tokenManager.setAlgorithm("DES/CBC/PKCS5Padding");
|
||||||
|
tokenManager.setKeySize(56);
|
||||||
|
tokenManager.initialize();
|
||||||
|
byte[] data = { 1, 5, 12, 18, 124, 44, 88, -28, -44};
|
||||||
|
byte[] token = tokenManager.doEncrypt(data);
|
||||||
|
assertNotNull(token);
|
||||||
|
byte[] tokenData = tokenManager.doDecrypt(token);
|
||||||
|
assertNotNull(tokenData);
|
||||||
|
assertArrayEquals(data, tokenData);
|
||||||
|
|
||||||
|
|
||||||
|
tokenManager.setAlgorithm("AES/CBC/NoPadding");
|
||||||
|
tokenManager.setKeySize(128);
|
||||||
|
tokenManager.initialize();
|
||||||
|
token = tokenManager.doEncrypt(data);
|
||||||
|
assertNotNull(token);
|
||||||
|
// Without padding the decrypted value is a multiple of the block size.
|
||||||
|
tokenData = Arrays.copyOf(tokenManager.doDecrypt(token), data.length);
|
||||||
|
assertNotNull(tokenData);
|
||||||
|
assertArrayEquals(data, tokenData);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -80,6 +80,14 @@ public class User
|
||||||
*/
|
*/
|
||||||
private String userManagerId;
|
private String userManagerId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* for request validation
|
||||||
|
*
|
||||||
|
* @since 2.2
|
||||||
|
*/
|
||||||
|
private String validationToken;
|
||||||
|
|
||||||
|
|
||||||
public User()
|
public User()
|
||||||
{
|
{
|
||||||
// no op
|
// no op
|
||||||
|
@ -272,6 +280,14 @@ public class User
|
||||||
this.userManagerId = userManagerId;
|
this.userManagerId = userManagerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getValidationToken() {
|
||||||
|
return validationToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValidationToken(String validationToken) {
|
||||||
|
this.validationToken = validationToken;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
|
|
|
@ -22,7 +22,9 @@ package org.apache.archiva.redback.rest.services;
|
||||||
import org.apache.archiva.redback.authentication.AuthenticationConstants;
|
import org.apache.archiva.redback.authentication.AuthenticationConstants;
|
||||||
import org.apache.archiva.redback.authentication.AuthenticationException;
|
import org.apache.archiva.redback.authentication.AuthenticationException;
|
||||||
import org.apache.archiva.redback.authentication.AuthenticationFailureCause;
|
import org.apache.archiva.redback.authentication.AuthenticationFailureCause;
|
||||||
|
import org.apache.archiva.redback.authentication.EncryptionFailedException;
|
||||||
import org.apache.archiva.redback.authentication.PasswordBasedAuthenticationDataSource;
|
import org.apache.archiva.redback.authentication.PasswordBasedAuthenticationDataSource;
|
||||||
|
import org.apache.archiva.redback.authentication.TokenManager;
|
||||||
import org.apache.archiva.redback.integration.filter.authentication.HttpAuthenticator;
|
import org.apache.archiva.redback.integration.filter.authentication.HttpAuthenticator;
|
||||||
import org.apache.archiva.redback.keys.AuthenticationKey;
|
import org.apache.archiva.redback.keys.AuthenticationKey;
|
||||||
import org.apache.archiva.redback.keys.KeyManager;
|
import org.apache.archiva.redback.keys.KeyManager;
|
||||||
|
@ -75,6 +77,9 @@ public class DefaultLoginService
|
||||||
@Context
|
@Context
|
||||||
private HttpServletRequest httpServletRequest;
|
private HttpServletRequest httpServletRequest;
|
||||||
|
|
||||||
|
// validation token lifetime: 3 hours
|
||||||
|
long tokenLifetime = 1000*3600*3;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public DefaultLoginService( SecuritySystem securitySystem,
|
public DefaultLoginService( SecuritySystem securitySystem,
|
||||||
@Named( "httpAuthenticator#basic" ) HttpAuthenticator httpAuthenticator )
|
@Named( "httpAuthenticator#basic" ) HttpAuthenticator httpAuthenticator )
|
||||||
|
@ -149,6 +154,13 @@ public class DefaultLoginService
|
||||||
}
|
}
|
||||||
User restUser = buildRestUser( user );
|
User restUser = buildRestUser( user );
|
||||||
restUser.setReadOnly( securitySystem.userManagerReadOnly() );
|
restUser.setReadOnly( securitySystem.userManagerReadOnly() );
|
||||||
|
// validationToken only set during login
|
||||||
|
try {
|
||||||
|
restUser.setValidationToken(securitySystem.getTokenManager().encryptToken(user.getUsername(), tokenLifetime));
|
||||||
|
} catch (EncryptionFailedException e) {
|
||||||
|
log.error("Validation token could not be created "+e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
// here create an http session
|
// here create an http session
|
||||||
httpAuthenticator.authenticate( authDataSource, httpServletRequest.getSession( true ) );
|
httpAuthenticator.authenticate( authDataSource, httpServletRequest.getSession( true ) );
|
||||||
return restUser;
|
return restUser;
|
||||||
|
|
|
@ -19,10 +19,12 @@ package org.apache.archiva.redback.system;
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import com.sun.javafx.fxml.expression.Expression;
|
||||||
import org.apache.archiva.redback.authentication.AuthenticationDataSource;
|
import org.apache.archiva.redback.authentication.AuthenticationDataSource;
|
||||||
import org.apache.archiva.redback.authentication.AuthenticationException;
|
import org.apache.archiva.redback.authentication.AuthenticationException;
|
||||||
import org.apache.archiva.redback.authentication.AuthenticationManager;
|
import org.apache.archiva.redback.authentication.AuthenticationManager;
|
||||||
import org.apache.archiva.redback.authentication.AuthenticationResult;
|
import org.apache.archiva.redback.authentication.AuthenticationResult;
|
||||||
|
import org.apache.archiva.redback.authentication.TokenManager;
|
||||||
import org.apache.archiva.redback.authorization.AuthorizationDataSource;
|
import org.apache.archiva.redback.authorization.AuthorizationDataSource;
|
||||||
import org.apache.archiva.redback.authorization.AuthorizationException;
|
import org.apache.archiva.redback.authorization.AuthorizationException;
|
||||||
import org.apache.archiva.redback.authorization.AuthorizationResult;
|
import org.apache.archiva.redback.authorization.AuthorizationResult;
|
||||||
|
@ -68,6 +70,10 @@ public class DefaultSecuritySystem
|
||||||
@Named( value = "keyManager#cached" )
|
@Named( value = "keyManager#cached" )
|
||||||
private KeyManager keyManager;
|
private KeyManager keyManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
@Named( value = "tokenManager#default")
|
||||||
|
private TokenManager tokenManager;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private UserSecurityPolicy policy;
|
private UserSecurityPolicy policy;
|
||||||
|
|
||||||
|
@ -305,4 +311,13 @@ public class DefaultSecuritySystem
|
||||||
return userManager.isReadOnly();
|
return userManager.isReadOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TokenManager getTokenManager() {
|
||||||
|
return tokenManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTokenManager(TokenManager tokenManager) {
|
||||||
|
this.tokenManager = tokenManager;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.apache.archiva.redback.system;
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import org.apache.archiva.redback.authentication.TokenManager;
|
||||||
import org.apache.archiva.redback.policy.AccountLockedException;
|
import org.apache.archiva.redback.policy.AccountLockedException;
|
||||||
import org.apache.archiva.redback.policy.MustChangePasswordException;
|
import org.apache.archiva.redback.policy.MustChangePasswordException;
|
||||||
import org.apache.archiva.redback.policy.UserSecurityPolicy;
|
import org.apache.archiva.redback.policy.UserSecurityPolicy;
|
||||||
|
@ -111,5 +112,12 @@ public interface SecuritySystem
|
||||||
* @since 2.1
|
* @since 2.1
|
||||||
*/
|
*/
|
||||||
boolean userManagerReadOnly();
|
boolean userManagerReadOnly();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the token manager implementation.
|
||||||
|
*
|
||||||
|
* @since 2.2
|
||||||
|
*/
|
||||||
|
TokenManager getTokenManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue