1 #include <QtGui>
2 #if defined(SEAFILE_USE_WEBKIT)
3   #include <QWebView>
4 #else
5   #include <QWebEngineView>
6   #include <QWebEnginePage>
7   #include <QWebEngineProfile>
8   #include <QWebEngineCookieStore>
9   #include "shib-helper.h"
10 #endif
11 #include <QVBoxLayout>
12 #include <QList>
13 #include <QLineEdit>
14 #include <QSslError>
15 #include <QNetworkReply>
16 #include <QNetworkCookie>
17 
18 #include "seafile-applet.h"
19 #include "utils/utils.h"
20 #include "utils/api-utils.h"
21 #include "account-mgr.h"
22 #include "network-mgr.h"
23 
24 #include "shib-login-dialog.h"
25 
26 namespace {
27 
28 const char *kSeahubShibCookieName = "seahub_auth";
29 
30 } // namespace
31 
ShibLoginDialog(const QUrl & url,const QString & computer_name,QWidget * parent)32 ShibLoginDialog::ShibLoginDialog(const QUrl& url,
33                                  const QString& computer_name,
34                                  QWidget *parent)
35     : QDialog(parent),
36       url_(url),
37       cookie_seen_(false)
38 {
39     setWindowTitle(tr("Login with Shibboleth"));
40     setWindowIcon(QIcon(":/images/seafile.png"));
41     setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
42 
43     QVBoxLayout *vlayout = new QVBoxLayout();
44     setLayout(vlayout);
45 
46     address_text_ = new QLineEdit;
47     address_text_->setObjectName("addressText");
48     address_text_->setText(url.toString());
49     address_text_->setReadOnly(true);
50 
51     vlayout->addWidget(address_text_);
52 
53 #if defined(SEAFILE_USE_WEBKIT)
54     webview_ = new QWebView;
55     CustomCookieJar *jar = new CustomCookieJar(this);
56     QNetworkAccessManager *mgr = webview_->page()->networkAccessManager();
57     NetworkManager::instance()->addWatch(mgr);
58     mgr->setCookieJar(jar);
59 
60     connect(mgr, SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError>&)),
61             this, SLOT(sslErrorHandler(QNetworkReply*, const QList<QSslError>&)));
62 
63     connect(jar, SIGNAL(newCookieCreated(const QUrl&, const QNetworkCookie&)),
64             this, SLOT(onNewCookieCreated(const QUrl&, const QNetworkCookie&)));
65 #else
66     webview_ = new QWebEngineView;
67 
68     web_engine_profile_ = new QWebEngineProfile();
69     web_engine_page_ = new QWebEnginePage(web_engine_profile_);
70 
71     webview_->setPage(web_engine_page_);
72     QWebEngineCookieStore *jar = webview_->page()->profile()->cookieStore();
73     connect(jar, SIGNAL(cookieAdded(const QNetworkCookie&)),
74             this, SLOT(onWebEngineCookieAdded(const QNetworkCookie&)));
75 #endif
76 
77     QUrl shib_login_url(url_);
78     QString path = shib_login_url.path();
79     if (!path.endsWith("/")) {
80         path += "/";
81     }
82     path += "shib-login";
83     shib_login_url.setPath(path);
84 
85     connect(webview_, SIGNAL(urlChanged(const QUrl&)),
86             this, SLOT(updateAddressBar(const QUrl&)));
87 
88     vlayout->addWidget(webview_);
89     webview_->load(::includeQueryParams(
90                        shib_login_url, ::getSeafileLoginParams(computer_name, "shib_")));
91 }
92 
93 #if !defined(SEAFILE_USE_WEBKIT)
~ShibLoginDialog()94 ShibLoginDialog::~ShibLoginDialog()
95 {
96 
97     // The web_engine_page_ object must delete before web_engine_profile.
98     web_engine_page_->deleteLater();
99     web_engine_profile_->deleteLater();
100 }
101 #endif
102 
sslErrorHandler(QNetworkReply * reply,const QList<QSslError> & ssl_errors)103 void ShibLoginDialog::sslErrorHandler(QNetworkReply* reply,
104                                       const QList<QSslError> & ssl_errors)
105 {
106     reply->ignoreSslErrors();
107 }
108 
onNewCookieCreated(const QUrl & url,const QNetworkCookie & cookie)109 void ShibLoginDialog::onNewCookieCreated(const QUrl& url, const QNetworkCookie& cookie)
110 {
111     if (cookie_seen_) {
112         return;
113     }
114     QString name = cookie.name();
115     QString value = cookie.value();
116     if (url.host() == url_.host() && name == kSeahubShibCookieName) {
117         Account account = parseAccount(value);
118         if (!account.isValid()) {
119             qWarning("wrong account information from server");
120             return;
121         }
122         cookie_seen_ = true;
123         seafApplet->accountManager()->setCurrentAccount(account);
124         accept();
125     }
126 }
127 
updateAddressBar(const QUrl & url)128 void ShibLoginDialog::updateAddressBar(const QUrl& url)
129 {
130     address_text_->setText(url.toString());
131     // Scroll to the left most.
132     address_text_->home(false);
133 }
134 
135 
136 /**
137  * The cookie value is like seahub_shib="foo@test.com@bd8cc1138", where
138  * foo@test.com is username and bd8cc1138 is api token"
139  */
parseAccount(const QString & cookie_value)140 Account ShibLoginDialog::parseAccount(const QString& cookie_value)
141 {
142     QString txt = cookie_value;
143     if (txt.startsWith("\"")) {
144         txt = txt.mid(1, txt.length() - 2);
145     }
146     int pos = txt.lastIndexOf("@");
147     QString email = txt.left(pos);
148     QString token = txt.right(txt.length() - pos - 1);
149     if (email.isEmpty() or token.isEmpty()) {
150         return Account();
151     }
152     return Account(url_, email, token, 0, true);
153 }
154 
onWebEngineCookieAdded(const QNetworkCookie & cookie)155 void ShibLoginDialog::onWebEngineCookieAdded(const QNetworkCookie& cookie)
156 {
157     // printf("cookie added: %s = %s\n", cookie.name().data(), cookie.value().data());
158     if (cookie.name() == kSeahubShibCookieName) {
159         onNewCookieCreated(url_, cookie);
160     }
161 }
162 
163 
CustomCookieJar(QObject * parent)164 CustomCookieJar::CustomCookieJar(QObject *parent)
165     : QNetworkCookieJar(parent)
166 {
167 }
168 
setCookiesFromUrl(const QList<QNetworkCookie> & cookies,const QUrl & url)169 bool CustomCookieJar::setCookiesFromUrl(const QList<QNetworkCookie>& cookies, const QUrl& url)
170 {
171   if (QNetworkCookieJar::setCookiesFromUrl(cookies, url)) {
172       foreach (const QNetworkCookie& cookie, cookies) {
173           emit newCookieCreated(url, cookie);
174       }
175     return true;
176   }
177 
178   return false;
179 }
180 
181 #if !defined(SEAFILE_USE_WEBKIT)
182 // We create an off-the-record QWebEngineProfile here, because we don't want the
183 // cookie to be persisted (the seahub_auth cookie should be cleared each time
184 // the shib login dialog is called)
SeafileQWebEnginePage(QObject * parent)185 SeafileQWebEnginePage::SeafileQWebEnginePage(QObject *parent)
186     : QWebEnginePage(new QWebEngineProfile(parent), parent)
187 {
188 }
189 
certificateError(const QWebEngineCertificateError & certificateError)190 bool SeafileQWebEnginePage::certificateError(
191     const QWebEngineCertificateError &certificateError)
192 {
193     return true;
194 }
195 
196 #endif
197