Esempio n. 1
0
    def AuthPage(svc, rduri, cid, name, pw, state, resptype, scope):
        clients = clientdb.ClientDB()
        try:
            # check client_id valid
            client = clients.find_client(cid)
            if client is None:
                raise AuthClientError()
            # check redirect_uri match client_id
            if not client.check_redirect_uri(rduri):
                raise AuthClientError()
            # check client_id may use response_type
            if not client.check_response_type(resptype):
                raise AuthError(rduri, 'invalid_client')
            user = UserManager.LoadUser(name)
            if (user == None):
                raise AuthError(rduri, 'access_denied')

            if not scope:
                scopes = client.get_scopes()
            else:
                scopes = scope.split(' ')
                for one_scope in scopes:
                    if not client.check_scope(one_scope):
                        raise AuthError(rduri, 'invalid_scope')

            if (user.Authorize(pw)):
                session = Session(user, svc.client_address[0], scopes=scopes)
                session.RecordLogin(True)
                if resptype == "code":
                    # give session, so other info may be recorded
                    code = Auth.RecordSession(session, cid)
                    target_uri = "%s?code=%s" % (rduri, code)
                    if state:
                        target_uri += "&state=%s" % state

                elif resptype == "token":
                    token = session.GetID()

                    target_uri = "%s?access_token=%s&token_type=session&expires_in=%d&scope=%s" % (
                        rduri, token, Config.SESSION_TIMEOUT_SECONDS,
                        ' '.join(scopes))
                    if state:
                        target_uri += "&state=%s" % state
                else:
                    raise AuthError(rduri, 'unsupported_response_type')

                svc.send_response(302)
                svc.send_header('Location', target_uri)
                svc.send_header('Content-Length', 0)
                svc.end_headers()
                svc.wfile.flush()
            else:
                raise AuthError(rduri, 'access_denied')
        finally:
            clients.close()
Esempio n. 2
0
    def POST(svc, session, params, action):
        try:
            if (action == 'authpage'):
                # rfc6749 4.1.2: authorization response
                # rfc6749 4.2.2: authorization response
                cid = svc.get_str(params, 'client_id')
                rduri = svc.get_str(params, 'redirect_uri')
                name = svc.get_str(params, 'name')
                pw = svc.get_str(params, 'pass')
                resptype = svc.get_str(params, 'response_type')
                state = svc.get_str(params, 'state', '')
                scope = svc.get_str(params, 'scope', '')
                Auth.AuthPage(svc, rduri, cid, name, pw, state, resptype,
                              scope)
            elif (action == 'pwauth'):
                raise NoPerm('pwauth is disabled')
                if (not params.has_key('user') or not params.has_key('pass')):
                    raise NoPerm('too few args')
                name = params['user']
                epw = params['pass']
                pw = base64.b64decode(epw)
                #            print "name: %s pass: %s" % (name, pw)
                user = UserManager.LoadUser(name)
                if (user == None):
                    raise NoPerm('forbidden')

                if (user.Authorize(pw)):
                    session = Session(user, svc.client_address[0])
                    session.RecordLogin(True)
                    # give session, so other info may be recorded
                    code = Auth.RecordSession(session)
                    (sessid, uid) = Auth.SessionInfoFromCode(code)
                    resp = {}
                    resp['access_token'] = sessid
                    resp['token_type'] = 'session'
                    svc.writedata(json.dumps(resp))
                    return
                else:
                    raise NoPerm('forbidden')
            elif (action == 'token'):
                return Auth.GET(svc, session, params, action)
            else:
                raise WrongArgs("unknown action")
        except AuthError as e:
            Auth.Error(svc, e.rduri, e.error)
        except AuthClientError:
            Auth.ClientError(svc)
