Skip to content

Commit

Permalink
feat: compute the set interserction between the user password and bre…
Browse files Browse the repository at this point in the history
…ached passwords

* feat: add password as an endpoint and record the password in the json response

* feat: compute set intersection between user's password and breached hashset

BREAKING CHANGE: computing set intersection is difficult with get request due to password's special symbols changing the URL

* feat: add POST request with password as the body of the request

BREAKING CHANGE: compute set intersection still not finding the user password in breached password set

* fix: change type for post body

* feat: compute set intersection of user password and breached password set

* test: insert a valid password into the database for testing

* feat: add test for crow server running and computing set intersection

* refactor: change the testing function to be more detailed

* refactor: change indentation to 4 tabs

* refactor: move endpoints to server files

* refactor: move post endpoint into server folder

* docs: method comments for server.hpp

* refactor: move the endpoint test to a server test file

BREAKING CHANGE: CMake dependencies will need to be changed to include crow

* refactor: add Crow dependencies

BREAKING CHANGE: linking crow is not working

* feat: error check if request body is null

* update: revert CMakeLists.txt back to working version in previous commits

* build: include Crow in src library and fix header imports

* docs: update documentation for server header file

* refactor: use different names for endpoints

* refactor: use passwords instead of password_set

* feat: serialize passwords as list

* fix: use new endpoint for checking intersection

* perf: use vector reserve

* test: use correct HTTP method for /passwords

* feat: create endpoint for showing all breached passwords and computing set intersection
---------

Co-authored-by: ni-jessica <jessica_ni@brown.edu>
Co-authored-by: Cedric Sirianni <cedric@sirianni.dev>
  • Loading branch information
3 people committed Nov 4, 2023
1 parent 77c3288 commit 3b10b92
Show file tree
Hide file tree
Showing 9 changed files with 175 additions and 28 deletions.
5 changes: 2 additions & 3 deletions backend/src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
find_package(Crow REQUIRED)
find_package(SQLite3 REQUIRED)

add_library(src database.cpp password.cpp)
add_library(src database.cpp password.cpp server.cpp)
target_link_libraries(src Crow::Crow)

add_executable(server main.cpp)

target_link_libraries(server Crow::Crow SQLite::SQLite3 src)

target_include_directories(server PRIVATE src)
27 changes: 12 additions & 15 deletions backend/src/main.cpp
Original file line number Diff line number Diff line change
@@ -1,43 +1,40 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unordered_set>
#include "crow.h"
#include "crow/middlewares/cors.h"
#include "database.hpp"
#include "password.hpp"
#include "server.hpp"

