コード例 #1
0
def main():
    parser = argparse.ArgumentParser(description="Authenticate with xbox live")
    parser.add_argument('--tokens', '-t', default=TOKENS_FILE,
                        help="Token filepath, file gets created if nonexistent and auth is successful."
                             " Default: {}".format(TOKENS_FILE))
    parser.add_argument('--email', '-e',
                        help="Microsoft Account Email address")
    parser.add_argument('--password', '-p',
                        help="Microsoft Account password")

    args = parser.parse_args()

    tokens_loaded = False
    two_factor_auth_required = False
    server_data = None

    auth_mgr = AuthenticationManager()
    if args.tokens:
        try:
            auth_mgr.load(args.tokens)
            tokens_loaded = True
        except FileNotFoundError as e:
            print('Failed to load tokens from \'{}\'. Error: {}'.format(e.filename, e.strerror))

    auth_mgr.email_address = args.email
    auth_mgr.password = args.password

    if (not args.email or not args.password) and not tokens_loaded:
        print("Please input authentication credentials")
    if not args.email and not tokens_loaded:
        auth_mgr.email_address = input("Microsoft Account Email: ")
    if not args.password and not tokens_loaded:
        auth_mgr.password = getpass.getpass('Microsoft Account Password: '******'2FA is required, message: %s' % e)
        two_factor_auth_required = True
        server_data = e.server_data
    except AuthenticationException as e:
        print('Email/Password authentication failed! Err: %s' % e)
        sys.exit(-1)

    if two_factor_auth_required:
        try:
            two_factor_auth(auth_mgr, server_data)
        except AuthenticationException as e:
            print('2FA Authentication failed! Err: %s' % e)
            sys.exit(-1)

    if args.tokens:
        auth_mgr.dump(args.tokens)

    print('Refresh Token: %s' % auth_mgr.refresh_token)
    print('XSTS Token: %s' % auth_mgr.xsts_token)
    print('Userinfo: %s' % auth_mgr.userinfo)
コード例 #2
0
def test_auth_invalid_credentials():
    auth_manager = AuthenticationManager()
    auth_manager.email_address = "*****@*****.**"
    auth_manager.password = "******"

    with Betamax(auth_manager.session).use_cassette('invalid_auth'):
        with pytest.raises(AuthenticationException):
            auth_manager.authenticate()

    assert auth_manager.authenticated is False
コード例 #3
0
def test_auth_valid_credentials():
    auth_manager = AuthenticationManager()
    auth_manager.email_address = "*****@*****.**"
    auth_manager.password = "******"

    with Betamax(auth_manager.session).use_cassette('full_auth'):
        auth_manager.authenticate(do_refresh=False)

    assert auth_manager.authenticated is True
    assert auth_manager.xsts_token.is_valid is True
    assert auth_manager.access_token.is_valid is True
    assert auth_manager.refresh_token.is_valid is True
    assert auth_manager.user_token.is_valid is True
    assert auth_manager.userinfo.userhash == '1674471606081042789'
    assert auth_manager.userinfo.xuid == '2535428504476914'
    assert auth_manager.userinfo.gamertag == 'xboxWebapiGamertag'
コード例 #4
0
def _do_2fa(cassette_name, strategy_index, proof=None, otc=None):
    auth_manager = AuthenticationManager()
    auth_manager.email_address = '*****@*****.**'
    auth_manager.password = '******'

    with Betamax(auth_manager.session).use_cassette(cassette_name):
        with pytest.raises(TwoFactorAuthRequired) as excinfo:
            auth_manager.authenticate()

        two_fa_auth = TwoFactorAuthentication(
            auth_manager.session, auth_manager.email_address, excinfo.value.server_data
        )
        two_fa_auth.check_otc(strategy_index, proof)
        access_token, refresh_token = two_fa_auth.authenticate(strategy_index, proof, otc)
        auth_manager.access_token = access_token
        auth_manager.refresh_token = refresh_token
        auth_manager.authenticate(do_refresh=False)
    return auth_manager
