001package org.apache.archiva.redback.policy.encoders;
002
003/*
004 * Copyright 2001-2006 The Apache Software Foundation.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 *      http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019import org.apache.archiva.redback.policy.PasswordEncoder;
020import org.apache.archiva.redback.policy.PasswordEncodingException;
021import org.apache.archiva.redback.users.Messages;
022import org.apache.commons.codec.binary.Base64;
023import org.apache.commons.lang3.StringUtils;
024
025import java.nio.charset.Charset;
026import java.security.MessageDigest;
027import java.security.NoSuchAlgorithmException;
028
029/**
030 * Abstract Password Encoder that uses the {@link MessageDigest} from JAAS.
031 *
032 * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
033 */
034public class AbstractJAASPasswordEncoder
035    implements PasswordEncoder
036{
037    private String algorithm;
038
039    private Object systemSalt;
040
041    public AbstractJAASPasswordEncoder( String algorithm )
042    {
043        this.algorithm = algorithm;
044    }
045
046    public void setSystemSalt( Object salt )
047    {
048        this.systemSalt = salt;
049    }
050
051    public String encodePassword( String rawPass, Object salt )
052    {
053        if ( rawPass == null )
054        {
055            throw new IllegalArgumentException( "rawPass parameter cannot be null." );
056        }
057
058        MessageDigest md = null;
059        try
060        {
061            md = MessageDigest.getInstance( this.algorithm );
062            String precode = rawPass;
063
064            // Only checking for null, not using StringUtils.isNotEmpty() as
065            // whitespace can make up a valid salt. 
066            if ( salt != null )
067            {
068                // Conforming to acegi password encoding standards for compatibility
069                precode += "{" + salt + "}";
070            }
071            md.update( precode.getBytes( Charset.forName( "UTF-8" ) ) );
072
073            byte raw[] = md.digest();
074            Base64 base64 = new Base64( 0, new byte[0] );
075            return ( base64.encodeToString( raw ) );
076        }
077        catch ( NoSuchAlgorithmException e )
078        {
079            throw new PasswordEncodingException(
080                Messages.getString( "password.encoder.no.such.algoritm", this.algorithm ), e );
081        }
082    }
083
084    public boolean isPasswordValid( String encPass, String rawPass, Object salt )
085    {
086        if ( StringUtils.isEmpty( encPass ) )
087        {
088            // TODO: Throw exception?
089            return false;
090        }
091
092        // PLXREDBACK-36 Commented out because a user with an empty password can't login due to the checking.
093        // Empty password checking can also be achieve by turning on MustHavePasswordRule.
094        //if ( StringUtils.isEmpty( rawPass ) )
095        //{
096        //    TODO: Throw exception?
097        //    return false;
098        //}
099
100        String testPass = encodePassword( rawPass, salt );
101        return ( encPass.equals( testPass ) );
102    }
103
104    public String encodePassword( String rawPass )
105    {
106        return encodePassword( rawPass, this.systemSalt );
107    }
108
109    public boolean isPasswordValid( String encPass, String rawPass )
110    {
111        return isPasswordValid( encPass, rawPass, this.systemSalt );
112    }
113
114}