| /* |
| * 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. |
| */ |
| package org.apache.qpid.protonj2.engine.impl.sasl; |
| |
| import org.apache.qpid.protonj2.engine.EngineHandler; |
| import org.apache.qpid.protonj2.engine.EngineHandlerContext; |
| import org.apache.qpid.protonj2.engine.EngineState; |
| import org.apache.qpid.protonj2.engine.HeaderEnvelope; |
| import org.apache.qpid.protonj2.engine.IncomingAMQPEnvelope; |
| import org.apache.qpid.protonj2.engine.OutgoingAMQPEnvelope; |
| import org.apache.qpid.protonj2.engine.SASLEnvelope; |
| import org.apache.qpid.protonj2.engine.exceptions.ProtocolViolationException; |
| import org.apache.qpid.protonj2.engine.impl.ProtonEngine; |
| import org.apache.qpid.protonj2.engine.impl.ProtonEngineNoOpSaslDriver; |
| |
| /** |
| * Base class used for common portions of the SASL processing pipeline. |
| */ |
| public final class ProtonSaslHandler implements EngineHandler { |
| |
| private EngineHandlerContext context; |
| private ProtonEngineSaslDriver driver; |
| private ProtonEngine engine; |
| private ProtonSaslContext saslContext; |
| |
| /** |
| * @return true if the SASL exchange has finished (succeed or failed). |
| */ |
| public boolean isDone() { |
| return saslContext != null ? saslContext.isDone() : false; |
| } |
| |
| @Override |
| public void handlerAdded(EngineHandlerContext context) { |
| this.engine = (ProtonEngine) context.engine(); |
| this.driver = new ProtonEngineSaslDriver(engine, this); |
| this.context = context; |
| |
| engine.registerSaslDriver(driver); |
| } |
| |
| @Override |
| public void handlerRemoved(EngineHandlerContext context) { |
| this.driver = null; |
| this.saslContext = null; |
| this.engine = null; |
| this.context = null; |
| |
| // If the engine wasn't started then it is okay to remove this handler otherwise |
| // we would only be removed from the pipeline on completion of SASL negotiations |
| // and the driver must remain to convey the outcome. |
| if (context.engine().state() == EngineState.IDLE) { |
| ((ProtonEngine) context.engine()).registerSaslDriver(ProtonEngineNoOpSaslDriver.INSTANCE); |
| } |
| } |
| |
| @Override |
| public void engineStarting(EngineHandlerContext context) { |
| driver.handleEngineStarting(engine); |
| } |
| |
| @Override |
| public void handleRead(EngineHandlerContext context, HeaderEnvelope header) { |
| if (isDone()) { |
| context.fireRead(header); |
| } else { |
| // Default to server if application has not configured one way or the other. |
| saslContext = driver.context(); |
| if (saslContext == null) { |
| saslContext = driver.server(); |
| } |
| |
| header.invoke(saslContext.headerReadContext(), context); |
| } |
| } |
| |
| @Override |
| public void handleRead(EngineHandlerContext context, SASLEnvelope frame) { |
| if (isDone()) { |
| throw new ProtocolViolationException("Unexpected SASL Frame: SASL processing has already completed"); |
| } else { |
| frame.invoke(safeGetSaslContext().saslReadContext(), context); |
| } |
| } |
| |
| @Override |
| public void handleRead(EngineHandlerContext context, IncomingAMQPEnvelope frame) { |
| if (isDone()) { |
| context.fireRead(frame); |
| } else { |
| throw new ProtocolViolationException("Unexpected AMQP Frame: SASL processing not yet completed"); |
| } |
| } |
| |
| @Override |
| public void handleWrite(EngineHandlerContext context, HeaderEnvelope frame) { |
| if (isDone()) { |
| context.fireWrite(frame); |
| } else { |
| // Default to client if application has not configured one way or the other. |
| saslContext = driver.context(); |
| if (saslContext == null) { |
| saslContext = driver.client(); |
| } |
| |
| // Delegate write to the SASL Context in use to allow for state updates. |
| frame.invoke(saslContext.headerWriteContext(), context); |
| } |
| } |
| |
| @Override |
| public void handleWrite(EngineHandlerContext context, OutgoingAMQPEnvelope frame) { |
| if (isDone()) { |
| context.fireWrite(frame); |
| } else { |
| throw new ProtocolViolationException("Unexpected AMQP Performative: SASL processing not yet completed"); |
| } |
| } |
| |
| @Override |
| public void handleWrite(EngineHandlerContext context, SASLEnvelope frame) { |
| if (isDone()) { |
| throw new ProtocolViolationException("Unexpected SASL Performative: SASL processing has yet completed"); |
| } else { |
| // Delegate to the SASL Context to allow state tracking to be maintained. |
| frame.invoke(safeGetSaslContext().saslWriteContext(), context); |
| } |
| } |
| |
| //----- Internal implementation API and helper methods |
| |
| ProtonEngine engine() { |
| return engine; |
| } |
| |
| EngineHandlerContext context() { |
| return context; |
| } |
| |
| private ProtonSaslContext safeGetSaslContext() { |
| if (saslContext != null) { |
| return saslContext; |
| } |
| |
| throw new IllegalStateException("Cannot process incoming SASL performative, driver not yet initialized"); |
| } |
| } |