コード例 #5
0
def test_auth_refresh_token():
    auth_manager = AuthenticationManager()
    auth_manager.refresh_token = RefreshToken(
        "CuZ*4TX7!SAF33cW*kzdFmgCLPRcz0DtUHFqjQgF726!FG3ScC5yMiDLsJYJ03m4fURrzf3J7X8l6A8mJGhHoRf42aHeJLrtp6wS"
        "Jh*PudaQdPNGJZHD1CpU4diJJxz0zhrijFsaAYXMqf3mSU7EerR5RtdHOwbcrlRlj7TBQ9RdLqWpy9KWsNhyPrwOMDJnBfAf3xsZ"
        "3g3QkmMeKGil85*q*MV*YqMPZTa8UVLPfM!jJeHwOjVhWPaYVq4hf6zIAwSLJl1Reo6GbkkPktrK3laFBGeqSkq651YgdjwtepwC"
        "Ef7oMwzz8c8msv8l95RU*QmtIjdRFd!fYtQctiGLDGs$")

    with Betamax(auth_manager.session).use_cassette('token_refresh'):
        auth_manager.authenticate(do_refresh=True)

    assert auth_manager.authenticated is True
    assert auth_manager.xsts_token.is_valid is True
    assert auth_manager.access_token.is_valid is True
    assert auth_manager.refresh_token.is_valid is True
    assert auth_manager.user_token.is_valid is True
    assert auth_manager.userinfo.userhash == '1674471606081042789'
    assert auth_manager.userinfo.xuid == '2535428504476914'
    assert auth_manager.userinfo.gamertag == 'xboxWebapiGamertag'
コード例 #6
0
class XBot(MicrosoftBot):
    def __init__(self, email, password, x_auth_key):
        self.x_auth_key = x_auth_key
        self.url = 'http://xapi.us/v2'
        self.client = Client(api_key=x_auth_key)

        # use the microsoft api to get the xuid info without using requests
        self.auth_mgr = AuthenticationManager()

        # set data for auth manager
        self.auth_mgr.email_address = email
        self.auth_mgr.password = password

        # authentication
        self.auth_mgr.authenticate(do_refresh=True)

        # set the new info to a xbl client
        self.xbl_client = XboxLiveClient(self.auth_mgr.userinfo.userhash,
                                         self.auth_mgr.xsts_token.jwt,
                                         self.auth_mgr.userinfo.xuid)
        self.profile_provider = ProfileProvider(self.xbl_client)
        self.people_provider = PeopleProvider(self.xbl_client)

    # must have a method here to get the recent activity of a user
    def get_games(self, gamertag):
        # this takes more time but saves api requests
        xuid = self.get_xuid(gamertag)
        gamer = self.client.gamer(gamertag=gamertag, xuid=xuid)

        try:
            titles = gamer.get('xboxonegames')['titles']
        except ReadTimeout:
            time.sleep(30)
            return []
        except JSONDecodeError:
            time.sleep(30)
            print(f"{gamertag} isn't a real gamer.")
            return []

        games = [title['name'] for title in titles]

        return games

    def send_messages_url(self, gamertags, message):
        endpoint = f"{self.url}/messages"
        headers = {
            'X-Auth': self.x_auth_key,
            'Content-Type': 'application/json'
        }

        # convert the gamertags to xuids
        xuids = [self.get_xuid(gamertag) for gamertag in gamertags]

        body = {"to": xuids, "message": message}

        response = requests.post(url=endpoint, data=body, headers=headers)

        return response

    def send_message(self, gamertag, message):
        # set a boolean to whether or not the message was sent
        sent = False

        # create a game object
        try:
            xuid = self.get_xuid(gamertag)
            gamer = self.client.gamer(gamertag, xuid)

            # send the message
            gamer.send_message(message)

            sent = True
        except JSONDecodeError:
            pass

        return sent

    # create a method that extrapolates on a set of gamers and adds to it
    # games --> particular games the gamer has to play to be added to the set
    def extrapolate(self, gamertags, games, max_messages):
        candidates = set()
        list_manager = ListManager(games)

        for gamer in gamertags:
            # find the frients and games --> build with a dict builder
            friends = self.get_friends(gamer)

            # use a set operator to add the friends
            candidates = candidates | friends
            time.sleep(2)

        # randomize the candidates to increase spread
        candidates = list(candidates)
        print(candidates)
        shuffle(candidates)

        # find overlapping games --> add to gamertags if it the gamer matches one of the games
        for gamer in candidates:
            gamertag = self.get_gamertag(gamer)
            if gamertag:
                print(f"found {gamertag}")

            # this protexts against getting nonetype errrors
            if not gamertag:
                continue
            elif not gamer:
                continue

            print(f"Checking what games {gamertag} plays")
            games = list(set(self.get_games(gamertag)))

            # check if any of the games are in the the list of games --> the list manager return
            if list_manager.find_any_overlap(games):
                gamertags.add(gamertag)
                print(f"Added {gamertag} from friends")

            # check if the gamertags has reached its max
            if len(gamertags) >= max_messages:
                break

            # sleep to avoid throttling
            time.sleep(10)

        # return the gamertags
        return gamertags
