Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,9 @@ jobs:
pnpm install
pnpm run install-playwright
pnpm run test
- name: Upload test artifacts
uses: actions/upload-artifact@v3
if: failure()
with:
name: test-results
path: ./server/e2e_tests/test-results/
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Changes to JS assets are not included here, but in [`atomic-data-browser`'s CHAN
- Add parent parameter to search endpoint which scopes a search to only the descendants of the given resource. #226
- Bookmark endpoint now also retrieves `og:image` and `og:description` #510
- Give server agent rights to edit all resources, fix issue with accepting invites in private drives #521
- Add cookie based authentication #512

## [v0.33.1] - 2022-09-25

Expand Down
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ actix-cors = "0.6"
actix-files = "0.6"
actix-multipart = "0.4"
actix-web-actors = "4"
base64 = "0.13"
chrono = "0.4"
colored = "2"
dialoguer = "0.10"
directories = ">= 2, < 5"
dotenv = "0.15"
futures = "0.3"
percent-encoding = "2.2.0"
promptly = "0.3"
regex = "1"
rio_api = "0.7"
Expand Down
166 changes: 83 additions & 83 deletions server/app_assets/dist/index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion server/app_assets/dist/index.js.map

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions server/e2e_tests/e2e-generated.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -330,9 +330,9 @@ test.describe('data-browser', async () => {
]);
await fileChooser.setFiles(demoFile);
await page.click(`[data-test]:has-text("${demoFileName}")`);
await expect(
await page.locator('[data-test="image-viewer"]'),
).toBeVisible();
const image = await page.locator('[data-test="image-viewer"]');
await expect(image).toBeVisible();
await expect(image).toHaveScreenshot({ maxDiffPixelRatio: 0.1 });
});

test('chatroom', async ({ page, browser }) => {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions server/e2e_tests/test-config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { TestConfig } from "./e2e-generated.spec";
const demoFileName = 'logo.svg';
const demoFileName = 'testimage.svg';

export const testConfig: TestConfig = {
demoFileName,
demoFile: `../../${demoFileName}`,
demoFile: `./${demoFileName}`,
demoInviteName: 'document demo',
serverUrl: 'http://localhost:9883',
frontEndUrl: 'http://localhost:9883',
Expand Down
27 changes: 27 additions & 0 deletions server/e2e_tests/testimage.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
59 changes: 57 additions & 2 deletions server/src/helpers.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
//! Functions useful in the server

use actix_web::http::header::HeaderMap;
use actix_web::cookie::Cookie;
use actix_web::http::header::{HeaderMap, HeaderValue};
use atomic_lib::authentication::AuthValues;
use percent_encoding::percent_decode_str;

use crate::errors::{AppErrorType, AtomicServerError};
use crate::{appstate::AppState, content_types::ContentType, errors::AtomicServerResult};

// Returns None if the string is empty.
Expand Down Expand Up @@ -56,6 +59,44 @@ pub fn get_auth_headers(
}
}

pub fn get_auth_from_cookie(
map: &HeaderMap,
requested_subject: &String,
) -> Option<AtomicServerResult<Option<AuthValues>>> {
let encoded_session = session_cookie_from_header(map.get("Cookie")?)?;

let session = base64::decode(encoded_session).ok()?;
let session_str = std::str::from_utf8(&session).ok()?;
let values: Result<AuthValues, AtomicServerError> =
serde_json::from_str(session_str).map_err(|_| AtomicServerError {
message: "Malformed authentication resource".to_string(),
error_type: AppErrorType::Unauthorized,
error_resource: None,
});

if let Ok(auth_values) = values {
if auth_values.requested_subject.eq(requested_subject) {
return Some(Err(AtomicServerError {
message: "Wrong requested subject".to_string(),
error_type: AppErrorType::Unauthorized,
error_resource: None,
}));
}

Some(Ok(Some(auth_values)))
} else {
Some(Err(values.err().unwrap()))
}
}

pub fn get_auth(
map: &HeaderMap,
requested_subject: String,
) -> AtomicServerResult<Option<AuthValues>> {
let cookie_result = get_auth_from_cookie(map, &requested_subject);
cookie_result.unwrap_or_else(|| get_auth_headers(map, requested_subject))
}

/// Checks for authentication headers and returns Some agent's subject if everything is well.
/// Skips these checks in public_mode and returns Ok(None).
#[tracing::instrument(skip(appstate))]
Expand All @@ -68,7 +109,7 @@ pub fn get_client_agent(
return Ok(None);
}
// Authentication check. If the user has no headers, continue with the Public Agent.
let auth_header_values = get_auth_headers(headers, requested_subject)?;
let auth_header_values = get_auth(headers, requested_subject)?;
let for_agent = atomic_lib::authentication::get_agent_from_auth_values_and_check(
auth_header_values,
&appstate.store,
Expand All @@ -94,3 +135,17 @@ pub fn try_extension(path: &str) -> Option<(ContentType, &str)> {
}
None
}

fn session_cookie_from_header(header: &HeaderValue) -> Option<String> {
let cookies: Vec<&str> = header.to_str().ok()?.split(';').collect();

for encoded_cookie in cookies {
let cookie = Cookie::parse(encoded_cookie).ok()?;
if cookie.name() == "atomic_session" {
let decoded = percent_decode_str(cookie.value()).decode_utf8().ok()?;
return Some(String::from(decoded));
}
}

None
}