Skip to content

Commit

Permalink
#182 strictrer authorization checks invites
Browse files Browse the repository at this point in the history
  • Loading branch information
joepio committed Dec 11, 2021
1 parent 5417b2b commit d889a1a
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 11 deletions.
29 changes: 27 additions & 2 deletions lib/src/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use std::collections::{HashMap, HashSet};
use urls::{SET, SIGNER};

use crate::{
datatype::DataType, datetime_helpers, errors::AtomicResult, resources::PropVals, urls, Atom,
AtomicError, Resource, Storelike, Value,
datatype::DataType, datetime_helpers, errors::AtomicResult, hierarchy, resources::PropVals,
urls, Atom, AtomicError, Resource, Storelike, Value,
};

/// Contains two resources. The first is the Resource representation of the applied Commits.
Expand Down Expand Up @@ -76,6 +76,7 @@ impl Commit {
) -> AtomicResult<CommitResponse> {
let subject_url = url::Url::parse(&self.subject)
.map_err(|e| format!("Subject '{}' is not a URL. {}", &self.subject, e))?;

if subject_url.query().is_some() {
return Err("Subject URL cannot have query parameters".into());
}
Expand Down Expand Up @@ -151,6 +152,30 @@ impl Commit {
if validate_schema {
resource_new.check_required_props(store)?;
}

// TODO: before_apply_commit hooks plugins
// This is where users should extend commits
for class in resource_new.get_classes(store)? {
match class.subject.as_str() {
urls::COMMIT => return Err("Commits can not be edited or created directly.".into()),
urls::INVITE => {
// Check if the creator has rights to invite people (= write) to the target resource
let target = resource_new
.get(urls::TARGET)
.map_err(|_e| "Invite does not have required Target attribute")?;
let target_resource = store.get_resource(&target.to_string())?;
if !hierarchy::check_write(store, &target_resource, &self.signer)? {
return Err(format!(
"Signer {} does not have the rights to create an Invite for {}. The target resource or its parent needs to provide the signer with Write rights.",
self.signer, target
)
.into());
}
}
_other => {}
};
}

// If a Destroy field is found, remove the resource and return early
// TODO: Should we remove the existing commits too? Probably.
if let Some(destroy) = self.destroy {
Expand Down
22 changes: 17 additions & 5 deletions lib/src/plugins/invite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,6 @@ pub fn construct_invite_redirect(
})?
.to_string();

println!("Invite resource before: {:?}", invite_resource);

if let Ok(usages_left) = invite_resource.get(urls::USAGES_LEFT) {
let num = usages_left.to_int()?;
if num == 0 {
Expand All @@ -73,17 +71,31 @@ pub fn construct_invite_redirect(
.map_err(|e| format!("Unable to save updated Invite. {}", e))?;
}

println!("Invite resource after: {:?}", invite_resource);
if let Ok(expires) = invite_resource.get(urls::EXPIRES_AT) {
if expires.to_int()? > crate::datetime_helpers::now() {
return Err("Invite is no longer valid".into());
}
}

// TODO: implement rights check
// check_if_invite_is_valid(invite_resource)?;
// Make sure the creator of the invite is still allowed to Write the target
let target_resource = store.get_resource(target)?;
let invite_creator =
crate::plugins::versioning::get_initial_commit_for_resource(target, store)?.signer;
if !crate::hierarchy::check_write(store, &target_resource, &invite_creator)? {
return Err(format!(
"Invite creator {} is not allowed to create invite for target {}",
invite_creator, target
)
.into());
}

add_rights(&agent, target, write, store)?;
if write {
// Also add read rights
add_rights(&agent, target, false, store)?;
}

// Construct the Redirect Resource, which might provide the Client with a Subject for his Agent.
let mut redirect = Resource::new_instance(urls::REDIRECT, store)?;
redirect.set_propval(
urls::DESTINATION.into(),
Expand Down
23 changes: 19 additions & 4 deletions lib/src/plugins/versioning.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,17 +84,34 @@ fn handle_all_versions_request(
collection.to_resource(store)
}

/// Searches the local store for all commits with this subject
/// Searches the local store for all commits with this subject, returns sorted from old to new.
fn get_commits_for_resource(subject: &str, store: &impl Storelike) -> AtomicResult<Vec<Commit>> {
let commit_atoms = store.tpf(None, Some(urls::SUBJECT), Some(subject), false)?;
let mut commit_resources = Vec::new();
for atom in commit_atoms {
// TODO: This will fail if a resource simply uses the SUBJECT url without being a valid Commit.
let commit = crate::Commit::from_resource(store.get_resource(&atom.subject)?)?;
commit_resources.push(commit)
}
// Sort all commits by date
commit_resources.sort_by(|a, b| a.created_at.cmp(&b.created_at));

Ok(commit_resources)
}

pub fn get_initial_commit_for_resource(
subject: &str,
store: &impl Storelike,
) -> AtomicResult<Commit> {
let commits = get_commits_for_resource(subject, store)?;
if commits.is_empty() {
return Err(AtomicError::not_found(
"No commits found for this resource".to_string(),
));
}
Ok(commits.first().unwrap().clone())
}

/// Constructs a Resource version for a specific Commit
/// Only works if the current store has the required Commits
pub fn construct_version(
Expand All @@ -114,9 +131,7 @@ pub fn construct_version(
));
}
}
let mut commits = get_commits_for_resource(subject, store)?;
// Sort all commits by date
commits.sort_by(|a, b| a.created_at.cmp(&b.created_at));
let commits = get_commits_for_resource(subject, store)?;
let mut version = Resource::new(subject.into());
for commit in commits {
if let Some(current_commit) = commit.url.clone() {
Expand Down

0 comments on commit d889a1a

Please sign in to comment.