001/*
002 *   Licensed to the Apache Software Foundation (ASF) under one
003 *   or more contributor license agreements.  See the NOTICE file
004 *   distributed with this work for additional information
005 *   regarding copyright ownership.  The ASF licenses this file
006 *   to you under the Apache License, Version 2.0 (the
007 *   "License"); you may not use this file except in compliance
008 *   with the License.  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,
013 *   software distributed under the License is distributed on an
014 *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *   KIND, either express or implied.  See the License for the
016 *   specific language governing permissions and limitations
017 *   under the License.
018 *
019 */
020
021package org.apache.directory.ldap.client.api.callback;
022
023
024import java.io.IOException;
025
026import javax.security.auth.callback.Callback;
027import javax.security.auth.callback.CallbackHandler;
028import javax.security.auth.callback.NameCallback;
029import javax.security.auth.callback.PasswordCallback;
030import javax.security.auth.callback.UnsupportedCallbackException;
031import javax.security.sasl.RealmCallback;
032import javax.security.sasl.RealmChoiceCallback;
033
034import org.apache.directory.api.i18n.I18n;
035import org.apache.directory.api.util.Strings;
036import org.apache.directory.ldap.client.api.SaslRequest;
037import org.slf4j.Logger;
038import org.slf4j.LoggerFactory;
039
040
041/**
042 * The CallbackHandler implementation used by the LdapConnection during SASL mechanism based bind operations.
043 *
044 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
045 */
046public class SaslCallbackHandler implements CallbackHandler
047{
048    /** The sasl request. */
049    private SaslRequest saslReq;
050
051    /** The logger. */
052    private static final Logger LOG = LoggerFactory.getLogger( SaslCallbackHandler.class );
053
054
055    /**
056     * Instantiates a new SASL callback handler.
057     *
058     * @param saslReq the SASL request
059     */
060    public SaslCallbackHandler( SaslRequest saslReq )
061    {
062        this.saslReq = saslReq;
063    }
064
065
066    /**
067     * {@inheritDoc}
068     */
069    @Override
070    public void handle( Callback[] callbacks ) throws IOException, UnsupportedCallbackException
071    {
072        for ( Callback cb : callbacks )
073        {
074            if ( cb instanceof NameCallback )
075            {
076                NameCallback ncb = ( NameCallback ) cb;
077
078                String name = saslReq.getUsername();
079                LOG.debug( "sending name {} in the NameCallback", name );
080                ncb.setName( name );
081            }
082            else if ( cb instanceof PasswordCallback )
083            {
084                PasswordCallback pcb = ( PasswordCallback ) cb;
085
086                LOG.debug( "sending credentials in the PasswordCallback" );
087                pcb.setPassword( Strings.utf8ToString( saslReq.getCredentials() ).toCharArray() );
088            }
089            else if ( cb instanceof RealmCallback )
090            {
091                RealmCallback rcb = ( RealmCallback ) cb;
092
093                if ( saslReq.getRealmName() != null )
094                {
095                    LOG.debug( "sending the user specified realm value {} in the RealmCallback", saslReq.getRealmName() );
096                    rcb.setText( saslReq.getRealmName() );
097                }
098                else
099                {
100                    LOG.debug(
101                        "No user specified relam value, sending the default realm value {} in the RealmCallback",
102                        rcb.getDefaultText() );
103                    rcb.setText( rcb.getDefaultText() );
104                }
105            }
106            else if ( cb instanceof RealmChoiceCallback )
107            {
108                RealmChoiceCallback rccb = ( RealmChoiceCallback ) cb;
109
110                boolean foundRealmName = false;
111
112                String[] realmNames = rccb.getChoices();
113                for ( int i = 0; i < realmNames.length; i++ )
114                {
115                    String realmName = realmNames[i];
116                    if ( realmName.equals( saslReq.getRealmName() ) )
117                    {
118                        foundRealmName = true;
119
120                        LOG.debug( "sending the user specified realm value {} in the RealmChoiceCallback", realmName );
121                        rccb.setSelectedIndex( i );
122                        break;
123                    }
124                }
125
126                if ( !foundRealmName )
127                {
128                    throw new IOException(
129                        I18n.format(
130                            "Cannot match ''java.naming.security.sasl.realm'' property value ''{0}'' with choices ''{1}'' in RealmChoiceCallback.",
131                            saslReq.getRealmName(), getRealmNamesAsString( realmNames ) ) );
132                }
133            }
134        }
135    }
136
137
138    /**
139     * Gets the realm names as a string.
140     *
141     * @param realmNames the array of realm names
142     * @return  the realm names as a string
143     */
144    private String getRealmNamesAsString( String[] realmNames )
145    {
146        StringBuilder sb = new StringBuilder();
147
148        if ( ( realmNames != null ) && ( realmNames.length > 0 ) )
149        {
150            for ( String realmName : realmNames )
151            {
152                sb.append( realmName );
153                sb.append( ',' );
154            }
155            sb.deleteCharAt( sb.length() - 1 );
156        }
157
158        return sb.toString();
159    }
160}