fix(xep): When using FAST, fallback to other SASL mechanisms on failure
This commit is contained in:
parent
0033d0eb6e
commit
4a6aa79e56
@ -53,10 +53,15 @@ abstract class Sasl2AuthenticationNegotiator extends SaslNegotiator
|
||||
/// the base64-encoded response data.
|
||||
Future<String> getRawStep(String input);
|
||||
|
||||
/// Tells the negotiator that it has been selected as the SASL negotiator for SASL2.
|
||||
void pickForSasl2() {
|
||||
_pickedForSasl2 = true;
|
||||
}
|
||||
|
||||
/// When SASL2 fails, should we retry (true) or just fail (false).
|
||||
/// Defaults to just returning false.
|
||||
bool shouldRetrySasl() => false;
|
||||
|
||||
@override
|
||||
void reset() {
|
||||
_pickedForSasl2 = false;
|
||||
@ -289,6 +294,16 @@ class Sasl2Negotiator extends XmppFeatureNegotiatorBase {
|
||||
await negotiator.onSasl2Failure(nonza);
|
||||
}
|
||||
|
||||
// Check if we should retry and, if we should, reset the current
|
||||
// negotiator, this negotiator, and retry.
|
||||
if (_currentSaslNegotiator!.shouldRetrySasl()) {
|
||||
_currentSaslNegotiator!.reset();
|
||||
reset();
|
||||
return const Result(
|
||||
NegotiatorState.retryLater,
|
||||
);
|
||||
}
|
||||
|
||||
return Result(
|
||||
SaslError.fromFailure(nonza),
|
||||
);
|
||||
|
@ -30,10 +30,14 @@ class FASTToken {
|
||||
);
|
||||
|
||||
factory FASTToken.fromXml(XMLNode token) {
|
||||
assert(token.tag == 'token',
|
||||
'Token can only be deserialised from a <token /> element',);
|
||||
assert(token.xmlns == fastXmlns,
|
||||
'Token can only be deserialised from a <token /> element',);
|
||||
assert(
|
||||
token.tag == 'token',
|
||||
'Token can only be deserialised from a <token /> element',
|
||||
);
|
||||
assert(
|
||||
token.xmlns == fastXmlns,
|
||||
'Token can only be deserialised from a <token /> element',
|
||||
);
|
||||
|
||||
return FASTToken(
|
||||
token.attributes['token']! as String,
|
||||
@ -80,7 +84,8 @@ class FASTSaslNegotiator extends Sasl2AuthenticationNegotiator {
|
||||
@override
|
||||
bool canInlineFeature(List<XMLNode> features) {
|
||||
return features.firstWhereOrNull(
|
||||
(child) => child.tag == 'fast' && child.xmlns == fastXmlns,) !=
|
||||
(child) => child.tag == 'fast' && child.xmlns == fastXmlns,
|
||||
) !=
|
||||
null;
|
||||
}
|
||||
|
||||
@ -114,6 +119,9 @@ class FASTSaslNegotiator extends Sasl2AuthenticationNegotiator {
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRetrySasl() => true;
|
||||
|
||||
@override
|
||||
Future<List<XMLNode>> onSasl2FeaturesReceived(XMLNode sasl2Features) async {
|
||||
if (fastToken != null && pickedForSasl2) {
|
||||
|
@ -160,4 +160,110 @@ void main() {
|
||||
expect(conn.resource, 'MU29eEZn');
|
||||
expect(fakeSocket.getState(), 7);
|
||||
});
|
||||
|
||||
test('Test failed FAST authentication with a token', () async {
|
||||
final fakeSocket = StubTCPSocket([
|
||||
StringExpectation(
|
||||
"<stream:stream xmlns='jabber:client' version='1.0' xmlns:stream='http://etherx.jabber.org/streams' to='test.server' xml:lang='en'>",
|
||||
'''
|
||||
<stream:stream
|
||||
xmlns="jabber:client"
|
||||
version="1.0"
|
||||
xmlns:stream="http://etherx.jabber.org/streams"
|
||||
from="test.server"
|
||||
xml:lang="en">
|
||||
<stream:features xmlns="http://etherx.jabber.org/streams">
|
||||
<mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
|
||||
<mechanism>PLAIN</mechanism>
|
||||
<mechanism>HT-SHA-256-NONE</mechanism>
|
||||
</mechanisms>
|
||||
<authentication xmlns='urn:xmpp:sasl:2'>
|
||||
<mechanism>PLAIN</mechanism>
|
||||
<mechanism>HT-SHA-256-NONE</mechanism>
|
||||
<mechanism>HT-SHA-256-ENDP</mechanism>
|
||||
<inline>
|
||||
<bind xmlns="urn:xmpp:bind:0" />
|
||||
<fast xmlns="urn:xmpp:fast:0">
|
||||
<mechanism>HT-SHA-256-NONE</mechanism>
|
||||
<mechanism>HT-SHA-256-ENDP</mechanism>
|
||||
</fast>
|
||||
</inline>
|
||||
</authentication>
|
||||
<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">
|
||||
<required/>
|
||||
</bind>
|
||||
</stream:features>''',
|
||||
),
|
||||
StanzaExpectation(
|
||||
"<authenticate xmlns='urn:xmpp:sasl:2' mechanism='HT-SHA-256-NONE'><user-agent id='d4565fa7-4d72-4749-b3d3-740edbf87770'><software>moxxmpp</software><device>PapaTutuWawa's awesome device</device></user-agent><initial-response>WXZzciBwYmFmdmZnZiBqdmd1IGp2eXFhcmZm</initial-response><fast xmlns='urn:xmpp:fast:0' /></authenticate>",
|
||||
'''
|
||||
<failure xmlns='urn:xmpp:sasl:2'>
|
||||
<not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>
|
||||
</failure>
|
||||
''',
|
||||
),
|
||||
StanzaExpectation(
|
||||
"<authenticate xmlns='urn:xmpp:sasl:2' mechanism='PLAIN'><user-agent id='d4565fa7-4d72-4749-b3d3-740edbf87770'><software>moxxmpp</software><device>PapaTutuWawa's awesome device</device></user-agent><initial-response>AHBvbHlub21kaXZpc2lvbgBhYWFh</initial-response><request-token xmlns='urn:xmpp:fast:0' mechanism='HT-SHA-256-NONE' /></authenticate>",
|
||||
'''
|
||||
<success xmlns='urn:xmpp:sasl:2'>
|
||||
<authorization-identifier>polynomdivision@test.server</authorization-identifier>
|
||||
<token xmlns='urn:xmpp:fast:0'
|
||||
expiry='2020-03-12T14:36:15Z'
|
||||
token='ed00e36cb42449a365a306a413f51ffd5ea8' />
|
||||
</success>
|
||||
''',
|
||||
),
|
||||
StanzaExpectation(
|
||||
'<iq xmlns="jabber:client" type="set" id="a"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/></iq>',
|
||||
'<iq xmlns="jabber:client" type="result" id="a"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><jid>polynomdivision@test.server/MU29eEZn</jid></bind></iq>',
|
||||
ignoreId: true,
|
||||
),
|
||||
]);
|
||||
final conn = XmppConnection(
|
||||
TestingReconnectionPolicy(),
|
||||
AlwaysConnectedConnectivityManager(),
|
||||
fakeSocket,
|
||||
)..setConnectionSettings(
|
||||
ConnectionSettings(
|
||||
jid: JID.fromString('polynomdivision@test.server'),
|
||||
password: 'aaaa',
|
||||
useDirectTLS: true,
|
||||
),
|
||||
);
|
||||
await conn.registerManagers([
|
||||
RosterManager(TestingRosterStateManager('', [])),
|
||||
DiscoManager([]),
|
||||
]);
|
||||
await conn.registerFeatureNegotiators([
|
||||
SaslPlainNegotiator(),
|
||||
ResourceBindingNegotiator(),
|
||||
FASTSaslNegotiator()
|
||||
..fastToken = const FASTToken(
|
||||
'WXZzciBwYmFmdmZnZiBqdmd1IGp2eXFhcmZm',
|
||||
'2020-03-12T14:36:15Z',
|
||||
),
|
||||
Sasl2Negotiator(
|
||||
userAgent: const UserAgent(
|
||||
id: 'd4565fa7-4d72-4749-b3d3-740edbf87770',
|
||||
software: 'moxxmpp',
|
||||
device: "PapaTutuWawa's awesome device",
|
||||
),
|
||||
),
|
||||
]);
|
||||
|
||||
final result1 = await conn.connect(
|
||||
waitUntilLogin: true,
|
||||
shouldReconnect: false,
|
||||
enableReconnectOnSuccess: false,
|
||||
);
|
||||
expect(result1.isType<NegotiatorError>(), false);
|
||||
expect(conn.resource, 'MU29eEZn');
|
||||
expect(fakeSocket.getState(), 4);
|
||||
|
||||
final token = conn
|
||||
.getNegotiatorById<FASTSaslNegotiator>(saslFASTNegotiator)!
|
||||
.fastToken;
|
||||
expect(token != null, true);
|
||||
expect(token!.token, 'ed00e36cb42449a365a306a413f51ffd5ea8');
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user