1 /*
2 * Copyright (C) 2021 Emweb bv, Herent, Belgium.
3 *
4 * See the LICENSE file for terms of use.
5 */
6 #include "Process.h"
7
8 #include "Service.h"
9
10 #include "ProcessImpl.h"
11 #include "ServiceImpl.h"
12
13 #include "Wt/PopupWindow.h"
14 #include "Wt/WApplication.h"
15 #include "Wt/WEnvironment.h"
16 #include "Wt/WLogger.h"
17 #include "Wt/WResource.h"
18
19 #include "Wt/Auth/Identity.h"
20
21 #include "Wt/Http/Client.h"
22 #include "Wt/Http/Request.h"
23 #include "Wt/Http/Response.h"
24
25 #include "web/WebUtils.h"
26
27 namespace Wt {
28
29 LOGGER("Auth.Saml.Process");
30
31 }
32
33 namespace Wt {
34 namespace Auth {
35 namespace Saml {
36
37 class Process::AuthnRequestResource final : public WResource {
38 public:
39 explicit AuthnRequestResource(Process &process);
40
41 ~AuthnRequestResource() final;
42
43 protected:
44 void handleRequest(const Http::Request &request, Http::Response &response) final;
45
46 private:
47 Process &process_;
48 };
49
50 class Process::PrivateAcsResource final : public WResource {
51 public:
52 explicit PrivateAcsResource(Process &process);
53
54 ~PrivateAcsResource() final;
55
56 protected:
57 void handleRequest(const Http::Request &request, Http::Response &response) final;
58
59 private:
60 Process &process_;
61 };
62
AuthnRequestResource(Process & process)63 Process::AuthnRequestResource::AuthnRequestResource(Process &process)
64 : process_(process)
65 {
66 setTakesUpdateLock(true);
67 }
68
~AuthnRequestResource()69 Process::AuthnRequestResource::~AuthnRequestResource()
70 {
71 beingDeleted();
72 }
73
handleRequest(const Http::Request &,Http::Response & response)74 void Process::AuthnRequestResource::handleRequest(const Http::Request &,
75 Http::Response &response)
76 {
77 bool success = process_.createAuthnRequest(response);
78 if (!success) {
79 response.setStatus(500);
80 response.setMimeType("text/html");
81 response.out() << "<html><body>"
82 << "<h1>SAML Authentication error</h1>"
83 << "</body></html>";
84 }
85 }
86
PrivateAcsResource(Process & process)87 Process::PrivateAcsResource::PrivateAcsResource(Process &process)
88 : process_(process)
89 {
90 setTakesUpdateLock(true);
91 }
92
~PrivateAcsResource()93 Process::PrivateAcsResource::~PrivateAcsResource()
94 {
95 beingDeleted();
96 }
97
handleRequest(const Http::Request & request,Http::Response & response)98 void Process::PrivateAcsResource::handleRequest(const Http::Request &request,
99 Http::Response &response)
100 {
101 if (process_.handleResponse(request)) {
102 #ifndef WT_TARGET_JAVA
103 std::ostream &o = response.out();
104 #else // WT_TARGET_JAVA
105 std::ostream o(response.out());
106 #endif // WT_TARGET_JAVA
107 WApplication *app = WApplication::instance();
108 const bool usePopup = app->environment().ajax() && process_.service_.popupEnabled();
109
110 if (!usePopup) {
111 #ifndef WT_TARGET_JAVA
112 WApplication::UpdateLock lock(app);
113 #endif
114 process_.doneCallbackConnection_ =
115 app->unsuspended().connect(&process_, &Process::onSamlDone);
116
117 std::string redirectTo = app->makeAbsoluteUrl(app->url(process_.startInternalPath_));
118 response.setStatus(303);
119 response.addHeader("Location", redirectTo);
120 } else {
121 std::string appJs = app->javaScriptClass();
122 o <<
123 "<!DOCTYPE html>"
124 "<html lang=\"en\" dir=\"ltr\">\n"
125 "<head><title></title>\n"
126 "<script type=\"text/javascript\">\n"
127 "function load() { "
128 """if (window.opener." << appJs << ") {"
129 "" "var " << appJs << "= window.opener." << appJs << ";"
130 #ifndef WT_TARGET_JAVA
131 << process_.redirected_.createCall({}) << ";"
132 #else // WT_TARGET_JAVA
133 << process_.redirected_.createCall() << ";"
134 #endif // WT_TARGET_JAVA
135 "" "window.close();"
136 "}\n"
137 "}\n"
138 "</script></head>"
139 "<body onload=\"load();\"></body></html>";
140 }
141 } else {
142 response.setStatus(500);
143 response.setMimeType("text/html");
144 response.out() << "<html><body>"
145 << "<h1>SAML Authentication error</h1>"
146 << "</body></html>";
147 }
148 }
149
Process(const Service & service)150 Process::Process(const Service &service)
151 : service_(service),
152 redirected_(this, "redirected")
153 {
154 impl_ = std::make_unique<ProcessImpl>(*this);
155 authnRequestResource_ = std::make_unique<AuthnRequestResource>(*this);
156 privateAcsResource_ = std::make_unique<PrivateAcsResource>(*this);
157
158 WApplication *app = WApplication::instance();
159 PopupWindow::loadJavaScript(app);
160
161 std::string url = app->makeAbsoluteUrl(authnRequestResource_->url());
162
163 redirected_.connect(this, &Process::onSamlDone);
164
165 #ifndef WT_TARGET_JAVA
166 if (service_.popupEnabled()) {
167 WStringStream js;
168 js << WT_CLASS << ".PopupWindow(" WT_CLASS
169 << "," << WWebWidget::jsStringLiteral(url)
170 << ", " << service.popupWidth()
171 << ", " << service.popupHeight() << ");";
172
173 implementJavaScript(&Process::startAuthenticate, js.str());
174 }
175 #endif
176 }
177
~Process()178 Process::~Process()
179 { }
180
startAuthenticate()181 void Process::startAuthenticate()
182 {
183 WApplication *app = WApplication::instance();
184 if (app->environment().javaScript() && service_.popupEnabled()) {
185 return;
186 }
187
188 app->suspend(service_.redirectTimeout_);
189
190 startInternalPath_ = app->internalPath();
191 app->redirect(authnRequestResource_->url());
192 }
193
194 #ifdef WT_TARGET_JAVA
connectStartAuthenticate(EventSignalBase & s)195 void Process::connectStartAuthenticate(EventSignalBase &s)
196 {
197 WApplication *app = WApplication::instance();
198 if (app->environment().javaScript()) {
199 std::string url = app->makeAbsoluteUrl(authnRequestResource_->url());
200 WStringStream js;
201 js << "function(object, event) {"
202 << WT_CLASS ".PopupWindow(" WT_CLASS
203 << "," << WWebWidget::jsStringLiteral(url)
204 << ", " << service_.popupWidth()
205 << ", " << service_.popupHeight() << ");"
206 << "}";
207
208 s.connect(js.str());
209 }
210
211 s.connect(this, &Process::startAuthenticate);
212 }
213 #endif
214
createAuthnRequest(Http::Response & response)215 bool Process::createAuthnRequest(Http::Response &response)
216 {
217 return impl_->createAuthnRequest(response);
218 }
219
handleResponse(const Http::Request & request)220 bool Process::handleResponse(const Http::Request &request)
221 {
222 return impl_->handleResponse(request);
223 }
224
onSamlDone()225 void Process::onSamlDone()
226 {
227 bool success = error_.empty();
228
229 authenticated().emit(success ? service_.assertionToIdentity(assertion_) : Identity());
230
231 if (doneCallbackConnection_.isConnected())
232 doneCallbackConnection_.disconnect();
233 }
234
privateAcsResourceUrl()235 std::string Process::privateAcsResourceUrl() const
236 {
237 return privateAcsResource_->url();
238 }
239
240 }
241 }
242 }
243