1 /*
2  * hunt-amqp: AMQP library for D programming language, based on hunt-net.
3  *
4  * Copyright (C) 2018-2019 HuntLabs
5  *
6  * Website: https://www.huntlabs.net
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 module hunt.amqp.sasl.impl.ProtonSaslMechanismFinderImpl;
12 
13 import hunt.collection.ArrayList;
14 import hunt.collection.Collections;
15 import hunt.collection.List;
16 import hunt.collection.Set;
17 import hunt.logging;
18 import hunt.amqp.sasl.ProtonSaslMechanism;
19 import hunt.amqp.sasl.ProtonSaslMechanismFactory;
20 import hunt.amqp.sasl.impl.ProtonSaslPlainImpl;
21 import hunt.amqp.sasl.impl.ProtonSaslAnonymousImpl;
22 import hunt.amqp.sasl.impl.ProtonSaslPlainFactoryImpl;
23 import hunt.amqp.sasl.impl.ProtonSaslAnonymousFactoryImpl;
24 import hunt.amqp.sasl.impl.ProtonSaslExternalImpl;
25 import hunt.amqp.sasl.impl.ProtonSaslExternalFactoryImpl;
26 
27 class ProtonSaslMechanismFinderImpl {
28 
29   //private static Logger LOG = LoggerFactory.getLogger(ProtonSaslMechanismFinderImpl.class);
30 
31   /**
32    * Attempts to find a matching Mechanism implementation given a list of supported mechanisms from a remote peer. Can
33    * return null if no matching Mechanisms are found.
34    *
35    * @param username
36    *          the username, or null if there is none
37    * @param password
38    *          the password, or null if there is none
39    * @param mechRestrictions
40    *          The possible mechanism(s) to which the client should restrict its mechanism selection to if offered by the
41    *          server, or null/empty if there is no restriction
42    * @param remoteMechanisms
43    *          list of mechanism names that are supported by the remote peer.
44    *
45    * @return the best matching Mechanism for the supported remote set.
46    */
47   public static ProtonSaslMechanism findMatchingMechanism(string username, string password,
48                                                           Set!string mechRestrictions, string [] remoteMechanisms) {
49 
50     ProtonSaslMechanism match = null;
51     List!ProtonSaslMechanism found = new ArrayList!ProtonSaslMechanism();
52 
53     foreach (string remoteMechanism ; remoteMechanisms) {
54       ProtonSaslMechanismFactory factory = findMechanismFactory(remoteMechanism);
55       if (factory !is null) {
56         ProtonSaslMechanism mech = factory.createMechanism();
57         if (mechRestrictions !is null && !mechRestrictions.isEmpty() && !mechRestrictions.contains(remoteMechanism)) {
58           //if (LOG.isTraceEnabled()) {
59           //  LOG.trace("Skipping " + remoteMechanism + " mechanism because it is not in the configured mechanisms restriction set");
60           //}
61         } else if (mech.isApplicable(username, password)) {
62           found.add(mech);
63         } else {
64           //if (LOG.isTraceEnabled()) {
65           //  LOG.trace("Skipping " + mech + " mechanism because the available credentials are not sufficient");
66           //}
67         }
68       }
69     }
70 
71     if (!found.isEmpty()) {
72       // Sorts by priority using mechanism comparison and return the last value in
73       // list which is the mechanism deemed to be the highest priority match.
74      // Collections.sort(found);
75       match = found.get(found.size() - 1);
76     }
77 
78     //if (LOG.isTraceEnabled()) {
79     //  LOG.trace("Best match for SASL auth was: " + match);
80     //}
81 
82     return match;
83   }
84 
85   /**
86    * Searches for a mechanism factory by using the scheme from the given name.
87    *
88    * @param name
89    *          The name of the authentication mechanism to search for.
90    *
91    * @return a mechanism factory instance matching the name, or null if none was created.
92    */
93   protected static ProtonSaslMechanismFactory findMechanismFactory(string name) {
94     if (name is null || name.length ==0) {
95      // LOG.warn("No SASL mechanism name was specified");
96       logError("No SASL mechanism name was specified");
97       return null;
98     }
99 
100     ProtonSaslMechanismFactory factory = null;
101 
102     // TODO: make it pluggable?
103     if (ProtonSaslPlainImpl.MECH_NAME == (name)) {
104       factory = new ProtonSaslPlainFactoryImpl();
105     } else if (ProtonSaslAnonymousImpl.MECH_NAME == (name)) {
106       factory = new ProtonSaslAnonymousFactoryImpl();
107     } else if (ProtonSaslExternalImpl.MECH_NAME == (name)) {
108       factory = new ProtonSaslExternalFactoryImpl();
109     }
110 
111     return factory;
112   }
113 }