Esempio n. 3
0
    def GetToken(svc, params, rduri, type, cid, csec):
        clients = clientdb.ClientDB()
        try:
            # check client_id valid // invalid_client
            client = clients.find_client(cid)
            if client is None:
                raise AuthClientError()
            # check client_secret match client_id // invalid_client
            if not client.check_secret(csec):
                raise AuthClientError()
            if not rduri:
                rduri = client.get_default_redirect_uri()
            else:
                # check redirect_uri match client_id // invalid_client
                if not client.check_redirect_uri(rduri):
                    raise AuthClientError()
            if not type:
                raise AuthError(rduri, 'invalid_request')
            # check client_id may use grant_type // unauthorized_client
            if not client.check_grant_type(type):
                raise AuthError(rduri, 'unauthorized_client')
            if type == 'authorization_code':
                if (not params.has_key('code')):
                    raise AuthError(rduri, 'invalid_request')
                code = params['code']
                (sessid, uid, scopes) = Auth.SessionInfoFromCode(code, cid)
                if (sessid == None):
                    raise AuthError(rduri, 'invalid_grant')
                Auth.RemoveCode(code)
            elif type == "refresh_token":
                if not params.has_key('refresh_token'):
                    raise AuthError(rduri, 'invalid_grant')
                old_refresh_token = svc.get_str(params, "refresh_token")
                refreshments = RefreshTokens()
                try:
                    old_token = refreshments.find(old_refresh_token)
                    if old_token is None:
                        raise AuthError(rduri, 'invalid_grant')
                    if old_token['client_id'] != cid:
                        raise AuthError(rduri, 'invalid_grant')
                    uid = old_token['uid']
                    scopes = old_token['scopes']
                    user = UserManager.LoadUserByUid(uid)

                    session = Session(user,
                                      svc.client_address[0],
                                      scopes=scopes.split(','))
                    session.RecordLogin(True)
                    sessid = session.GetID()

                    refreshments.remove(old_refresh_token)
                finally:
                    refreshments.close()
            else:
                raise AuthError(rduri, 'unsupported_grant_type')

            resp = {}
            resp['access_token'] = sessid
            resp['token_type'] = 'session'
            resp['expires_in'] = Config.SESSION_TIMEOUT_SECONDS
            resp['scope'] = ' '.join(scopes)

            if client.check_grant_type('refresh_token'):
                refreshments = RefreshTokens()
                try:
                    refresh_token = refreshments.new(uid, cid,
                                                     svc.client_address[0],
                                                     scopes)
                finally:
                    refreshments.close()

                resp['refresh_token'] = refresh_token

            svc.writedata(json.dumps(resp))
        finally:
            clients.close()
Esempio n. 4
0
class XMPPServer(xmpp.Plugin):
    """XMPP server for the BBS"""
    def __init__(self, rosters, host):
        self.probed = False
        self._closed = False
        self.rosters = rosters
        self._session = None

        self.rosters.set_resources(self.get_resources())

        self._fixedjid = UCache.UCache.formalize_jid(unicode(self.authJID))
        self._userid = self._fixedjid.partition('@')[0].encode("gbk")

        if (not self.rosters.allow_login(self.authJID.bare)):
            Log.warn("user %s login denied" % self._userid)
            #            self.unbind_res()
            self.stream_error('policy-violation',
                              'Login denied. Too many logins?')
            return
        Log.info("%s: session start" % unicode(self.authJID))

        if self.authJID.resource[:-8] != "Resource" and len(
                self.authJID.resource) > 8:
            try:
                routes = self.routes(self.authJID.bare)
                for route in routes:
                    jid = route[0]
                    if jid.resource[:-8] == self.authJID.resource[:-8]:
                        if jid.resource != self.authJID.resource:
                            # old resource!
                            Log.info("old jid: %s %r" % (jid.full, route[1]))
                            route[1].stream_error(
                                'conflict',
                                'A new client with the same resource connected'
                            )
                    else:
                        Log.info("another me: %s %r" % (jid.full, route[1]))
            except NoRoute:
                pass
            Log.debug("%s: checked for old sessions" % self.authJID.full)

        # Login the user
        self._user = UserManager.UserManager.LoadUser(self._userid)
        if (self._user == None):
            raise Exception("How can that be!")
        self._peer_addr = self.getpeername()
        self._session = Session(self._user, self._peer_addr[0])
        self._session.RecordLogin()
        # insert into global session list!
        self._userinfo = self._session.Register()
        self._loginid = self._session.utmpent
        self._hostname = host
        self.bind(xmpp.ReceivedCloseStream, self.recv_close)
        self.bind(xmpp.StreamClosed, self.stream_closed)
        self.bind(xmpp.SentCloseStream, self.sent_close)

        self.rosters.register_conn(self)

        msgbox = MsgBox.MsgBox(self._userid)
        if self.rosters.get_xmpp_read(self._user.GetUID()) is None:
            self.rosters.set_xmpp_read(
                self._user.GetUID(),
                msgbox.GetMsgCount(all=False) - msgbox.GetUnreadCount())
        self.check_msg()

    def get_loginid(self):
        return self._loginid

    def recv_close(self):
        Log.debug("%s: close because he wants to" % self.authJID.full)
        return self.close()

    def stream_closed(self):
        Log.debug("%s: close because stream closed" % self.authJID.full)
        return self.close()

    def sent_close(self):
        Log.debug("%s: close because we want to" % self.authJID.full)
        return self.close()

    def close(self):
        if (self._closed):
            Log.debug("already closed. ignore")
            return
        self._closed = True
        Log.info("%s: session end" % unicode(self.authJID))
        if (self._session):
            self._session.Unregister()
        self.unbind_res()
        self.rosters.unregister_conn(self)

    @xmpp.iq('{urn:xmpp:ping}ping')
    def ping(self, iq):
        """Handle ping requests"""

        self.refresh()
        return self.iq('result', iq)

    @xmpp.stanza('message')
    def message(self, elem):
        """Proxy message from one user to another"""

        # so, possible:
        # XMPP user -> Old user
        # XMPP user -> XMPP user => make it like XMPP->old

        # Old user -> XMPP user (emulated) => handled elsewhere

        to_jid = elem.get('to')
        from_jid = elem.get('from')
        if (from_jid == None):
            return

