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.
|
/// the base64-encoded response data.
|
||||||
Future<String> getRawStep(String input);
|
Future<String> getRawStep(String input);
|
||||||
|
|
||||||
|
/// Tells the negotiator that it has been selected as the SASL negotiator for SASL2.
|
||||||
void pickForSasl2() {
|
void pickForSasl2() {
|
||||||
_pickedForSasl2 = true;
|
_pickedForSasl2 = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// When SASL2 fails, should we retry (true) or just fail (false).
|
||||||
|
/// Defaults to just returning false.
|
||||||
|
bool shouldRetrySasl() => false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void reset() {
|
void reset() {
|
||||||
_pickedForSasl2 = false;
|
_pickedForSasl2 = false;
|
||||||
@ -289,6 +294,16 @@ class Sasl2Negotiator extends XmppFeatureNegotiatorBase {
|
|||||||
await negotiator.onSasl2Failure(nonza);
|
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(
|
return Result(
|
||||||
SaslError.fromFailure(nonza),
|
SaslError.fromFailure(nonza),
|
||||||
);
|
);
|
||||||
|
@ -30,10 +30,14 @@ class FASTToken {
|
|||||||
);
|
);
|
||||||
|
|
||||||
factory FASTToken.fromXml(XMLNode token) {
|
factory FASTToken.fromXml(XMLNode token) {
|
||||||
assert(token.tag == 'token',
|
assert(
|
||||||
'Token can only be deserialised from a <token /> element',);
|
token.tag == 'token',
|
||||||
assert(token.xmlns == fastXmlns,
|
'Token can only be deserialised from a <token /> element',
|
||||||
'Token can only be deserialised from a <token /> element',);
|
);
|
||||||
|
assert(
|
||||||
|
token.xmlns == fastXmlns,
|
||||||
|
'Token can only be deserialised from a <token /> element',
|
||||||
|
);
|
||||||
|
|
||||||
return FASTToken(
|
return FASTToken(
|
||||||
token.attributes['token']! as String,
|
token.attributes['token']! as String,
|
||||||
@ -80,7 +84,8 @@ class FASTSaslNegotiator extends Sasl2AuthenticationNegotiator {
|
|||||||
@override
|
@override
|
||||||
bool canInlineFeature(List<XMLNode> features) {
|
bool canInlineFeature(List<XMLNode> features) {
|
||||||
return features.firstWhereOrNull(
|
return features.firstWhereOrNull(
|
||||||
(child) => child.tag == 'fast' && child.xmlns == fastXmlns,) !=
|
(child) => child.tag == 'fast' && child.xmlns == fastXmlns,
|
||||||
|
) !=
|
||||||
null;
|
null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,6 +119,9 @@ class FASTSaslNegotiator extends Sasl2AuthenticationNegotiator {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool shouldRetrySasl() => true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<XMLNode>> onSasl2FeaturesReceived(XMLNode sasl2Features) async {
|
Future<List<XMLNode>> onSasl2FeaturesReceived(XMLNode sasl2Features) async {
|
||||||
if (fastToken != null && pickedForSasl2) {
|
if (fastToken != null && pickedForSasl2) {
|
||||||
|
@ -160,4 +160,110 @@ void main() {
|
|||||||
expect(conn.resource, 'MU29eEZn');
|
expect(conn.resource, 'MU29eEZn');
|
||||||
expect(fakeSocket.getState(), 7);
|
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