Flatten callhome-provider
[netconf.git] / apps / callhome-provider / src / main / java / org / opendaylight / netconf / topology / callhome / CallHomeTlsSessionContextManager.java
1 /*
2  * Copyright (c) 2023 PANTHEON.tech s.r.o. and others. All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.netconf.topology.callhome;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.util.concurrent.SettableFuture;
13 import io.netty.channel.Channel;
14 import io.netty.handler.ssl.SslHandler;
15 import java.security.PublicKey;
16 import javax.net.ssl.SSLPeerUnverifiedException;
17 import org.opendaylight.netconf.client.SimpleNetconfClientSessionListener;
18 import org.slf4j.Logger;
19 import org.slf4j.LoggerFactory;
20
21 public class CallHomeTlsSessionContextManager extends AbstractCallHomeSessionContextManager<CallHomeTlsSessionContext> {
22     private static final Logger LOG = LoggerFactory.getLogger(CallHomeTlsSessionContextManager.class);
23
24     private final CallHomeTlsAuthProvider authProvider;
25     private final CallHomeStatusRecorder statusRecorder;
26
27     public CallHomeTlsSessionContextManager(final CallHomeTlsAuthProvider authProvider,
28         final CallHomeStatusRecorder statusRecorder) {
29         super();
30         this.authProvider = requireNonNull(authProvider);
31         this.statusRecorder = requireNonNull(statusRecorder);
32     }
33
34     @Override
35     public CallHomeTlsSessionContext findByChannel(final Channel channel) {
36         requireNonNull(channel);
37         return channel.isOpen() ? createValidContext(channel) : null;
38     }
39
40     private CallHomeTlsSessionContext createValidContext(final Channel channel) {
41         // extract peer public key from SSL session
42         final PublicKey publicKey;
43         try {
44             final var cert = channel.pipeline().get(SslHandler.class).engine().getSession()
45                 .getPeerCertificates()[0];
46             publicKey = cert.getPublicKey();
47         } catch (SSLPeerUnverifiedException e) {
48             LOG.error("Exception retrieving certificate", e);
49             return null;
50         }
51         // identify connection
52         final String id = authProvider.idFor(publicKey);
53         if (id == null) {
54             statusRecorder.reportUnknown(channel.remoteAddress(), publicKey);
55             return null;
56         }
57
58         final var context = createContext(id, channel);
59         if (context == null) {
60             // if there is an issue creating context then the cause expected to be
61             // logged within overridden createContext() method
62             return null;
63         }
64         register(context);
65         // close callback
66         channel.closeFuture().addListener(ignored -> {
67             remove(id);
68             if (context.settableFuture().isDone()) {
69                 // disconnect after netconf session established
70                 statusRecorder.reportDisconnected(id);
71             }
72         });
73
74         LOG.debug("Session context is created for TLS session: {}", context);
75         return context;
76     }
77
78     /**
79      * Builds {@link CallHomeTlsSessionContext} based on {@link Channel} object.
80      *
81      * <p> The method expected to be overridden in order to inject
82      * {@link org.opendaylight.netconf.client.NetconfClientSessionListener NetconfClientSessionListener} and/or
83      * {@link SettableFuture} of a {@link org.opendaylight.netconf.client.NetconfClientSession NetconfClientSession}
84      *  to be established on current connection.
85      *
86      * @param id unique client (device) identifier
87      * @param channel netty channel instance
88      * @return created object or {@code null} if it cannot be created for some reason
89      */
90     public CallHomeTlsSessionContext createContext(final String id, final Channel channel) {
91         return new CallHomeTlsSessionContext(id, channel, new SimpleNetconfClientSessionListener(),
92             SettableFuture.create());
93     }
94 }