implements Oauth2 mechanism to connect to MSN xmpp server. Fixes #6978

This commit is contained in:
Yann Leboulanger 2011-12-18 23:20:30 +01:00
parent a1e369dde4
commit 6e4bac262c
7 changed files with 71 additions and 1 deletions

View File

@ -384,6 +384,9 @@ class Config:
'last_archiving_time': [opt_str, '1970-01-01T00:00:00Z', _('Last time we syncronized with logs from server.')],
'enable_message_carbons': [ opt_bool, False, _('If enabled and if server supports this feature, Gajim will receive messages sent and received by other resources.')],
'ft_send_local_ips': [ opt_bool, True, _('If enabled, Gajim will send your local IPs so your contact can connect to your machine to transfer files.')],
'oauth2_refresh_token': [ opt_str, '', _('Latest token for Oauth2 authentication.')],
'oauth2_client_id': [ opt_str, '0000000044077801', _('client_id for Oauth2 authentication.')],
'oauth2_redirect_url': [ opt_str, 'http%3A%2F%2Fgajim.org%2Fmsnauth%2Findex.cgi', _('redirect_url for Oauth2 authentication.')],
}, {}),
'statusmsg': ({
'message': [ opt_str, '' ],

View File

@ -40,6 +40,7 @@ import operator
import time
import locale
import hmac
import json
try:
randomsource = random.SystemRandom()
@ -2492,6 +2493,32 @@ class Connection(CommonConnection, ConnectionHandlers):
def get_password(self, callback, type_):
self.pasword_callback = (callback, type_)
if type_ == 'X-MESSENGER-OAUTH2':
client_id = gajim.config.get_per('accounts', self.name,
'oauth2_client_id')
refresh_token = gajim.config.get_per('accounts', self.name,
'oauth2_refresh_token')
if refresh_token:
renew_URL = 'https://oauth.live.com/token?client_id=' \
'%(client_id)s&redirect_uri=https%%3A%%2F%%2Foauth.live.' \
'com%%2Fdesktop&grant_type=refresh_token&refresh_token=' \
'%(refresh_token)s' % locals()
result = helpers.download_image(self.name, {'src': renew_URL})[0]
if result:
dict_ = json.loads(result)
if 'access_token' in dict_:
self.set_password(dict_['access_token'])
return
script_url = gajim.config.get_per('accounts', self.name,
'oauth2_redirect_url')
token_URL = 'https://oauth.live.com/authorize?client_id=' \
'%(client_id)s&scope=wl.messenger%%20wl.offline_access&' \
'response_type=code&redirect_uri=%(script_url)s' % locals()
helpers.launch_browser_mailer('url', token_URL)
self.disconnect(on_purpose=True)
gajim.nec.push_incoming_event(Oauth2CredentialsRequiredEvent(None,
conn=self))
return
if self.password:
self.set_password(self.password)
return

View File

@ -1783,6 +1783,10 @@ class PasswordRequiredEvent(nec.NetworkIncomingEvent):
name = 'password-required'
base_network_events = []
class Oauth2CredentialsRequiredEvent(nec.NetworkIncomingEvent):
name = 'oauth2-credentials-required'
base_network_events = []
class FailedDecryptEvent(nec.NetworkIncomingEvent):
name = 'failed-decrypt'
base_network_events = []

View File

@ -264,6 +264,12 @@ class SASL(PlugIn):
self._owner._caller.get_password(self.set_password, self.mechanism)
self.startsasl = SASL_IN_PROCESS
raise NodeProcessed
if 'X-MESSENGER-OAUTH2' in self.mecs:
self.mecs.remove('X-MESSENGER-OAUTH2')
self.mechanism = 'X-MESSENGER-OAUTH2'
self._owner._caller.get_password(self.set_password, self.mechanism)
self.startsasl = SASL_IN_PROCESS
raise NodeProcessed
self.startsasl = SASL_FAILURE
log.info('I can only use EXTERNAL, SCRAM-SHA-1, DIGEST-MD5, GSSAPI and '
'PLAIN mecanisms.')
@ -497,6 +503,10 @@ class SASL(PlugIn):
'\n', '')
node = Node('auth', attrs={'xmlns': NS_SASL, 'mechanism': 'PLAIN'},
payload=[sasl_data])
elif self.mechanism == 'X-MESSENGER-OAUTH2':
node = Node('auth', attrs={'xmlns': NS_SASL,
'mechanism': 'X-MESSENGER-OAUTH2'})
node.addData(password)
self._owner.send(str(node))

View File

@ -2165,6 +2165,7 @@ class DoubleInputDialog:
def on_okbutton_clicked(self, widget):
user_input1 = self.input_entry1.get_text().decode('utf-8')
user_input2 = self.input_entry2.get_text().decode('utf-8')
self.cancel_handler = None
self.dialog.destroy()
if not self.ok_handler:
return

View File

@ -63,7 +63,7 @@ if os.name == 'nt':
os.environ['PATH'] = ';'.join(new_list)
from common import demandimport
#demandimport.enable()
demandimport.enable()
demandimport.ignore += ['gobject._gobject', 'libasyncns', 'i18n',
'logging.NullHandler', 'dbus.glib', 'dbus.service',
'command_system.implementation.standard',

View File

@ -701,6 +701,30 @@ class Interface:
_('Password Required'), text, _('Save password'), ok_handler=on_ok,
cancel_handler=on_cancel)
def handle_oauth2_credentials(self, obj):
account = obj.conn.name
def on_ok(refresh):
gajim.config.set_per('accounts', account, 'oauth2_refresh_token',
refresh)
st = gajim.config.get_per('accounts', account, 'last_status')
msg = helpers.from_one_line(gajim.config.get_per('accounts',
account, 'last_status_msg'))
gajim.interface.roster.send_status(account, st, msg)
del self.pass_dialog[account]
def on_cancel():
gajim.config.set_per('accounts', account, 'oauth2_refresh_token',
'')
self.roster.set_state(account, 'offline')
self.roster.update_status_combobox()
del self.pass_dialog[account]
instruction = _('Please copy / paste the refresh token from the website'
' that has just been opened.')
self.pass_dialog[account] = dialogs.InputTextDialog(
_('Oauth2 Credentials'), instruction, is_modal=False,
ok_handler=on_ok, cancel_handler=on_cancel)
def handle_event_roster_info(self, obj):
#('ROSTER_INFO', account, (jid, name, sub, ask, groups))
account = obj.conn.name
@ -1412,6 +1436,7 @@ class Interface:
'metacontacts-received': [self.handle_event_metacontacts],
'muc-admin-received': [self.handle_event_gc_affiliation],
'muc-owner-received': [self.handle_event_gc_config],
'oauth2-credentials-required': [self.handle_oauth2_credentials],
'our-show': [self.handle_event_status],
'password-required': [self.handle_event_password_required],
'plain-connection': [self.handle_event_plain_connection],