コード例 #7
0
class MicrosoftBot:
    def __init__(self, email, password):
        self.auth_mgr = AuthenticationManager()

        # set data for auth manager
        self.auth_mgr.email_address = email
        self.auth_mgr.password = password

        # authentication
        self.auth_mgr.authenticate(do_refresh=True)

        # set the new info to a xbl client
        self.xbl_client = XboxLiveClient(self.auth_mgr.userinfo.userhash,
                                         self.auth_mgr.xsts_token.jwt,
                                         self.auth_mgr.userinfo.xuid)
        # not currently used but could be useful later
        self.profile_provider = ProfileProvider(self.xbl_client)
        self.people_provider = PeopleProvider(self.xbl_client)

    # sends message to list of multiple users
    def send_message(self, message, users):
        response = self.xbl_client.message.send_message(message,
                                                        gamertags=users)

        return response

    # create a function that gets xuids
    def get_xuid(self, gamertag):
        profile = self.profile_provider.get_profile_by_gamertag(
            gamertag).json()
        try:
            xuid = profile['profileUsers'][0]['id']
        except KeyError:
            # this means the gamertag wasn't found
            return

        return xuid

    # create a function to convert xuids back to gamertags
    def get_gamertag(self, xuid):
        profile = self.profile_provider.get_profile_by_xuid(xuid).json()
        try:
            settings = profile['profileUsers'][0]['settings']
        except KeyError:
            time.sleep(61)
            return None
        info_dict = {pair['id']: pair['value'] for pair in settings}
        gamertag = info_dict['Gamertag']

        return gamertag

    # create a method to get the user's friends (use xuids to save time and maximize integration)
    def get_friends(self, gamertag):
        xuid = self.get_xuid(gamertag)
        response_json = self.people_provider.get_friends_by_xuid(xuid).json()

        try:
            friends_raw = response_json['people']
        except KeyError:
            print(f"{gamertag} is a certified loner")
            friends_raw = []
            # this often means that the api is blocking us
            time.sleep(180)

        xuids = {info['xuid'] for info in friends_raw}

        return xuids
