[Evolution-hackers] Move authentication of backends back to the client (3.13.90)



        Hello,
I just committed a change, for 3.13.90 development version, into 
evolution-data-server [1], evolution [2], evolution-ews [3] and 
evolution-mapi [4], which moves authentication back to the clients. 
That is, any credentials (password) prompts are done fully 
asynchronously on the backend side, and the clients are responsible to 
provide the credentials. There is one exception, if the credentials 
are already saved, then they are used first, without any communication 
between the backend and the clients.

This is done in a very similar way as it used to be couple years ago, 
except the notifications about credentials requests and 
authentications responses are handled on top of ESource objects. I 
chose the ESource object, because it already manages password saving 
and because some backends, those on the source registry side, cannot 
be accessed in any different way. There is also a "proxy" in EBackend, 
which works on top of the ESource methods and signals, making things 
easier for the backend implementators. The backend "proxy" also makes 
couple things for free, based on the existence of certain ESource 
extensions, which are updated accordingly during the authentication 
process. The extensions are E_SOURCE_EXTENSION_AUTHENTICATION and 
E_SOURCE_EXTENSION_WEBDAV_BACKEND. The first is updated when the 
received credentials contain also user name, the second is updated 
with a trust prompt results, which are also passed into the backend as 
part of the credentials. That's required, because the change on the 
ESource can be propagated to backends too late, due to the round-trip 
from the client to evolution-source-registry, and only then to the 
factory, while the client talks to the factory directly.

The backend open and the authentication process currently looks like:

        client-side                        | D-Bus |    backend-side
    -----------------------------------------------------------------------
     e_client_open()                       | ----> | ECal/BookBackend::open() is called:
                                           |       | a) either set ESource::connection-status to CONNECTED
                                           |       | b) or try to connect to the server without credentials
                                           |       |    and use e_backend_credentials_required/_sync() or
                                           | <---- |    e_backend_schedule_credentials_required() to broadcast
                                           |       |    a request for credentials to any client listening
                                           |       |
     listener of ESource::credentials-requ |       |
     ired signal can ask for actual        |       |
     credentials a user and send them back |       |
     with e_source_invoke_authenticate()   |       |
                                           | ----> | implement EBackendClass::authenticate_sync() to receive
                                           |       | and use the provided credentials. Based on the result
                                           |       | of this function is updated ESource::connection-status
                                           |       | automatically and the output arguments are used to
                                           |       | iterate with e_backend_credentials_required() again
                                           |       |

The main differences from the pre-this-change behaviour are:
a) the backend can open without being connected to the destination
   (possibly remote) data store
b) the ESource::credentials-required signal is delivered to all
   current listeners of evolution-source-registry, not only to the one
   which requested the open of the backend
c) the backend works with the new ESource::connection-status in its
   open() implementation only, as long as it uses the provided "proxy"
   functions on the EBackend master class.

As mentioned above, the credentials-required reasons also contain an 
SSL failures (aka when the destination server certificate checking 
fails for some reason), which allows to tight the Trust Prompts to the 
clients as well, thus they are not shown "out of blue" in the system 
anymore.

I re-introduces libedataserverui library, which holds structures to 
handle credential prompts and show the trust prompts directly in the 
application's UI. The library is built conditionally, only when the 
GTK+ is available.

The easiest way to implement credential prompt in an application is to 
create an ECredentialsPrompter and let it live for a whole time of the 
application. It listens for ESource::credentials-required signal and 
responds only to REQUIRED and REJECTED reasons. There can be disabled 
this auto-prompt, either globally or for respective sources. This 
credential prompter also listens for ESource::connection-status 
changes and cancels any ongoing prompts when it changes. That's one of 
the reasons why the ESource::connection-status is used during the 
authentication phase. That means that this ECredentialsPrompter is 
written to handle the situation with multiple clients responding to 
the credential prompt gracefully, by hiding (cancelling) the prompt 
when it is no longer needed (when another client provided the 
credentials). A similar functionality is made within the ETrustPrompt.

Evolution itself also uses ECredentialsPrompter. It also checks all 
known sources right after open for their ESource::connection-status 
state and shows a note in an infobar of the window for those which 
failed due to SSL certificate, with a button to "View Certificate" and 
eventually accept or reject the certificate trust. It also handles 
those sources with AWAITING_CREDENTIALS connection status, in the same 
way, an infobar message is shown, this time with a "Reconnect" button. 
Evolution doesn't retry with old credentials on start, it rather asks 
for the last provided values, which helps to not spam the server with 
invalid credentials.

I keep calling the 'credentials' and not simply 'password' above, 
because the ECredentialsPrompter is made "plugin-aware". I mean with 
that, that there can be written more than just a password prompter 
(the only current implementation which allows to change a user name as 
well), but also any other credential provider and prompter. That 
allows to create login methods for GSSAPI/Kerberos, OAuth and so on 
directly in evolution-data-server and let backends use these methods 
too (the Camel has this functionality already, but it cannot be easily 
reused in EBackend-s). I aim on the OAuth, to allow creating for 
example Google sources which require it even without GNOME Online 
Accounts - like with the GTasks backend. Another useful case can be 
the Two-Factor authentication, instead of using application-specific 
password.

The other follow-up work will be to adapt any "clients" and libraries 
which might be affected by this change. I'd like to help as much as 
I'll be able, thus if there will be any issues spotted, feel free to 
ping me or drop me an email and I'll help you to migrate your code. I 
also removed all the old authentication code and functions, to avoid 
those system-modal prompts and basically all the old behavior users 
didn't like, the same as to cleanup API as much as possible.
        Bye,
        Milan

[1] https://git.gnome.org/browse/evolution-data-server/commit/?id=884fb8d872787d9
[2] https://git.gnome.org/browse/evolution/commit/?id=a6e34f0bb7fd2cf
[3] https://git.gnome.org/browse/evolution-ews/commit/?id=115540270568f77
[4] https://git.gnome.org/browse/evolution-mapi/commit/?id=e568bc225455969


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]