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 *     https://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                
080                if ( LOG.isDebugEnabled() )
081                {
082                    LOG.debug( I18n.msg( I18n.MSG_04153_SENDING_NAME_IN_CALLBACK, name ) );
083                }
084                
085                ncb.setName( name );
086            }
087            else if ( cb instanceof PasswordCallback )
088            {
089                PasswordCallback pcb = ( PasswordCallback ) cb;
090
091                if ( LOG.isDebugEnabled() )
092                {
093                    LOG.debug( I18n.msg( I18n.MSG_04154_SENDING_CREDS_IN_CALLBACK ) );
094                }
095                
096                pcb.setPassword( Strings.utf8ToString( saslReq.getCredentials() ).toCharArray() );
097            }
098            else if ( cb instanceof RealmCallback )
099            {
100                RealmCallback rcb = ( RealmCallback ) cb;
101
102                if ( saslReq.getRealmName() != null )
103                {
104                    if ( LOG.isDebugEnabled() )
105                    {
106                        LOG.debug( I18n.msg( I18n.MSG_04155_SENDING_USER_REALM_IN_CALLBACK, saslReq.getRealmName() ) );
107                    }
108                    
109                    rcb.setText( saslReq.getRealmName() );
110                }
111                else
112                {
113                    if ( LOG.isDebugEnabled() )
114                    {
115                        LOG.debug( I18n.msg( I18n.MSG_04156_SENDING_DEFAULT_REALM_IN_CALLBACK, rcb.getDefaultText() ) );
116                    }
117                    
118                    rcb.setText( rcb.getDefaultText() );
119                }
120            }
121            else if ( cb instanceof RealmChoiceCallback )
122            {
123                RealmChoiceCallback rccb = ( RealmChoiceCallback ) cb;
124
125                boolean foundRealmName = false;
126
127                String[] realmNames = rccb.getChoices();
128                for ( int i = 0; i < realmNames.length; i++ )
129                {
130                    String realmName = realmNames[i];
131                    if ( realmName.equals( saslReq.getRealmName() ) )
132                    {
133                        foundRealmName = true;
134
135                        if ( LOG.isDebugEnabled() )
136                        {
137                            LOG.debug( I18n.msg( I18n.MSG_04157_SENDING_USER_REALM_IN_CALLBACK, realmName ) );
138                        }
139                        
140                        rccb.setSelectedIndex( i );
141                        break;
142                    }
143                }
144
145                if ( !foundRealmName )
146                {
147                    throw new IOException(
148                        I18n.err( I18n.ERR_04171_CANNOT_PARSE_MATCHED_DN,
149                            saslReq.getRealmName(), getRealmNamesAsString( realmNames ) ) );
150                }
151            }
152        }
153    }
154
155
156    /**
157     * Gets the realm names as a string.
158     *
159     * @param realmNames the array of realm names
160     * @return  the realm names as a string
161     */
162    private String getRealmNamesAsString( String[] realmNames )
163    {
164        StringBuilder sb = new StringBuilder();
165
166        if ( ( realmNames != null ) && ( realmNames.length > 0 ) )
167        {
168            for ( String realmName : realmNames )
169            {
170                sb.append( realmName );
171                sb.append( ',' );
172            }
173            sb.deleteCharAt( sb.length() - 1 );
174        }
175
176        return sb.toString();
177    }
178}