コード例 #8
0
class WebAPIDisplay(object):
    focus_map = {
        None: 'selected'
    }

    palette = [
        ('bg', 'white', 'dark gray'),
        ('header', 'yellow', 'dark blue', 'standout'),

        # footer
        ('foot', 'dark cyan', 'dark blue', 'bold'),
        ('key', 'light cyan', 'dark blue', 'underline')
    ]

    header_text = ('header', [
        "Xbox WebAPI"
    ])

    footer_main_text = ('foot', [
        ('key', 'L:'), "view log ",
        ('key', 'Q:'), "quit  "
    ])

    footer_log_text = ('foot', [
        ('key', 'Q:'), "quit "
    ])

    log_fmt = logging.Formatter(logging.BASIC_FORMAT)
    log_level = logging.DEBUG

    def __init__(self, tokenfile_path):
        self.tokenfile_path = tokenfile_path

        self.auth_mgr = AuthenticationManager()
        self.two_factor_auth = None

        # 2FA cache
        self.index = None
        self.proof = None
        self.otc = None

        self.loop = None
        self.log = LogListBox(self)

        self.view_stack = []

        try:
            self.auth_mgr.load(self.tokenfile_path)
        except Exception as e:
            logging.debug('Tokens failed to load from file, Error: {}'.format(e))

        '''
        self.need_refresh = self.auth_mgr.refresh_token and \
            self.auth_mgr.refresh_token.is_valid and \
            self.auth_mgr.refresh_token.date_valid < (datetime.now(tzutc()) + timedelta(days=7))
        '''
        self.need_full_auth = not self.auth_mgr.refresh_token or not self.auth_mgr.refresh_token.is_valid

    def push_view(self, sender, view):
        self.view_stack.append(view)
        self.loop.widget = view
        self.loop.draw_screen()

    def pop_view(self, sender):
        if len(self.view_stack) > 1:
            top_widget = self.view_stack.pop()
            if hasattr(top_widget, 'close_view'):
                top_widget.close_view(sender)

            self.loop.widget = self.view_stack[-1]
            self.loop.draw_screen()
        else:
            self.do_quit()

    def _input_prompt(self, prompt, callback, entries=None):
        if entries:
            list_entries = [
                urwid.AttrWrap(urwid.SelectableIcon(e, cursor_position=0), None) for e in entries
            ]
            walker = urwid.SimpleFocusListWalker([urwid.AttrMap(e, None, self.focus_map) for e in list_entries])
            listbox = SelectableListBox(walker, callback)
            view = urwid.BoxAdapter(listbox, height=len(entries))
        else:
            edit_text = QuestionBox(callback, align='left')
            view = urwid.AttrMap(edit_text, None, self.focus_map)

        box = urwid.LineBox(view, title=prompt)
        self._view_menu([box])

    def view_main(self):
        if self.need_full_auth:
            self.view_authentication_menu()
        # elif self.need_refresh:
        #    self._authenticate(status_text='Refreshing tokens...\n')
        else:
            self._authenticate()

    def _two_factor_finish_auth(self, otc):
        self.otc = otc
        self.view_msgbox('Waiting for 2FA to complete', 'Please wait')

        access_token, refresh_token = None, None
        try:
            access_token, refresh_token = self.two_factor_auth.authenticate(
                self.index, self.proof, self.otc
            )
        except AuthenticationException as e:
            logging.debug('2FA Authentication failed, Error: {}'.format(e))
            self.view_msgbox('2FA Authentication failed!\n{}\n'.format(e), 'Error',
                             show_quit_button=True)

        self.auth_mgr.access_token = access_token
        self.auth_mgr.refresh_token = refresh_token
        self._authenticate()

    def _two_factor_auth_ask_otc(self, proof):
        self.proof = proof
        need_otc = self.two_factor_auth.check_otc(self.index, proof)
        if need_otc:
            self._input_prompt('Enter One-Time-Code (OTC)', self._two_factor_finish_auth)
        else:
            self._two_factor_finish_auth(None)

    def _two_factor_auth_verify_proof(self, index):
        self.index = index
        verification_prompt = self.two_factor_auth.get_method_verification_prompt(index)
        if verification_prompt:
            self._input_prompt(verification_prompt, self._two_factor_auth_ask_otc)
        else:
            self._two_factor_auth_ask_otc(None)

    def view_two_factor_auth(self, server_data):
        self.two_factor_auth = TwoFactorAuthentication(
            self.auth_mgr.session, self.auth_mgr.email_address, server_data
        )
        entries = ['{!s}, Name: {}'.format(
            TwoFactorAuthMethods(strategy.get('type', 0)), strategy.get('display'))
            for strategy in self.two_factor_auth.auth_strategies
        ]
        self._input_prompt('Choose desired auth method', self._two_factor_auth_verify_proof, entries)

    def _authenticate(self, email=None, password=None, status_text='Authenticating...\n'):
        self.auth_mgr.email_address = email
        self.auth_mgr.password = password
        try:
            self.view_msgbox(status_text, 'Please wait')
            self.auth_mgr.authenticate(do_refresh=True)  # do_refresh=self.need_refresh
            self.auth_mgr.dump(self.tokenfile_path)
            do_show_quit_button = True
            if not self.need_full_auth:
                # If authentication was done from tokens, auto close on success
                do_show_quit_button = False
                self.loop.set_alarm_in(2.0, lambda *args: self.do_quit())
            self.view_msgbox('Authentication was successful!\n', 'Success',
                             show_quit_button=do_show_quit_button)

        except TwoFactorAuthRequired as e:
            self.view_two_factor_auth(e.server_data)

        except AuthenticationException as e:
            logging.debug('Authentication failed, Error: {}'.format(e))
            self.view_msgbox('Authentication failed!\n{}\n'.format(e), 'Error',
                             show_quit_button=True)

    def _on_button_press(self, button, user_arg=None):
        label = button.get_label()
        if 'Authenticate' == label:
            email, pwd = (t.get_edit_text() for t in user_arg)
            self._authenticate(email, pwd)
        elif 'Quit' == label or 'Cancel' == label:
            self.do_quit()
        else:
            raise ValueError('tui: Unexpected button pressed: {}'.format(label))

    def _view_menu(self, elements):
        header = urwid.AttrMap(urwid.Text(self.header_text), 'header')
        footer = urwid.AttrMap(urwid.Text(self.footer_main_text), 'foot')

        assert isinstance(elements, list)
        pile = urwid.Pile(elements)

        p = urwid.AttrWrap(pile, 'bg')
        padded = urwid.Padding(p, 'center', ('relative', 80))
        filler = urwid.Filler(padded)
        frame = urwid.Frame(filler, header=header, footer=footer)
        self.push_view(self, frame)

    def view_authentication_menu(self):
        info_label = urwid.Text(
            'Please authenticate with your Microsoft Account\n', align='center'
        )
        div = urwid.Divider()
        email_text = urwid.AttrMap(urwid.Edit('Email Address: '), None, self.focus_map)
        password_text = urwid.AttrMap(urwid.Edit('Account Password: '******'*'), None, self.focus_map)

        authenticate_button = urwid.Button('Authenticate')
        authenticate_button._label.align = 'center'
        authenticate_button = urwid.AttrMap(authenticate_button, None, self.focus_map)

        cancel_button = urwid.Button('Cancel')
        cancel_button._label.align = 'center'
        cancel_button = urwid.AttrMap(cancel_button, None, self.focus_map)

        authenticate_button = urwid.Padding(authenticate_button, align='center', width=('relative', 30))
        cancel_button = urwid.Padding(cancel_button, align='center', width=('relative', 23))

        pile = TabSwitchingPile(
            [info_label, div, email_text, div, password_text, div, authenticate_button, cancel_button]
        )
        box = urwid.LineBox(pile, title='Authentication required')

        urwid.connect_signal(authenticate_button.base_widget, 'click', self._on_button_press,
                             user_arg=[email_text.base_widget, password_text.base_widget])
        urwid.connect_signal(cancel_button.base_widget, 'click', self._on_button_press)

        self._view_menu([box])

    def view_msgbox(self, msg, title, show_quit_button=False):
        text = urwid.Text(msg, align='center')

        if show_quit_button:
            button = urwid.Button('Quit')
            button._label.align = 'center'
            button = urwid.AttrMap(button, None, self.focus_map)
            pad_button = urwid.Padding(button, 'center', ('relative', 10))
            pile = urwid.Pile([text, pad_button])
            box = urwid.LineBox(pile, title)

            # Clicking OK exits UI
            urwid.connect_signal(button.base_widget, 'click', self._on_button_press)
        else:
            box = urwid.LineBox(text, title)

        self._view_menu([box])

    def view_log(self):
        header = urwid.AttrMap(urwid.Text(self.header_text), 'header')
        footer = urwid.AttrMap(urwid.Text(self.footer_log_text), 'foot')
        frame = urwid.Frame(self.log, header=header, footer=footer)
        self.push_view(self, frame)

    def return_to_main_menu(self):
        while len(self.view_stack) > 1:
            self.pop_view(self)

    def do_quit(self):
        raise urwid.ExitMainLoop()

    def run(self):
        self.loop = urwid.MainLoop(
            urwid.SolidFill('x'),
            handle_mouse=False,
            palette=self.palette,
            unhandled_input=self.unhandled_input
        )

        self.loop.set_alarm_in(0.0001, lambda *args: self.view_main())
        self.loop.run()
        return self.auth_mgr.is_authenticated

    def unhandled_input(self, input):
        if input in ('q', 'Q'):
            self.do_quit()
        elif input in ('l', 'L'):
            self.view_log()