NAWA 0.9
Web Application Framework for C++
sessions.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2019-2022 Tobias Flaig.
3 *
4 * This file is part of nawa.
5 *
6 * nawa is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License,
8 * version 3, as published by the Free Software Foundation.
9 *
10 * nawa is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with nawa. If not, see <https://www.gnu.org/licenses/>.
17 */
18
24#include <catch2/catch.hpp>
25#include <nawa/Exception.h>
29
30using namespace nawa;
31using namespace std;
32
33TEST_CASE("nawa::Session class", "[unit][session]") {
34 ConnectionInitContainer connectionInit;
35 connectionInit.requestInit.environment["REMOTE_ADDR"] = "1.2.3.4";
36
37 SECTION("Basic session handling") {
38 string sessionId;
39 {
40 Connection connection(connectionInit);
41 auto& session = connection.session();
42 session.start();
43 CHECK_NOTHROW(session.set("testKey", "testVal"));
44 CHECK(any_cast<string>(session["testKey"]) == "testVal");
45 sessionId = session.getID();
46 }
47 {
48 ConnectionInitContainer connectionInit1 = connectionInit;
49 connectionInit1.requestInit.cookieVars.insert({"SESSION", sessionId});
50 Connection connection(connectionInit1);
51 auto& session = connection.session();
52 session.start();
53 REQUIRE(session.getID() == sessionId);
54 CHECK(any_cast<string>(session["testKey"]) == "testVal");
55 }
56 }
57
58 SECTION("Basic session handling without cookies") {
59 string sessionId;
60 {
61 Connection connection(connectionInit);
62 auto& session = connection.session();
63 sessionId = session.start("");
64 CHECK_NOTHROW(session.set("testKey", "testVal"));
65 CHECK(any_cast<string>(session["testKey"]) == "testVal");
66 }
67 {
68 Connection connection(connectionInit);
69 auto& session = connection.session();
70 string newSessionId = session.start(sessionId);
71 REQUIRE(newSessionId == sessionId);
72 REQUIRE(session.getID() == sessionId);
73 CHECK(any_cast<string>(session["testKey"]) == "testVal");
74 }
75 }
76
77 SECTION("Session autostart") {
78 ConnectionInitContainer connectionInit1 = connectionInit;
79 connectionInit1.config.set({"session", "autostart"}, "on");
80 string sessionId;
81 {
82 Connection connection(connectionInit1);
83 auto& session = connection.session();
84 CHECK_NOTHROW(session.set("testKey", "testVal"));
85 CHECK(any_cast<string>(session["testKey"]) == "testVal");
86 sessionId = session.getID();
87 }
88 {
89 connectionInit1.requestInit.cookieVars.insert({"SESSION", sessionId});
90 Connection connection(connectionInit1);
91 auto& session = connection.session();
92 REQUIRE(session.getID() == sessionId);
93 CHECK(any_cast<string>(session["testKey"]) == "testVal");
94 }
95 }
96
97 SECTION("Attempt to access and modify inactive session") {
98 Connection connection(connectionInit);
99 auto& session = connection.session();
100 CHECK_THROWS_AS(session.set("testKey", "testVal"), Exception);
101 CHECK_THROWS_AS(session.unset("testKey"), Exception);
102 CHECK_NOTHROW(session["testKey"]);
103 }
104
105 SECTION("Invalidation of sessions") {
106 Connection connection(connectionInit);
107 auto& session = connection.session();
108 session.start();
109 CHECK_NOTHROW(session.set("testKey", "testVal"));
110 session.invalidate();
111 CHECK_THROWS_AS(session.set("testKey", "testVal"), Exception);
112 }
113
114 SECTION("Client IP check: same IP") {
115 ConnectionInitContainer connectionInit1 = connectionInit;
116 string clientIPCheckMode = GENERATE("lax", "strict");
117 connectionInit1.config.set({"session", "validate_ip"}, clientIPCheckMode);
118 string sessionId;
119 {
120 Connection connection(connectionInit1);
121 auto& session = connection.session();
122 session.start();
123 CHECK_NOTHROW(session.set("testKey", "testVal"));
124 CHECK(any_cast<string>(session["testKey"]) == "testVal");
125 sessionId = session.getID();
126 }
127 {
128 connectionInit1.requestInit.cookieVars.insert({"SESSION", sessionId});
129 Connection connection(connectionInit1);
130 auto& session = connection.session();
131 session.start();
132 REQUIRE(session.getID() == sessionId);
133 CHECK(any_cast<string>(session["testKey"]) == "testVal");
134 }
135 }
136
137 SECTION("Client IP check: different IP (lax)") {
138 ConnectionInitContainer connectionInit1 = connectionInit;
139 connectionInit1.config.set({"session", "validate_ip"}, "lax");
140 string sessionId;
141 {
142 Connection connection(connectionInit1);
143 auto& session = connection.session();
144 session.start();
145 CHECK_NOTHROW(session.set("testKey", "testVal"));
146 CHECK(any_cast<string>(session["testKey"]) == "testVal");
147 sessionId = session.getID();
148 }
149 {
150 ConnectionInitContainer connectionInit2 = connectionInit1;
151 connectionInit2.requestInit.cookieVars.insert({"SESSION", sessionId});
152 connectionInit2.requestInit.environment["REMOTE_ADDR"] = "1.2.3.5";
153 Connection connection(connectionInit2);
154 auto& session = connection.session();
155 session.start();
156 CHECK_FALSE(session.getID() == sessionId);
157 CHECK_FALSE(session.isSet("testKey"));
158 }
159 {
160 connectionInit1.requestInit.cookieVars.insert({"SESSION", sessionId});
161 Connection connection(connectionInit1);
162 auto& session = connection.session();
163 session.start();
164 REQUIRE(session.getID() == sessionId);
165 CHECK(any_cast<string>(session["testKey"]) == "testVal");
166 }
167 }
168
169 SECTION("Client IP check: different IP (strict)") {
170 ConnectionInitContainer connectionInit1 = connectionInit;
171 connectionInit1.config.set({"session", "validate_ip"}, "strict");
172 string sessionId;
173 {
174 Connection connection(connectionInit1);
175 auto& session = connection.session();
176 session.start();
177 CHECK_NOTHROW(session.set("testKey", "testVal"));
178 CHECK(any_cast<string>(session["testKey"]) == "testVal");
179 sessionId = session.getID();
180 }
181 {
182 ConnectionInitContainer connectionInit2 = connectionInit1;
183 connectionInit2.requestInit.cookieVars.insert({"SESSION", sessionId});
184 connectionInit2.requestInit.environment["REMOTE_ADDR"] = "1.2.3.5";
185 Connection connection(connectionInit2);
186 auto& session = connection.session();
187 session.start();
188 CHECK_FALSE(session.getID() == sessionId);
189 CHECK_FALSE(session.isSet("testKey"));
190 }
191 {
192 connectionInit1.requestInit.cookieVars.insert({"SESSION", sessionId});
193 Connection connection(connectionInit1);
194 auto& session = connection.session();
195 session.start();
196 CHECK_FALSE(session.getID() == sessionId);
197 CHECK_FALSE(session.isSet("testKey"));
198 }
199 }
200}
Container used by request handlers to initiate the nawa::Connection object.
Response object to be passed back to NAWA and accessor to the request.
Exception class that can be used by apps to catch errors resulting from nawa function calls.
Class for managing sessions and getting and setting connection-independent session data.
void set(std::pair< std::string, std::string > key, std::string value)
Definition: Config.cpp:94
nawa::Session & session() noexcept
Definition: Connection.cpp:361
std::string start(std::string sessionId, std::optional< unsigned long > keepalive=std::nullopt)
Definition: Session.cpp:127
Definition: AppInit.h:31
std::unordered_map< std::string, std::string > environment
std::unordered_multimap< std::string, std::string > cookieVars
TEST_CASE("nawa::Session class", "[unit][session]")
Definition: sessions.cpp:33