Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
server.py733 linesDownload Raw Back to paramiko
1# Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>2#3# This file is part of paramiko.4#5# Paramiko is free software; you can redistribute it and/or modify it under the6# terms of the GNU Lesser General Public License as published by the Free7# Software Foundation; either version 2.1 of the License, or (at your option)8# any later version.9#10# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY11# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR12# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more13# details.14#15# You should have received a copy of the GNU Lesser General Public License16# along with Paramiko; if not, write to the Free Software Foundation, Inc.,17# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.18 19"""20`.ServerInterface` is an interface to override for server support.21"""22 23import threading24from paramiko import util25from paramiko.common import (26    DEBUG,27    ERROR,28    OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED,29    AUTH_FAILED,30    AUTH_SUCCESSFUL,31)32 33 34class ServerInterface:35    """36    This class defines an interface for controlling the behavior of Paramiko37    in server mode.38 39    Methods on this class are called from Paramiko's primary thread, so you40    shouldn't do too much work in them.  (Certainly nothing that blocks or41    sleeps.)42    """43 44    def check_channel_request(self, kind, chanid):45        """46        Determine if a channel request of a given type will be granted, and47        return ``OPEN_SUCCEEDED`` or an error code.  This method is48        called in server mode when the client requests a channel, after49        authentication is complete.50 51        If you allow channel requests (and an ssh server that didn't would be52        useless), you should also override some of the channel request methods53        below, which are used to determine which services will be allowed on54        a given channel:55 56            - `check_channel_pty_request`57            - `check_channel_shell_request`58            - `check_channel_subsystem_request`59            - `check_channel_window_change_request`60            - `check_channel_x11_request`61            - `check_channel_forward_agent_request`62 63        The ``chanid`` parameter is a small number that uniquely identifies the64        channel within a `.Transport`.  A `.Channel` object is not created65        unless this method returns ``OPEN_SUCCEEDED`` -- once a66        `.Channel` object is created, you can call `.Channel.get_id` to67        retrieve the channel ID.68 69        The return value should either be ``OPEN_SUCCEEDED`` (or70        ``0``) to allow the channel request, or one of the following error71        codes to reject it:72 73            - ``OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED``74            - ``OPEN_FAILED_CONNECT_FAILED``75            - ``OPEN_FAILED_UNKNOWN_CHANNEL_TYPE``76            - ``OPEN_FAILED_RESOURCE_SHORTAGE``77 78        The default implementation always returns79        ``OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED``.80 81        :param str kind:82            the kind of channel the client would like to open (usually83            ``"session"``).84        :param int chanid: ID of the channel85        :return: an `int` success or failure code (listed above)86        """87        return OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED88 89    def get_allowed_auths(self, username):90        """91        Return a list of authentication methods supported by the server.92        This list is sent to clients attempting to authenticate, to inform them93        of authentication methods that might be successful.94 95        The "list" is actually a string of comma-separated names of types of96        authentication.  Possible values are ``"password"``, ``"publickey"``,97        and ``"none"``.98 99        The default implementation always returns ``"password"``.100 101        :param str username: the username requesting authentication.102        :return: a comma-separated `str` of authentication types103        """104        return "password"105 106    def check_auth_none(self, username):107        """108        Determine if a client may open channels with no (further)109        authentication.110 111        Return ``AUTH_FAILED`` if the client must authenticate, or112        ``AUTH_SUCCESSFUL`` if it's okay for the client to not113        authenticate.114 115        The default implementation always returns ``AUTH_FAILED``.116 117        :param str username: the username of the client.118        :return:119            ``AUTH_FAILED`` if the authentication fails; ``AUTH_SUCCESSFUL`` if120            it succeeds.121        :rtype: int122        """123        return AUTH_FAILED124 125    def check_auth_password(self, username, password):126        """127        Determine if a given username and password supplied by the client is128        acceptable for use in authentication.129 130        Return ``AUTH_FAILED`` if the password is not accepted,131        ``AUTH_SUCCESSFUL`` if the password is accepted and completes132        the authentication, or ``AUTH_PARTIALLY_SUCCESSFUL`` if your133        authentication is stateful, and this key is accepted for134        authentication, but more authentication is required.  (In this latter135        case, `get_allowed_auths` will be called to report to the client what136        options it has for continuing the authentication.)137 138        The default implementation always returns ``AUTH_FAILED``.139 140        :param str username: the username of the authenticating client.141        :param str password: the password given by the client.142        :return:143            ``AUTH_FAILED`` if the authentication fails; ``AUTH_SUCCESSFUL`` if144            it succeeds; ``AUTH_PARTIALLY_SUCCESSFUL`` if the password auth is145            successful, but authentication must continue.146        :rtype: int147        """148        return AUTH_FAILED149 150    def check_auth_publickey(self, username, key):151        """152        Determine if a given key supplied by the client is acceptable for use153        in authentication.  You should override this method in server mode to154        check the username and key and decide if you would accept a signature155        made using this key.156 157        Return ``AUTH_FAILED`` if the key is not accepted,158        ``AUTH_SUCCESSFUL`` if the key is accepted and completes the159        authentication, or ``AUTH_PARTIALLY_SUCCESSFUL`` if your160        authentication is stateful, and this password is accepted for161        authentication, but more authentication is required.  (In this latter162        case, `get_allowed_auths` will be called to report to the client what163        options it has for continuing the authentication.)164 165        Note that you don't have to actually verify any key signtature here.166        If you're willing to accept the key, Paramiko will do the work of167        verifying the client's signature.168 169        The default implementation always returns ``AUTH_FAILED``.170 171        :param str username: the username of the authenticating client172        :param .PKey key: the key object provided by the client173        :return:174            ``AUTH_FAILED`` if the client can't authenticate with this key;175            ``AUTH_SUCCESSFUL`` if it can; ``AUTH_PARTIALLY_SUCCESSFUL`` if it176            can authenticate with this key but must continue with177            authentication178        :rtype: int179        """180        return AUTH_FAILED181 182    def check_auth_interactive(self, username, submethods):183        """184        Begin an interactive authentication challenge, if supported.  You185        should override this method in server mode if you want to support the186        ``"keyboard-interactive"`` auth type, which requires you to send a187        series of questions for the client to answer.188 189        Return ``AUTH_FAILED`` if this auth method isn't supported.  Otherwise,190        you should return an `.InteractiveQuery` object containing the prompts191        and instructions for the user.  The response will be sent via a call192        to `check_auth_interactive_response`.193 194        The default implementation always returns ``AUTH_FAILED``.195 196        :param str username: the username of the authenticating client197        :param str submethods:198            a comma-separated list of methods preferred by the client (usually199            empty)200        :return:201            ``AUTH_FAILED`` if this auth method isn't supported; otherwise an202            object containing queries for the user203        :rtype: int or `.InteractiveQuery`204        """205        return AUTH_FAILED206 207    def check_auth_interactive_response(self, responses):208        """209        Continue or finish an interactive authentication challenge, if210        supported.  You should override this method in server mode if you want211        to support the ``"keyboard-interactive"`` auth type.212 213        Return ``AUTH_FAILED`` if the responses are not accepted,214        ``AUTH_SUCCESSFUL`` if the responses are accepted and complete215        the authentication, or ``AUTH_PARTIALLY_SUCCESSFUL`` if your216        authentication is stateful, and this set of responses is accepted for217        authentication, but more authentication is required.  (In this latter218        case, `get_allowed_auths` will be called to report to the client what219        options it has for continuing the authentication.)220 221        If you wish to continue interactive authentication with more questions,222        you may return an `.InteractiveQuery` object, which should cause the223        client to respond with more answers, calling this method again.  This224        cycle can continue indefinitely.225 226        The default implementation always returns ``AUTH_FAILED``.227 228        :param responses: list of `str` responses from the client229        :return:230            ``AUTH_FAILED`` if the authentication fails; ``AUTH_SUCCESSFUL`` if231            it succeeds; ``AUTH_PARTIALLY_SUCCESSFUL`` if the interactive auth232            is successful, but authentication must continue; otherwise an233            object containing queries for the user234        :rtype: int or `.InteractiveQuery`235        """236        return AUTH_FAILED237 238    def check_auth_gssapi_with_mic(239        self, username, gss_authenticated=AUTH_FAILED, cc_file=None240    ):241        """242        Authenticate the given user to the server if he is a valid krb5243        principal.244 245        :param str username: The username of the authenticating client246        :param int gss_authenticated: The result of the krb5 authentication247        :param str cc_filename: The krb5 client credentials cache filename248        :return: ``AUTH_FAILED`` if the user is not authenticated otherwise249                 ``AUTH_SUCCESSFUL``250        :rtype: int251        :note: Kerberos credential delegation is not supported.252        :see: `.ssh_gss`253        :note: : We are just checking in L{AuthHandler} that the given user is254                 a valid krb5 principal!255                 We don't check if the krb5 principal is allowed to log in on256                 the server, because there is no way to do that in python. So257                 if you develop your own SSH server with paramiko for a certain258                 platform like Linux, you should call C{krb5_kuserok()} in259                 your local kerberos library to make sure that the260                 krb5_principal has an account on the server and is allowed to261                 log in as a user.262        :see: http://www.unix.com/man-page/all/3/krb5_kuserok/263        """264        if gss_authenticated == AUTH_SUCCESSFUL:265            return AUTH_SUCCESSFUL266        return AUTH_FAILED267 268    def check_auth_gssapi_keyex(269        self, username, gss_authenticated=AUTH_FAILED, cc_file=None270    ):271        """272        Authenticate the given user to the server if he is a valid krb5273        principal and GSS-API Key Exchange was performed.274        If GSS-API Key Exchange was not performed, this authentication method275        won't be available.276 277        :param str username: The username of the authenticating client278        :param int gss_authenticated: The result of the krb5 authentication279        :param str cc_filename: The krb5 client credentials cache filename280        :return: ``AUTH_FAILED`` if the user is not authenticated otherwise281                 ``AUTH_SUCCESSFUL``282        :rtype: int283        :note: Kerberos credential delegation is not supported.284        :see: `.ssh_gss` `.kex_gss`285        :note: : We are just checking in L{AuthHandler} that the given user is286                 a valid krb5 principal!287                 We don't check if the krb5 principal is allowed to log in on288                 the server, because there is no way to do that in python. So289                 if you develop your own SSH server with paramiko for a certain290                 platform like Linux, you should call C{krb5_kuserok()} in291                 your local kerberos library to make sure that the292                 krb5_principal has an account on the server and is allowed293                 to log in as a user.294        :see: http://www.unix.com/man-page/all/3/krb5_kuserok/295        """296        if gss_authenticated == AUTH_SUCCESSFUL:297            return AUTH_SUCCESSFUL298        return AUTH_FAILED299 300    def enable_auth_gssapi(self):301        """302        Overwrite this function in your SSH server to enable GSSAPI303        authentication.304        The default implementation always returns false.305 306        :returns bool: Whether GSSAPI authentication is enabled.307        :see: `.ssh_gss`308        """309        UseGSSAPI = False310        return UseGSSAPI311 312    def check_port_forward_request(self, address, port):313        """314        Handle a request for port forwarding.  The client is asking that315        connections to the given address and port be forwarded back across316        this ssh connection.  An address of ``"0.0.0.0"`` indicates a global317        address (any address associated with this server) and a port of ``0``318        indicates that no specific port is requested (usually the OS will pick319        a port).320 321        The default implementation always returns ``False``, rejecting the322        port forwarding request.  If the request is accepted, you should return323        the port opened for listening.324 325        :param str address: the requested address326        :param int port: the requested port327        :return:328            the port number (`int`) that was opened for listening, or ``False``329            to reject330        """331        return False332 333    def cancel_port_forward_request(self, address, port):334        """335        The client would like to cancel a previous port-forwarding request.336        If the given address and port is being forwarded across this ssh337        connection, the port should be closed.338 339        :param str address: the forwarded address340        :param int port: the forwarded port341        """342        pass343 344    def check_global_request(self, kind, msg):345        """346        Handle a global request of the given ``kind``.  This method is called347        in server mode and client mode, whenever the remote host makes a global348        request.  If there are any arguments to the request, they will be in349        ``msg``.350 351        There aren't any useful global requests defined, aside from port352        forwarding, so usually this type of request is an extension to the353        protocol.354 355        If the request was successful and you would like to return contextual356        data to the remote host, return a tuple.  Items in the tuple will be357        sent back with the successful result.  (Note that the items in the358        tuple can only be strings, ints, or bools.)359 360        The default implementation always returns ``False``, indicating that it361        does not support any global requests.362 363        .. note:: Port forwarding requests are handled separately, in364            `check_port_forward_request`.365 366        :param str kind: the kind of global request being made.367        :param .Message msg: any extra arguments to the request.368        :return:369            ``True`` or a `tuple` of data if the request was granted; ``False``370            otherwise.371        """372        return False373 374    # ...Channel requests...375 376    def check_channel_pty_request(377        self, channel, term, width, height, pixelwidth, pixelheight, modes378    ):379        """380        Determine if a pseudo-terminal of the given dimensions (usually381        requested for shell access) can be provided on the given channel.382 383        The default implementation always returns ``False``.384 385        :param .Channel channel: the `.Channel` the pty request arrived on.386        :param str term: type of terminal requested (for example, ``"vt100"``).387        :param int width: width of screen in characters.388        :param int height: height of screen in characters.389        :param int pixelwidth:390            width of screen in pixels, if known (may be ``0`` if unknown).391        :param int pixelheight:392            height of screen in pixels, if known (may be ``0`` if unknown).393        :return:394            ``True`` if the pseudo-terminal has been allocated; ``False``395            otherwise.396        """397        return False398 399    def check_channel_shell_request(self, channel):400        """401        Determine if a shell will be provided to the client on the given402        channel.  If this method returns ``True``, the channel should be403        connected to the stdin/stdout of a shell (or something that acts like404        a shell).405 406        The default implementation always returns ``False``.407 408        :param .Channel channel: the `.Channel` the request arrived on.409        :return:410            ``True`` if this channel is now hooked up to a shell; ``False`` if411            a shell can't or won't be provided.412        """413        return False414 415    def check_channel_exec_request(self, channel, command):416        """417        Determine if a shell command will be executed for the client.  If this418        method returns ``True``, the channel should be connected to the stdin,419        stdout, and stderr of the shell command.420 421        The default implementation always returns ``False``.422 423        :param .Channel channel: the `.Channel` the request arrived on.424        :param str command: the command to execute.425        :return:426            ``True`` if this channel is now hooked up to the stdin, stdout, and427            stderr of the executing command; ``False`` if the command will not428            be executed.429 430        .. versionadded:: 1.1431        """432        return False433 434    def check_channel_subsystem_request(self, channel, name):435        """436        Determine if a requested subsystem will be provided to the client on437        the given channel.  If this method returns ``True``, all future I/O438        through this channel will be assumed to be connected to the requested439        subsystem.  An example of a subsystem is ``sftp``.440 441        The default implementation checks for a subsystem handler assigned via442        `.Transport.set_subsystem_handler`.443        If one has been set, the handler is invoked and this method returns444        ``True``.  Otherwise it returns ``False``.445 446        .. note:: Because the default implementation uses the `.Transport` to447            identify valid subsystems, you probably won't need to override this448            method.449 450        :param .Channel channel: the `.Channel` the pty request arrived on.451        :param str name: name of the requested subsystem.452        :return:453            ``True`` if this channel is now hooked up to the requested454            subsystem; ``False`` if that subsystem can't or won't be provided.455        """456        transport = channel.get_transport()457        handler_class, args, kwargs = transport._get_subsystem_handler(name)458        if handler_class is None:459            return False460        handler = handler_class(channel, name, self, *args, **kwargs)461        handler.start()462        return True463 464    def check_channel_window_change_request(465        self, channel, width, height, pixelwidth, pixelheight466    ):467        """468        Determine if the pseudo-terminal on the given channel can be resized.469        This only makes sense if a pty was previously allocated on it.470 471        The default implementation always returns ``False``.472 473        :param .Channel channel: the `.Channel` the pty request arrived on.474        :param int width: width of screen in characters.475        :param int height: height of screen in characters.476        :param int pixelwidth:477            width of screen in pixels, if known (may be ``0`` if unknown).478        :param int pixelheight:479            height of screen in pixels, if known (may be ``0`` if unknown).480        :return: ``True`` if the terminal was resized; ``False`` if not.481        """482        return False483 484    def check_channel_x11_request(485        self,486        channel,487        single_connection,488        auth_protocol,489        auth_cookie,490        screen_number,491    ):492        """493        Determine if the client will be provided with an X11 session.  If this494        method returns ``True``, X11 applications should be routed through new495        SSH channels, using `.Transport.open_x11_channel`.496 497        The default implementation always returns ``False``.498 499        :param .Channel channel: the `.Channel` the X11 request arrived on500        :param bool single_connection:501            ``True`` if only a single X11 channel should be opened, else502            ``False``.503        :param str auth_protocol: the protocol used for X11 authentication504        :param str auth_cookie: the cookie used to authenticate to X11505        :param int screen_number: the number of the X11 screen to connect to506        :return: ``True`` if the X11 session was opened; ``False`` if not507        """508        return False509 510    def check_channel_forward_agent_request(self, channel):511        """512        Determine if the client will be provided with an forward agent session.513        If this method returns ``True``, the server will allow SSH Agent514        forwarding.515 516        The default implementation always returns ``False``.517 518        :param .Channel channel: the `.Channel` the request arrived on519        :return: ``True`` if the AgentForward was loaded; ``False`` if not520 521        If ``True`` is returned, the server should create an522        :class:`AgentServerProxy` to access the agent.523        """524        return False525 526    def check_channel_direct_tcpip_request(self, chanid, origin, destination):527        """528        Determine if a local port forwarding channel will be granted, and529        return ``OPEN_SUCCEEDED`` or an error code.  This method is530        called in server mode when the client requests a channel, after531        authentication is complete.532 533        The ``chanid`` parameter is a small number that uniquely identifies the534        channel within a `.Transport`.  A `.Channel` object is not created535        unless this method returns ``OPEN_SUCCEEDED`` -- once a536        `.Channel` object is created, you can call `.Channel.get_id` to537        retrieve the channel ID.538 539        The origin and destination parameters are (ip_address, port) tuples540        that correspond to both ends of the TCP connection in the forwarding541        tunnel.542 543        The return value should either be ``OPEN_SUCCEEDED`` (or544        ``0``) to allow the channel request, or one of the following error545        codes to reject it:546 547            - ``OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED``548            - ``OPEN_FAILED_CONNECT_FAILED``549            - ``OPEN_FAILED_UNKNOWN_CHANNEL_TYPE``550            - ``OPEN_FAILED_RESOURCE_SHORTAGE``551 552        The default implementation always returns553        ``OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED``.554 555        :param int chanid: ID of the channel556        :param tuple origin:557            2-tuple containing the IP address and port of the originator558            (client side)559        :param tuple destination:560            2-tuple containing the IP address and port of the destination561            (server side)562        :return: an `int` success or failure code (listed above)563        """564        return OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED565 566    def check_channel_env_request(self, channel, name, value):567        """568        Check whether a given environment variable can be specified for the569        given channel.  This method should return ``True`` if the server570        is willing to set the specified environment variable.  Note that571        some environment variables (e.g., PATH) can be exceedingly572        dangerous, so blindly allowing the client to set the environment573        is almost certainly not a good idea.574 575        The default implementation always returns ``False``.576 577        :param channel: the `.Channel` the env request arrived on578        :param str name: name579        :param str value: Channel value580        :returns: A boolean581        """582        return False583 584    def get_banner(self):585        """586        A pre-login banner to display to the user. The message may span587        multiple lines separated by crlf pairs. The language should be in588        rfc3066 style, for example: en-US589 590        The default implementation always returns ``(None, None)``.591 592        :returns: A tuple containing the banner and language code.593 594        .. versionadded:: 2.3595        """596        return (None, None)597 598 599class InteractiveQuery:600    """601    A query (set of prompts) for a user during interactive authentication.602    """603 604    def __init__(self, name="", instructions="", *prompts):605        """606        Create a new interactive query to send to the client.  The name and607        instructions are optional, but are generally displayed to the end608        user.  A list of prompts may be included, or they may be added via609        the `add_prompt` method.610 611        :param str name: name of this query612        :param str instructions:613            user instructions (usually short) about this query614        :param str prompts: one or more authentication prompts615        """616        self.name = name617        self.instructions = instructions618        self.prompts = []619        for x in prompts:620            if isinstance(x, str):621                self.add_prompt(x)622            else:623                self.add_prompt(x[0], x[1])624 625    def add_prompt(self, prompt, echo=True):626        """627        Add a prompt to this query.  The prompt should be a (reasonably short)628        string.  Multiple prompts can be added to the same query.629 630        :param str prompt: the user prompt631        :param bool echo:632            ``True`` (default) if the user's response should be echoed;633            ``False`` if not (for a password or similar)634        """635        self.prompts.append((prompt, echo))636 637 638class SubsystemHandler(threading.Thread):639    """640    Handler for a subsystem in server mode.  If you create a subclass of this641    class and pass it to `.Transport.set_subsystem_handler`, an object of this642    class will be created for each request for this subsystem.  Each new object643    will be executed within its own new thread by calling `start_subsystem`.644    When that method completes, the channel is closed.645 646    For example, if you made a subclass ``MP3Handler`` and registered it as the647    handler for subsystem ``"mp3"``, then whenever a client has successfully648    authenticated and requests subsystem ``"mp3"``, an object of class649    ``MP3Handler`` will be created, and `start_subsystem` will be called on650    it from a new thread.651    """652 653    def __init__(self, channel, name, server):654        """655        Create a new handler for a channel.  This is used by `.ServerInterface`656        to start up a new handler when a channel requests this subsystem.  You657        don't need to override this method, but if you do, be sure to pass the658        ``channel`` and ``name`` parameters through to the original659        ``__init__`` method here.660 661        :param .Channel channel: the channel associated with this662            subsystem request.663        :param str name: name of the requested subsystem.664        :param .ServerInterface server:665            the server object for the session that started this subsystem666        """667        threading.Thread.__init__(self, target=self._run)668        self.__channel = channel669        self.__transport = channel.get_transport()670        self.__name = name671        self.__server = server672 673    def get_server(self):674        """675        Return the `.ServerInterface` object associated with this channel and676        subsystem.677        """678        return self.__server679 680    def _run(self):681        try:682            self.__transport._log(683                DEBUG, "Starting handler for subsystem {}".format(self.__name)684            )685            self.start_subsystem(self.__name, self.__transport, self.__channel)686        except Exception as e:687            self.__transport._log(688                ERROR,689                'Exception in subsystem handler for "{}": {}'.format(690                    self.__name, e691                ),692            )693            self.__transport._log(ERROR, util.tb_strings())694        try:695            self.finish_subsystem()696        except:697            pass698 699    def start_subsystem(self, name, transport, channel):700        """701        Process an ssh subsystem in server mode.  This method is called on a702        new object (and in a new thread) for each subsystem request.  It is703        assumed that all subsystem logic will take place here, and when the704        subsystem is finished, this method will return.  After this method705        returns, the channel is closed.706 707        The combination of ``transport`` and ``channel`` are unique; this708        handler corresponds to exactly one `.Channel` on one `.Transport`.709 710        .. note::711            It is the responsibility of this method to exit if the underlying712            `.Transport` is closed.  This can be done by checking713            `.Transport.is_active` or noticing an EOF on the `.Channel`.  If714            this method loops forever without checking for this case, your715            Python interpreter may refuse to exit because this thread will716            still be running.717 718        :param str name: name of the requested subsystem.719        :param .Transport transport: the server-mode `.Transport`.720        :param .Channel channel: the channel associated with this subsystem721            request.722        """723        pass724 725    def finish_subsystem(self):726        """727        Perform any cleanup at the end of a subsystem.  The default728        implementation just closes the channel.729 730        .. versionadded:: 1.1731        """732        self.__channel.close()733 
codekingpro/portable-devtools · Team Ai