[SEBA-593] Splitting aaa app in implementation and api bundles

Change-Id: Ib81650be0695258bdd1f4cd6131f2206760e0da6
diff --git a/app/src/main/java/org/opencord/aaa/impl/StateMachine.java b/app/src/main/java/org/opencord/aaa/impl/StateMachine.java
new file mode 100644
index 0000000..f9e5f25
--- /dev/null
+++ b/app/src/main/java/org/opencord/aaa/impl/StateMachine.java
@@ -0,0 +1,572 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed 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.opencord.aaa.impl;
+
+import com.google.common.collect.Maps;
+import org.onlab.packet.MacAddress;
+import org.onosproject.net.ConnectPoint;
+import org.opencord.aaa.AuthenticationEvent;
+import org.opencord.aaa.StateMachineDelegate;
+import org.slf4j.Logger;
+
+import java.util.Map;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * AAA Finite State Machine.
+ */
+
+class StateMachine {
+    //INDEX to identify the state in the transition table
+    static final int STATE_IDLE = 0;
+    static final int STATE_STARTED = 1;
+    static final int STATE_PENDING = 2;
+    static final int STATE_AUTHORIZED = 3;
+    static final int STATE_UNAUTHORIZED = 4;
+
+    //INDEX to identify the transition in the transition table
+    static final int TRANSITION_START = 0; // --> started
+    static final int TRANSITION_REQUEST_ACCESS = 1;
+    static final int TRANSITION_AUTHORIZE_ACCESS = 2;
+    static final int TRANSITION_DENY_ACCESS = 3;
+    static final int TRANSITION_LOGOFF = 4;
+
+    private static int identifier = -1;
+    private byte challengeIdentifier;
+    private byte[] challengeState;
+    private byte[] username;
+    private byte[] requestAuthenticator;
+
+    // Supplicant connectivity info
+    private ConnectPoint supplicantConnectpoint;
+    private MacAddress supplicantAddress;
+    private short vlanId;
+    private byte priorityCode;
+
+    private String sessionId = null;
+
+    private final Logger log = getLogger(getClass());
+
+
+    private State[] states = {
+            new Idle(), new Started(), new Pending(), new Authorized(), new Unauthorized()
+    };
+
+
+    //State transition table
+    /*
+
+                state       IDLE    |   STARTED         |   PENDING         |   AUTHORIZED  |   UNAUTHORIZED
+             ////
+       input
+       ----------------------------------------------------------------------------------------------------
+
+       START                STARTED |   _               |   _               |   STARTED     |   STARTED
+
+       REQUEST_ACCESS       _       |   PENDING         |   _               |   _           |   _
+
+       AUTHORIZE_ACCESS     _       |   _               |   AUTHORIZED      |   _           |   _
+
+       DENY_ACCESS          _       |   -               |   UNAUTHORIZED    |   _           |   _
+
+       LOGOFF               _       |   _               |   _               |   IDLE        |   IDLE
+     */
+
+    private int[] idleTransition =
+            {STATE_STARTED, STATE_IDLE, STATE_IDLE, STATE_IDLE, STATE_IDLE};
+    private int[] startedTransition =
+            {STATE_STARTED, STATE_PENDING, STATE_STARTED, STATE_STARTED, STATE_STARTED};
+    private int[] pendingTransition =
+            {STATE_PENDING, STATE_PENDING, STATE_AUTHORIZED, STATE_UNAUTHORIZED, STATE_PENDING};
+    private int[] authorizedTransition =
+            {STATE_STARTED, STATE_AUTHORIZED, STATE_AUTHORIZED, STATE_AUTHORIZED, STATE_IDLE};
+    private int[] unauthorizedTransition =
+            {STATE_STARTED, STATE_UNAUTHORIZED, STATE_UNAUTHORIZED, STATE_UNAUTHORIZED, STATE_IDLE};
+
+    //THE TRANSITION TABLE
+    private int[][] transition =
+            {idleTransition, startedTransition, pendingTransition, authorizedTransition,
+                    unauthorizedTransition};
+
+    private int currentState = STATE_IDLE;
+
+    // Maps of state machines. Each state machine is represented by an
+    // unique identifier on the switch: dpid + port number
+    private static Map<String, StateMachine> sessionIdMap;
+    private static Map<Integer, StateMachine> identifierMap;
+
+    private static StateMachineDelegate delegate;
+
+    public static void initializeMaps() {
+        sessionIdMap = Maps.newConcurrentMap();
+        identifierMap = Maps.newConcurrentMap();
+        identifier = -1;
+    }
+
+    public static void destroyMaps() {
+        sessionIdMap = null;
+        identifierMap = null;
+    }
+
+    public static void setDelegate(StateMachineDelegate delegate) {
+        StateMachine.delegate = delegate;
+    }
+
+    public static void unsetDelegate(StateMachineDelegate delegate) {
+        if (StateMachine.delegate == delegate) {
+            StateMachine.delegate = null;
+        }
+    }
+
+    public static Map<String, StateMachine> sessionIdMap() {
+        return sessionIdMap;
+    }
+
+    public static StateMachine lookupStateMachineById(byte identifier) {
+        return identifierMap.get((int) identifier);
+    }
+
+    public static StateMachine lookupStateMachineBySessionId(String sessionId) {
+        return sessionIdMap.get(sessionId);
+    }
+
+    public static void deleteStateMachineMapping(StateMachine machine) {
+        identifierMap.entrySet().removeIf(e -> e.getValue().equals(machine));
+    }
+
+    /**
+     * Deletes authentication state machine records for a given MAC address.
+     * @param mac mac address of the suppliant who's state machine should be removed
+     */
+    public static void deleteByMac(MacAddress mac) {
+
+        // Walk the map from session IDs to state machines looking for a MAC match
+        for (Map.Entry<String, StateMachine> e : sessionIdMap.entrySet()) {
+
+            // If a MAC match is found then delete the entry from the session ID
+            // and identifier map as well as call delete identifier to clean up
+            // the identifier bit set.
+            if (e.getValue() != null && e.getValue().supplicantAddress != null &&
+                   e.getValue().supplicantAddress.equals(mac)) {
+                sessionIdMap.remove(e.getValue().sessionId);
+                if (e.getValue().identifier != -1) {
+                    deleteStateMachineMapping(e.getValue());
+                }
+                break;
+            }
+        }
+    }
+
+    /**
+     * Creates a new StateMachine with the given session ID.
+     *
+     * @param sessionId session Id represented by the switch dpid +  port number
+     */
+    public StateMachine(String sessionId) {
+        log.info("Creating a new state machine for {}", sessionId);
+        this.sessionId = sessionId;
+        sessionIdMap.put(sessionId, this);
+    }
+
+    /**
+     * Gets the connect point for the supplicant side.
+     *
+     * @return supplicant connect point
+     */
+    public ConnectPoint supplicantConnectpoint() {
+        return supplicantConnectpoint;
+    }
+
+    /**
+     * Sets the supplicant side connect point.
+     *
+     * @param supplicantConnectpoint supplicant select point.
+     */
+    public void setSupplicantConnectpoint(ConnectPoint supplicantConnectpoint) {
+        this.supplicantConnectpoint = supplicantConnectpoint;
+    }
+
+    /**
+     * Gets the MAC address of the supplicant.
+     *
+     * @return supplicant MAC address
+     */
+    public MacAddress supplicantAddress() {
+        return supplicantAddress;
+    }
+
+    /**
+     * Sets the supplicant MAC address.
+     *
+     * @param supplicantAddress new supplicant MAC address
+     */
+    public void setSupplicantAddress(MacAddress supplicantAddress) {
+        this.supplicantAddress = supplicantAddress;
+    }
+
+    /**
+     * Gets the client's Vlan ID.
+     *
+     * @return client vlan ID
+     */
+    public short vlanId() {
+        return vlanId;
+    }
+
+    /**
+     * Sets the client's vlan ID.
+     *
+     * @param vlanId new client vlan ID
+     */
+    public void setVlanId(short vlanId) {
+        this.vlanId = vlanId;
+    }
+
+    /**
+     * Gets the client's priority Code.
+     *
+     * @return client Priority code
+     */
+    public byte priorityCode() {
+        return priorityCode;
+    }
+
+    /**
+     * Sets the client's priority Code.
+     *
+     * @param priorityCode new client priority Code
+     */
+    public void setPriorityCode(byte priorityCode) {
+        this.priorityCode = priorityCode;
+    }
+
+    /**
+     * Gets the client id that is requesting for access.
+     *
+     * @return The client id.
+     */
+    public String sessionId() {
+        return this.sessionId;
+    }
+
+    /**
+     * Set the challenge identifier and the state issued by the RADIUS.
+     *
+     * @param challengeIdentifier The challenge identifier set into the EAP packet from the RADIUS message.
+     * @param challengeState      The challenge state from the RADIUS.
+     */
+    protected void setChallengeInfo(byte challengeIdentifier, byte[] challengeState) {
+        this.challengeIdentifier = challengeIdentifier;
+        this.challengeState = challengeState;
+    }
+
+    /**
+     * Set the challenge identifier issued by the RADIUS on the access challenge request.
+     *
+     * @param challengeIdentifier The challenge identifier set into the EAP packet from the RADIUS message.
+     */
+    protected void setChallengeIdentifier(byte challengeIdentifier) {
+        log.info("Set Challenge Identifier to {}", challengeIdentifier);
+        this.challengeIdentifier = challengeIdentifier;
+    }
+
+    /**
+     * Gets the challenge EAP identifier set by the RADIUS.
+     *
+     * @return The challenge EAP identifier.
+     */
+    protected byte challengeIdentifier() {
+        return this.challengeIdentifier;
+    }
+
+
+    /**
+     * Set the challenge state info issued by the RADIUS.
+     *
+     * @param challengeState The challenge state from the RADIUS.
+     */
+    protected void setChallengeState(byte[] challengeState) {
+        log.info("Set Challenge State");
+        this.challengeState = challengeState;
+    }
+
+    /**
+     * Gets the challenge state set by the RADIUS.
+     *
+     * @return The challenge state.
+     */
+    protected byte[] challengeState() {
+        return this.challengeState;
+    }
+
+    /**
+     * Set the username.
+     *
+     * @param username The username sent to the RADIUS upon access request.
+     */
+    protected void setUsername(byte[] username) {
+        this.username = username;
+    }
+
+    /**
+     * Gets the username.
+     *
+     * @return The requestAuthenticator.
+     */
+    protected byte[] requestAuthenticator() {
+        return this.requestAuthenticator;
+    }
+
+    /**
+     * Sets the authenticator.
+     *
+     * @param authenticator The username sent to the RADIUS upon access request.
+     */
+    protected void setRequestAuthenticator(byte[] authenticator) {
+        this.requestAuthenticator = authenticator;
+    }
+
+
+    /**
+     * Gets the username.
+     *
+     * @return The username.
+     */
+    protected byte[] username() {
+        return this.username;
+    }
+
+    /**
+     * Return the identifier of the state machine.
+     *
+     * @return The state machine identifier.
+     */
+    public synchronized byte identifier() {
+        identifier = (identifier + 1) % 255;
+        identifierMap.put(identifier, this);
+        return (byte) identifier;
+    }
+
+    /**
+     * Move to the next state.
+     *
+     * @param msg message
+     */
+    private void next(int msg) {
+        currentState = transition[currentState][msg];
+        log.info("Current State " + currentState);
+    }
+
+    /**
+     * Client has requested the start action to allow network access.
+     *
+     * @throws StateMachineException if authentication protocol is violated
+     */
+    public void start() throws StateMachineException {
+        states[currentState].start();
+
+        delegate.notify(new AuthenticationEvent(
+                AuthenticationEvent.Type.STARTED, supplicantConnectpoint));
+
+        //move to the next state
+        next(TRANSITION_START);
+        identifier = this.identifier();
+    }
+
+    /**
+     * An Identification information has been sent by the supplicant.
+     * Move to the next state if possible.
+     *
+     * @throws StateMachineException if authentication protocol is violated
+     */
+    public void requestAccess() throws StateMachineException {
+        states[currentState].requestAccess();
+
+        delegate.notify(new AuthenticationEvent(
+                AuthenticationEvent.Type.REQUESTED, supplicantConnectpoint));
+
+        //move to the next state
+        next(TRANSITION_REQUEST_ACCESS);
+    }
+
+    /**
+     * RADIUS has accepted the identification.
+     * Move to the next state if possible.
+     *
+     * @throws StateMachineException if authentication protocol is violated
+     */
+    public void authorizeAccess() throws StateMachineException {
+        states[currentState].radiusAccepted();
+        //move to the next state
+        next(TRANSITION_AUTHORIZE_ACCESS);
+
+        delegate.notify(new AuthenticationEvent(
+                AuthenticationEvent.Type.APPROVED, supplicantConnectpoint));
+
+        // Clear mapping
+        deleteStateMachineMapping(this);
+    }
+
+    /**
+     * RADIUS has denied the identification.
+     * Move to the next state if possible.
+     *
+     * @throws StateMachineException if authentication protocol is violated
+     */
+    public void denyAccess() throws StateMachineException {
+        states[currentState].radiusDenied();
+        //move to the next state
+        next(TRANSITION_DENY_ACCESS);
+
+        delegate.notify(new AuthenticationEvent(
+                AuthenticationEvent.Type.DENIED, supplicantConnectpoint));
+
+        // Clear mappings
+        deleteStateMachineMapping(this);
+    }
+
+    /**
+     * Logoff request has been requested.
+     * Move to the next state if possible.
+     *
+     * @throws StateMachineException if authentication protocol is violated
+     */
+    public void logoff() throws StateMachineException {
+        states[currentState].logoff();
+        //move to the next state
+        next(TRANSITION_LOGOFF);
+    }
+
+    /**
+     * Gets the current state.
+     *
+     * @return The current state. Could be STATE_IDLE, STATE_STARTED, STATE_PENDING, STATE_AUTHORIZED,
+     * STATE_UNAUTHORIZED.
+     */
+    public int state() {
+        return currentState;
+    }
+
+    @Override
+    public String toString() {
+        return ("sessionId: " + this.sessionId) + "\t" + ("identifier: " + this.identifier) + "\t" +
+                ("state: " + this.currentState);
+    }
+
+    abstract class State {
+        private final Logger log = getLogger(getClass());
+
+        private String name = "State";
+
+        public void start() throws StateMachineInvalidTransitionException {
+            log.warn("START transition from this state is not allowed.");
+        }
+
+        public void requestAccess() throws StateMachineInvalidTransitionException {
+            log.warn("REQUEST ACCESS transition from this state is not allowed.");
+        }
+
+        public void radiusAccepted() throws StateMachineInvalidTransitionException {
+            log.warn("AUTHORIZE ACCESS transition from this state is not allowed.");
+        }
+
+        public void radiusDenied() throws StateMachineInvalidTransitionException {
+            log.warn("DENY ACCESS transition from this state is not allowed.");
+        }
+
+        public void logoff() throws StateMachineInvalidTransitionException {
+            log.warn("LOGOFF transition from this state is not allowed.");
+        }
+    }
+
+    /**
+     * Idle state: supplicant is logged of from the network.
+     */
+    class Idle extends State {
+        private final Logger log = getLogger(getClass());
+        private String name = "IDLE_STATE";
+
+        public void start() {
+            log.info("Moving from IDLE state to STARTED state.");
+        }
+    }
+
+    /**
+     * Started state: supplicant has entered the network and informed the authenticator.
+     */
+    class Started extends State {
+        private final Logger log = getLogger(getClass());
+        private String name = "STARTED_STATE";
+
+        public void requestAccess() {
+            log.info("Moving from STARTED state to PENDING state.");
+        }
+    }
+
+    /**
+     * Pending state: supplicant has been identified by the authenticator but has not access yet.
+     */
+    class Pending extends State {
+        private final Logger log = getLogger(getClass());
+        private String name = "PENDING_STATE";
+
+        public void radiusAccepted() {
+            log.info("Moving from PENDING state to AUTHORIZED state.");
+        }
+
+        public void radiusDenied() {
+            log.info("Moving from PENDING state to UNAUTHORIZED state.");
+        }
+    }
+
+    /**
+     * Authorized state: supplicant port has been accepted, access is granted.
+     */
+    class Authorized extends State {
+        private final Logger log = getLogger(getClass());
+        private String name = "AUTHORIZED_STATE";
+
+        public void start() {
+            log.info("Moving from AUTHORIZED state to STARTED state.");
+        }
+
+        public void logoff() {
+
+            log.info("Moving from AUTHORIZED state to IDLE state.");
+        }
+    }
+
+    /**
+     * Unauthorized state: supplicant port has been rejected, access is denied.
+     */
+    class Unauthorized extends State {
+        private final Logger log = getLogger(getClass());
+        private String name = "UNAUTHORIZED_STATE";
+
+        public void start() {
+            log.info("Moving from UNAUTHORIZED state to STARTED state.");
+        }
+
+        public void logoff() {
+            log.info("Moving from UNAUTHORIZED state to IDLE state.");
+        }
+    }
+
+
+}