Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set default security shield API #362

Open
wants to merge 10 commits into
base: main
Choose a base branch
from

Conversation

giannis-rdx
Copy link
Contributor

@giannis-rdx giannis-rdx commented Jan 29, 2025

This PR is part of the ABW-4146.
It adds an API to change the default security shield

Copy link

codecov bot commented Jan 29, 2025

Codecov Report

Attention: Patch coverage is 98.14815% with 1 line in your changes missing coverage. Please review.

Project coverage is 92.64%. Comparing base (3634378) to head (da8a962).
Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
...ogic/app_preferences/security_structures_update.rs 96.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #362      +/-   ##
==========================================
+ Coverage   92.59%   92.64%   +0.04%     
==========================================
  Files        1172     1180       +8     
  Lines       26255    26618     +363     
  Branches       81       77       -4     
==========================================
+ Hits        24312    24660     +348     
- Misses       1932     1947      +15     
  Partials       11       11              
Flag Coverage Δ
kotlin 97.63% <ø> (-0.10%) ⬇️
rust 92.30% <98.14%> (+0.10%) ⬆️
swift 92.86% <ø> (-0.18%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

});
}

Ok(p.app_preferences
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer creating a method for this inside Profile logic crate.

Better to delegate as much logic relating to profile changes as possible to Profile logic crate. And let SargonOS be a as dumb as possible "coordinator"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm you mean in the profile_update.rs, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done ?

&mut self,
shield_id: &SecurityStructureID,
) -> Result<bool> {
Ok(self
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why return Result?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If ID not found. Right.

So do you map to correct Error if not found?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as I wrote in #362 (review) there should be an update method on AppPreferences , which should actually should delegate to Security which should have this impl, but you need to change the impl to actually return an Error if not found - which you are not doing now. So Result<()> is correct, but now you are incorrectly returning Bool which you unconditionally wrap in a Result.

impl Security {
  /// # Throws
  /// Throws `CommonError:InvalidSecurityStructureID` if the structure identified by `shield_id` does not exist.
  fn update_security_structure_remove_flag_default(
		&mut self,
		shield_id: &SecurityStructureID,
	) -> Result<()> {
		self
			.security_structures_of_factor_source_ids
			.try_update_with(shield_id, |s| {
				s.metadata.remove_flag(SecurityStructureFlag::Default)
			}))
			.map(|_| CommonError::InvalidSecurityStructureID { bad_value: shield_id.to_string() })
	}
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and then ofc unit test all the way :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand why you want reusable functions and not having profile logic in SargonOS, but I don't understand at all where am I supposed to place the new functions. The way the profile-logic is structured doesn't really help me.

Therefore we need the following functions:

  • update_security_structure_remove_flag_default: where this is supposed to go? in (profile crate) models/app_preferences, I see there is a query fun there has_gateway_with_url. But it doesn't seem the right place to me. Should it be in the profile_update? Should I create a security_structures_logic file under profile/logic?
  • update_security_structure_set_flag_default: same as above
  • get_default_scurity_structure: a new file query_app_preferences under profile/logic? or in the models/app_preferences?
  • set_default_security_structure: it looks like it has to to profile_update or something else?

Copy link
Contributor

@Sajjon Sajjon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean you should do this - favour composition of many small resusable functions, instead of a bigger function which is not reausable.

The idea of SargonOS is to not have Profile model logic. So it should delegate that logic to profile-logic crate.

// In os-factors crate
async fn set_default_security_structure(
    &self,
    shield_id: SecurityStructureID,
) -> Result<()> {
    let updated_ids = self.update_profile_with(|p| Ok(p.set_default_security_structure(shield_id)))?;

    // Emit event
    self.event_bus
        .emit(EventNotification::profile_modified(
            EventProfileModified::SecurityStructuresUpdated {
                ids: updated_ids,
            },
        ))
        .await;

    Ok(())
}

// In Profile Logic crate
impl AppPreferences {
    fn get_default_scurity_structure(&self) -> Option<SecurityStructureOfFactorSourceIds> {
        self
            .security
            .security_structures_of_factor_source_ids
            .iter()
            .find(|s| s.metadata.is_default());
    }
}
impl Profile {
    fn get_default_Security_structure(&self) -> Option<SecurityStructureOfFactorSourceIds> {
        self
            .app_preferences.get_default_security_structure()
    }

    /// Returns the list of IDs of updated SecurityShields - either one or two elements,
    /// depending on if shield identified by `shield_id` already was "default" shield or not.
    fn set_default_security_structure(
        &self,
        shield_id: SecurityStructureID,
    ) -> Result<<Vec<SecurityStructureID>> {

        let current_default_shield = self.get_default_Security_structure();

        let updated_ids = match current_default_shield {
            Some(ref current_default_shield) => {
                vec![current_default_shield.metadata.id, shield_id]
            }
            None => vec![shield_id],
        };

        if let Some(current_default_shield) = &current_default_shield {
            self.update_security_structure_remove_flag_default(
                &current_default_shield.metadata.id,
            )
        }
        p.update_security_structure_add_flag_default(&shield_id)

        updated_ids
    }
}

Comment on lines 10 to 11
/// Used to mark a Security Shield as "default". We can only have one.
Default,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we call it Main as we do on FactorSourceFlag (despite in UI it will be treated as "default")?

If so, I would update the name of every related function as well

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it’s not exactly the same. “Main” indicates the primary or most important option, while “default” refers to something preselected. That’s why in UI, it is (correctly) defined as “default.”

Based on that, do you really think we should treat it as “main” in the code? 🤔

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think they are both (Device Factor Sources & Security Shields) kind of the same. Nothing prevents using a non-main factor source/security shield, but host apps will use the main one when either creating/securifying an entity.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah main in code makes sense to call it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have democracy, so if the majority agrees, I will change it 😄

)
}

pub fn set_flag(&mut self, flag: SecurityStructureFlag) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

insert_flag ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines 490 to 495
assert!(event_bus_driver.recorded().iter().any(|e| e.event
== Event::ProfileModified {
change: EventProfileModified::SecurityStructuresUpdated {
ids: vec![structure_ids_sample_other.metadata.id()]
}
}));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to check ProfileSaved was emitted as well

Also, we should check that SecurityStructuresUpdated is emitted with two ids when there was an existent main shield, and with one id when there isn't

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants