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