#       self.recv(to_jid, elem)

        text_body = None
        for child in elem:
            if (child.tag.endswith('}body')):
                text_body = child.text
        if (text_body == None):
            return

        ret = self.rosters.send_msg(from_jid, to_jid, text_body)
        if (ret <= 0):
            Log.warn("sendmsg() failed to %s from %s error %d" %
                     (to_jid, from_jid, ret))
            errors = {
                -1: "That user has locked screen, please send later.",
                -11: "That user denied your message.",
                -12:
                "That user has too many unread messages. Please send later.",
                -13: "User has gone after message sent.",
                -14: "User has gone before message sent.",
                -2: "User has gone before message sent.",
                -21: "Error when sending message!"
            }
            if (ret in errors):
                elem = self.E.message(
                    {
                        'from': to_jid,
                        'to': from_jid,
                        'type': 'error'
                    }, self.E.body(errors[ret]))
                self.recv(from_jid, elem)
            # -2: no perm to see cloak
            # 0: error
            # -1: lockscreen
            # -11: blocked
            # -12: too many messages
            # -13: user gone when notifying
            # -14: user gone before saving
            # -21: error when saving message

    def make_jid(self, userid):
        return "%s@%s" % (userid, self._hostname)

    def refresh(self):
        self._userinfo.freshtime = int(time.time())
        self._userinfo.save()

    def ping_result(self, iq):
        self.refresh()

    def ping_client(self):
        try:
            pingelem = self.E.ping(xmlns='urn:xmpp:ping')
            return self.iq('get', self.ping_result, pingelem)
        except Exception as e:
            Log.debug("ping client %r failed: %r" % (self.authJID, e))
            Log.debug(traceback.format_exc())
            return False

    def get_uid(self):
        return self._user.GetUID()

    def recv_msg(self, from_, msgtext):
        # got a new message! send it!
        elem = self.E.message({
            'from': from_,
            'to': unicode(self.authJID)
        }, self.E.body(msgtext))
        self.recv(unicode(self.authJID), elem)

    def check_msg(self):
        Log.debug("checking msg for %s" % self._userid)
        msgbox = MsgBox.MsgBox(self._userid)
        msg_count = msgbox.GetMsgCount(all=False)
        my_pid = os.getpid()
        xmpp_read = self.rosters.get_xmpp_read(self._user.GetUID())
        if xmpp_read > msg_count:
            xmpp_read = 0
        Log.debug("total: %d xmpp read: %d" % (msg_count, xmpp_read))
        self.rosters.set_xmpp_read(self._user.GetUID(), msg_count)
        if xmpp_read < msg_count:
            return xmpp_read
        else:
            return -1

    def deliver_msg(self, start):
        Log.debug("deliver msg to %s" % unicode(self.authJID))
        msgbox = MsgBox.MsgBox(self._userid)
        msg_count = msgbox.GetMsgCount(all=False)
        my_pid = os.getpid()
        for i in range(start, msg_count):
            msghead = msgbox.LoadMsgHead(i, all=False)
            if msghead.topid == my_pid:
                msgtext = msgbox.LoadMsgText(msghead)
                self.recv_msg(self.make_jid(msghead.id), msgtext)

    def steal_msg(self):
        Log.debug("stealing msg for %s" % self._userid)
        msgbox = MsgBox.MsgBox(self._userid)
        msg_count = msgbox.GetMsgCount(all=False)
        msg_unread = msgbox.GetUnreadCount()
        read_count = msg_count - msg_unread
        my_pid = os.getpid()
        term_read = self.rosters.get_term_read(self.get_uid())
        term_stealed = self.rosters.get_term_stealed(self.get_uid())

        all_xmpp = True
        new_unread = {}
        # these are unread msgs!
        for i in range(read_count - 1, msg_count):
            if i < 0:  # read_count == 0...
                continue

            msghead = msgbox.LoadMsgHead(i, all=False)
            if i >= read_count and all_xmpp:
                if msghead.topid == my_pid:
                    # still xmpp
                    # RACE!
                    msgbox.GetUnreadMsg()
                else:
                    # not xmpp
                    all_xmpp = False

            if msghead.topid == my_pid:
                # xmpp msg, don't care
                continue

            if i < read_count:  # read_count - 1
                session = self.rosters.find_session(self.authJID.bare,
                                                    msghead.topid)
                if session is None or session.get_mode() != modes.MSG:
                    continue
                Log.debug("considered msg %d as unread" % i)

            # unread msg!
            if msghead.topid not in new_unread:
                Log.debug("for pid %d, first unread at %d" %
                          (msghead.topid, i))
                new_unread[msghead.topid] = i

        final_unread = {}
        to_steal = {}
        to_steal_begin = msg_count

        for pid in term_read:
            if pid in new_unread:
                if new_unread[pid] == term_read[pid][0]:
                    # still unread
                    final_unread[pid] = (term_read[pid][0],
                                         term_read[pid][1] + 1)
                    Log.debug(".. still unread: %d for %d, %d times" %
                              (new_unread[pid], pid, term_read[pid][1] + 1))
                    if final_unread[pid][1] > STEAL_AFTER_SEEN:
                        to_steal[pid] = final_unread[pid]
                        Log.debug(".. let's steal! %d+ from %d" %
                                  (to_steal[pid][0], pid))
                        if pid in term_stealed:
                            steal_begin = max(final_unread[pid][0],
                                              term_stealed[pid] + 1)
                        else:
                            steal_begin = final_unread[pid][0]
                        if steal_begin < to_steal_begin:
                            to_steal_begin = steal_begin
                else:
                    # moved forward
                    final_unread[pid] = (new_unread[pid], 1)
                    Log.debug(".. moved: %d->%d for %d" %
                              (term_read[pid][0], new_unread[pid], pid))
            else:
                # disappeared? consider as read
                Log.debug(".. disappeared: %d" % pid)
                pass
        for pid in new_unread:
            if pid not in term_read:
                # new session
                Log.debug(".. new unread: %d for %d" % (new_unread[pid], pid))
                final_unread[pid] = (new_unread[pid], 1)

        if to_steal:
            Log.debug("steal starting from %d" % to_steal_begin)
            for i in range(to_steal_begin, msg_count):
                msghead = msgbox.LoadMsgHead(i, all=False)
                if msghead.topid == my_pid:
                    Log.debug("skip xmpp %d for %d" % (i, msghead.topid))
                    msgbox.GetUnreadMsg()
                elif msghead.topid in to_steal:
                    if msghead.topid not in term_stealed or i > term_stealed[
                            msghead.topid]:
                        Log.debug("steal! %d from %d" % (i, msghead.topid))
                        # not stealed...
                        msgtext = msgbox.LoadMsgText(msghead)
                        self.recv_msg(self.make_jid(msghead.id), msgtext)
                        term_stealed[msghead.topid] = i
                    else:
                        Log.debug("already stealed: %d from %d" %
                                  (i, msghead.topid))

        self.rosters.set_term_read(self.get_uid(), final_unread)

    @xmpp.stanza('presence')
    def presence(self, elem):
        """Presence information may be sent out from the client or
        received from another account."""

        Log.warn("handle presence. me: %r elem: %r" %
                 (self.authJID, elem_to_str(elem)))
        if self.authJID == elem.get('from'):
            if (elem.get('to') == None
                    or (not self.authJID.match_bare(elem.get('to')))):
                return self.send_presence(elem)
        self.recv_presence(elem)

    def send_presence(self, elem):
        Log.warn("send_presence me: %r elem: %r" %
                 (self.authJID, elem_to_str(elem)))
        # we want to send a presence
        direct = elem.get('to')
        if not direct:
            # not sending directly to one JID
            # send to everyone who is watching me
            self.rosters.broadcast(self, elem)
            if elem.get('type') != 'probe':
                # if it is not a probe, send a copy to the client also
                self.recv_presence(elem)
            if not self.probed:
                # if we have not probed our watch list, probe them
                self.probed = True
                self.rosters.probe(self)
            # check if rosters will handle this
        elif not self.rosters.send(self, direct, elem):
            # if not, send it to the JID specified
            self.send(direct, elem)

    def recv_presence(self, elem):
        Log.warn("recv_presence me: %r elem: %r" %
                 (self.authJID, elem_to_str(elem)))
        # we got a precense
        # check if rosters will handle this
        if not self.rosters.recv(self, elem):
            # if not, send this to the client
            Log.warn("\tsending it to client")
            self.write(elem)

    @xmpp.iq('{jabber:iq:roster}query')
    def roster(self, iq):
        """A roster is this account's list of contacts; it may be
        fetched or updated."""

        roster = self.rosters.get(self)
        method = getattr(self, '%s_roster' % iq.get('type'))
        return method and method(iq, roster)

    def get_roster(self, iq, roster):
        query = self.E.query({'xmlns': 'jabber:iq:roster'})
        for item in roster.items():
            query.append(item)
        return self.iq('result', iq, query)

    def set_roster(self, iq, roster):
        query = self.E.query(xmlns='jabber:iq:roster')
        for item in iq[0]:
            result = roster.set(item)
            if result is not None:
                query.append(result)
        if len(query) > 0:
            self.push(roster, query)
        return self.iq('result', iq)

    def push(self, roster, query):
        """Push roster changes to all clients that have requested this
        roster."""

        for jid in roster.requests():
            for (to, route) in self.routes(jid):
                route.iq('set', self.ignore, query)

    def ignore(self, iq):
        """An IQ no-op."""

    @xmpp.iq('{vcard-temp}vCard')
    def vcard(self, iq):
        """vCard support: the client requests its vCard after
        establishing a session."""

        if iq.get('type') == 'get':
            if (iq.get('to') == None):
                target = iq.get('from')
            else:
                target = iq.get('to')

            form_target = UCache.UCache.formalize_jid(target)
            name = form_target.partition('@')[0]
            user = UserManager.UserManager.LoadUser(name)
            info = user.GetInfo()
            desc = '''\r
Logins: %d\r
Posts: %d\r
Last login: %s from %s\r
Experience: %d\r
Performance: %d\r
Life: %d\r
''' % (info['numlogins'], info['numposts'], info['lastlogintime'],
            info['lasthost'], info['exp'], info['perf'], info['life'])
            if ('plan' in info):
                desc += "Plan:\r%s" % (info['plan'].replace('\n', '\r\n'))

            vcard = self.E.vCard({'xmlns': 'vcard-temp'}, self.E('FN', name),
                                 self.E('NICKNAME',
                                        Util.Util.RemoveTags(info['nick'])),
                                 self.E('DESC', Util.Util.RemoveTags(desc)))

            if (iq.get('to') == None):
                return self.iq('result', iq, vcard)
            else:
                return self.iq('result', iq, vcard, {'from': iq.get('to')})

    @xmpp.iq('{%s}query' % __disco_info_ns__)
    def disco_info(self, iq):
        """ Service Discovery: disco#info """

        target = iq.get('to')

        if (target.find('@') < 0):
            # query server info
            query = self.E.query({'xmlns': __disco_info_ns__},
                                 self.E.identity({
                                     'category':
                                     'server',
                                     'type':
                                     'im',
                                     'name':
                                     Config.Config.GetString(
                                         'XMPP_SERVER_IDENTITY_NAME', 'BBS'),
                                 }))
            features = [__disco_info_ns__, __disco_items_ns__, __vcard_ns__]
            for feature in features:
                query.append(self.E.feature({'var': feature}))

        else:
            # query client info
            query = self.E.query({'xmlns': __disco_info_ns__},
                                 self.E.identity({
                                     'category':
                                     'client',
                                     'type':
                                     'term',
                                     'name':
                                     Config.Config.GetString(
                                         'XMPP_SERVER_IDENTITY_NAME', 'BBS'),
                                 }))

            features = [__disco_info_ns__, __disco_items_ns__, __vcard_ns__]
            for feature in features:
                query.append(self.E.feature({'var': feature}))

        return self.iq('result', iq, query, {'from': target})

    @xmpp.iq('{%s}query' % __disco_items_ns__)
    def disco_items(self, iq):
        """ Service Discovery: disco#items """

        target = iq.get('to')

        if (target.find('@') < 0):
            # query server info
            query = self.E.query({'xmlns': __disco_items_ns__})

        else:
            # query client info
            query = self.E.query({'xmlns': __disco_items_ns__})

        return self.iq('result', iq, query, {'from': target})