int main()
{
database::Database db = database::Database("passwords.db");
db.execute("CREATE TABLE passwords (password TEXT);");

// generate and insert the passwords into the database
std::unordered_set<std::string> password_set = password::generatePasswords(100, 20);
for (const auto &password : password_set)
std::unordered_set<std::string> passwords = password::generatePasswords(100, 20);
for (const auto &password : passwords)
{
db.execute("INSERT INTO passwords (password) VALUES ('" + password + "');");
}
db.execute("INSERT INTO passwords (password) VALUES ('chocolate1');");
db.printTable("passwords");
// test password
passwords.insert("TestPass1&");
db.execute("INSERT INTO passwords (password) VALUES ('TestPass1&');");

// Enable CORS
crow::App<crow::CORSHandler> app;

// Customize CORS
auto &cors = app.get_middleware<crow::CORSHandler>();

cors.global().headers("*").methods("POST"_method, "GET"_method);
cors.global().headers("*").methods("POST"_method);

CROW_ROUTE(app, "/")
([]()
{
crow::json::wvalue response;
response["status"] = "success";
return response; });

CROW_ROUTE(app, "/cors")
([]()
{ return "Check Access-Control-Allow-Origin header"; });
// initialize endpoints
server::root(app);
server::passwords(app, passwords);
server::intersection(app, passwords);

// set the port, set the app to run on multiple threads, and run the app
app.port(18080).multithreaded().run();
Expand Down
57 changes: 57 additions & 0 deletions backend/src/server.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#include "server.hpp"

namespace server
{
void root(crow::App<crow::CORSHandler> &app)
{
CROW_ROUTE(app, "/")
([]()
{ crow::json::wvalue response;
response["status"] = "success";
response["data"] = "server is now running";
return response; });
}

void passwords(crow::App<crow::CORSHandler> &app, const std::unordered_set<std::string> &passwords)
{
CROW_ROUTE(app, "/passwords")
([passwords]()
{ crow::json::wvalue response;
std::vector<std::string> result;
result.reserve(passwords.size());
for (const auto &password : passwords)
{
result.push_back(password);
}
response["passwords"] = result;
return response; });
}

void intersection(crow::App<crow::CORSHandler> &app, const std::unordered_set<std::string> &passwords)
{
CROW_ROUTE(app, "/intersection")
.methods("POST"_method)([passwords](const crow::request &req)
{
crow::json::wvalue response;

std::string user_password = req.body;
if (user_password.empty())
{
response["status"] = "error";
return response;
}

const bool is_breached = passwords.find(user_password) != passwords.end();
if (is_breached)
{
response["status"] = "fail";
}
else
{
response["status"] = "success";
}

return response; });
}

};
34 changes: 34 additions & 0 deletions backend/src/server.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include <string.h>
#include <unordered_set>
#include "crow.h"
#include "crow/middlewares/cors.h"

#ifndef SERVER_H
#define SERVER_H

namespace server
{
/**
* @brief Endpoint to check the server is running.
*
* @param app The crow server.
*/
void root(crow::App<crow::CORSHandler> &app);

/**
* @brief Endpoint to show all breached passwords.
*
* @param app The crow server.
* @param passwords The set of all breached passwords.
*/
void passwords(crow::App<crow::CORSHandler> &app, const std::unordered_set<std::string> &passwords);

/**
* @brief Endpoint to compute set intersection.
*
* @param app The crow server.
* @param passwords The set of all breached passwords.
*/
void intersection(crow::App<crow::CORSHandler> &app, const std::unordered_set<std::string> &passwords);
}
#endif // SERVER_H
1 change: 1 addition & 0 deletions backend/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ find_package(Catch2 REQUIRED)

set (TEST_SOURCE
password.cpp
server.cpp
)

add_executable(pdl_test ${TEST_SOURCE})
Expand Down
8 changes: 4 additions & 4 deletions backend/test/password.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#define CATCH_CONFIG_MAIN
#include <regex>
#include <catch2/catch_test_macros.hpp>
#include "../src/password.hpp"
#include "password.hpp"

namespace
{
Expand All @@ -19,11 +19,11 @@ namespace

TEST_CASE("Test generatePassword creates valid passwords")
{
std::unordered_set<std::string> password_set = password::generatePasswords(3, 12);
std::unordered_set<std::string> passwords = password::generatePasswords(3, 12);
// function generated 3 passwords
CHECK(password_set.size() == 3);
CHECK(passwords.size() == 3);

for (const auto &password : password_set)
for (const auto &password : passwords)
{
CHECK(password.length() >= 10);
CHECK(hasLettersAndDigit(password));
Expand Down
57 changes: 57 additions & 0 deletions backend/test/server.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#define CATCH_CONFIG_MAIN
#include <catch2/catch_test_macros.hpp>
#include "server.hpp"
#include "password.hpp"

TEST_CASE("Test endpoints return response code 200")
{
// enable CORS
crow::App<crow::CORSHandler> app;

// customize CORS
auto &cors = app.get_middleware<crow::CORSHandler>();

cors.global().headers("*").methods("POST"_method);

// create a mock password set
std::unordered_set<std::string> passwords = password::generatePasswords(3, 12);

// initialize endpoints
server::root(app);
server::passwords(app, passwords);
server::intersection(app, passwords);

// check that all the route handlers were created
app.validate();

crow::request req;
crow::response res;

SECTION("Root")
{
req.url = "/";

app.handle(req, res);
CHECK(res.code == 200);
}

SECTION("Passwords")
{
req.url = "/passwords";

app.handle(req, res);
CHECK(res.code == 200);
}

SECTION("Intersection")
{
req.url = "/intersection";
req.method = "POST"_method;
req.add_header("Access-Control-Allow-Headers", "*");
req.add_header("Content-Type", "application/json");
req.body = "TestPass1&";

app.handle(req, res);
CHECK(res.code == 200);
}
}
12 changes: 7 additions & 5 deletions frontend/app/handlers.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
// Make API call to server to check if password was found in breached dataset
export const checkSecurity = async () => {
export const checkSecurity = async (password: string) => {
try {
const response = await fetch("http://localhost:18080", {
method: "GET",
const response = await fetch("http://localhost:18080/intersection", {
method: "POST",
mode: "cors",
headers: {
"Access-Control-Allow-Headers": "*",
"Access-Control-Allow-Headers": "*", // cors setting
"Content-Type": "application/json"
},
body: password
})
const data = await response.json();
return data;
} catch (error) {
console.error("Error fetching data:", error);
return { status: "error" };
}
};
};
2 changes: 1 addition & 1 deletion frontend/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export default function SignUp() {
) => {
event.preventDefault();
setIsLoading(true);
const response = await checkSecurity(); // makes an API call to the backend
const response = await checkSecurity(password); // makes an API call with the user's password
setIsLoading(false);

if (response.status == "success") {
Expand Down

0 comments on commit 3b10b92

Please sign in to comment.