10000 [PM-12695] Add hidden field changes to password history by andrebispo5 · Pull Request #1012 · bitwarden/ios · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

[PM-12695] Add hidden field changes to password history #1012

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

Merged
merged 16 commits into from
Oct 24, 2024

Conversation

andrebispo5
Copy link
Contributor
@andrebispo5 andrebispo5 commented Oct 4, 2024

🎟️ Tracking

PM-12695

📔 Objective

When an hidden field is updated, the changes should reflect in the password history list.

📸 Screenshots

Screen.Recording.2024-10-04.at.21.04.09.mov

⏰ Reminders before review

  • Contributor guidelines followed
  • All formatters and local linters executed and passed
  • Written new unit and / or integration tests where applicable
  • Protected functional changes with optionality (feature flags)
  • Used internationalization (i18n) for all UI strings
  • CI builds passed
  • Communicated to DevOps any deployment requirements
  • Updated any necessary documentation (Confluence, contributing docs) or informed the documentation team

🦮 Reviewer guidelines

  • 👍 (:+1:) or similar for great changes
  • 📝 (:memo:) or ℹ️ (:information_source:) for notes or general info
  • ❓ (:question:) for questions
  • 🤔 (:thinking:) or 💭 (:thought_balloon:) for more open inquiry that's not quite a confirmed issue and could potentially benefit from discussion
  • 🎨 (:art:) for suggestions / improvements
  • ❌ (:x:) or ⚠️ (:warning:) for more significant problems or concerns needing attention
  • 🌱 (:seedling:) or ♻️ (:recycle:) for future improvements or indications of technical debt
  • ⛏ (:pick:) for minor or nitpick changes

Copy link
Contributor
github-actions bot commented Oct 4, 2024

Logo
Checkmarx One – Scan Summary & Details91c8d76a-d5f2-4ef0-9469-64af7740a68d

No New Or Fixed Issues Found

Copy link
codecov bot commented Oct 4, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 89.47%. Comparing base (21d2de0) to head (cf52631).
Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1012      +/-   ##
==========================================
+ Coverage   89.40%   89.47%   +0.07%     
==========================================
  Files         674      675       +1     
  Lines       42445    42494      +49     
==========================================
+ Hits        37948    38022      +74     
+ Misses       4497     4472      -25     

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

Comment on lines 231 to 241
private func mapFields(_ addEditState: AddEditItemState) -> [FieldView]? {
addEditState.customFieldsState.customFields.isEmpty ?
nil : addEditState.customFieldsState.customFields.map { customField in
FieldView(
name: customField.name,
value: customField.value,
type: .init(fieldType: customField.type),
linkedId: customField.linkedIdType?.rawValue
)
}
}
Copy link
Member

Choose a reason for hiding this comment

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

🎨 Given that you're modifying this, IMO this goes beyond the responsibilities of CipherView+Update. If we could be more specific on the places where we transform a state into a view it'd be better for maintainability and reusability.
So maybe we could add a FieldView+Extensions file with a init(state: CustomFieldState) which creates the FieldView based on that state so then you can use it here more directly. Additionally, I think it's easier to read with a guard isEmpty instead of the ternary operator.
Moreover, I think we don't need the whole AddEditItemState just the custom fields state array to perform the mapping and the name of the function should be more explicit.

    /// Maps the array of `CustomFieldState` into the an array of `FieldView`.
    private func mapToCustomFieldViews(from customFields: [CustomFieldState]) -> [FieldView]? {
        guard !customFields.isEmpty else {
            return nil
        }

        return customFields.map { FieldView(state: $0) }
    }

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup it makes sense, I'll create the extension for the mapping. Jumping between Android and iOS, I always forget about the Swift guards 🥲

Comment on lines 211 to 212
var newHistory = passwordHistory
if let oldFields {
Copy link
Member

Choose a reason for hiding this comment

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

🎨 You can use a guard here to simplify nesting:

guard let oldFields else {
    return passwordHistory
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Swift guards 😅

Comment on lines 221 to 225
if newHistory == nil {
newHistory = [passHistoryHiddenField]
} else if !passwordHistory!.contains(passHistoryHiddenField) {
newHistory!.append(passHistoryHiddenField)
}
Copy link
Member

Choose a reason for hiding this comment

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

⚠️ If passwordHistory is nil this will crash. We almost never use !, it's just better to use ?, if let or guard let to avoid unexpected behaviors/crashes even if we are 100% certain it won't be nil and if that's the case it should be reviewed whether the variable should be optional.
❓ I think it's impossible that the !passwordHistory!.contains(passHistoryHiddenField) is false, I mean passwordHistory won't ever contain the new passHistoryHiddenField because the combination of password + lastUsedDate won't ever be duplicated unless there's a bug with this part or somehow we get duplicated custom fields because of a bug. So I think you could remove the if contains part.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry this was code that was left behind from one of the iterations I made and the names of the variables tricked my eyes. Thanks for noticing it 👍

@andrebispo5 andrebispo5 requested a review from fedemkr October 9, 2024 14:12
// MARK: - FieldView+Update

extension BitwardenSdk.FieldView {
/// initializes a new LoginView with updated properties
Copy link
Member

Choose a reason for hiding this comment

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

⛏️

Suggested change
/// initializes a new LoginView with updated properties
/// initializes a new FieldView with updated properties

Comment on lines 215 to 236
var newHistory = passwordHistory
oldFields.filter { field in
FieldType(fieldType: field.type) == FieldType.hidden &&
!field.name.isEmptyOrNil &&
!field.value.isEmptyOrNil &&
!(newFields?.contains(field) ?? false)
}.forEach { hiddenField in
guard !hiddenField.name.isEmptyOrNil else {
return
}

let passHistoryHiddenField = PasswordHistoryView(
password: "\(hiddenField.name ?? ""): \(hiddenField.value ?? "")",
lastUsedDate: lastUsedDate
)
if newHistory == nil {
newHistory = [passHistoryHiddenField]
} else {
newHistory?.append(passHistoryHiddenField)
}
}
return newHistory
Copy link
Member

Choose a reason for hiding this comment

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

🎨 I was thinking about doing this change to improve the code and avoid having the extra logic to create the array if the newHistory == nil. What do you think about this? Do you feel like it's easier to read or your approach is? I feel like I have pros and cons on both approaches but leaning more towards this one but would love to hear your thoughts on this as well.

Suggested change
var newHistory = passwordHistory
oldFields.filter { field in
FieldType(fieldType: field.type) == FieldType.hidden &&
!field.name.isEmptyOrNil &&
!field.value.isEmptyOrNil &&
!(newFields?.contains(field) ?? false)
}.forEach { hiddenField in
guard !hiddenField.name.isEmptyOrNil else {
return
}
let passHistoryHiddenField = PasswordHistoryView(
password: "\(hiddenField.name ?? ""): \(hiddenField.value ?? "")",
lastUsedDate: lastUsedDate
)
if newHistory == nil {
newHistory = [passHistoryHiddenField]
} else {
newHistory?.append(passHistoryHiddenField)
}
}
return newHistory
let newPasswordHistoryFields = oldFields
.filter { field in
FieldType(fieldType: field.type) == FieldType.hidden &&
!field.name.isEmptyOrNil &&
!field.value.isEmptyOrNil &&
!(newFields?.contains(field) ?? false)
}.compactMap { hiddenField in
guard !hiddenField.name.isEmptyOrNil else {
return nil
}
return PasswordHistoryView(
password: "\(hiddenField.name ?? ""): \(hiddenField.value ?? "")",
lastUsedDate: lastUsedDate
)
}
guard !newPasswordHistoryFields.isEmpty else {
return passwordHistory
}
return passwordHistory?.append(contentsOf: newPasswordHistoryFields) ?? newPasswordHistoryFields

Comment on lines 214 to 228
let newPasswordHistoryFields: [PasswordHistoryView] = oldFields
.filter { field in
FieldType(fieldType: field.type) == FieldType.hidden &&
!field.name.isEmptyOrNil &&
!field.value.isEmptyOrNil &&
!(newFields?.contains(field) ?? false)
}.compactMap { hiddenField in
guard !hiddenField.name.isEmptyOrNil else {
return nil
}
return PasswordHistoryView(
password: "\(hiddenField.name ?? ""): \(hiddenField.value ?? "")",
lastUsedDate: lastUsedDate
)
}
Copy link
Member

Choose a reason for hiding this comment

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

🤔 Sorry I missed this before but given that in the filter you have && !field.name.isEmptyOrNil; is it necessary to check again and add the guard in compactMap?

guard !hiddenField.name.isEmptyOrNil else {
      return nil
}

🤔 And another point I missed: if you have a Field that now has changed its name then newFields.contains(field) will be false and will be included in the history, adding the PasswordHistoryView but using the old field name instead of the new one. This is not the same behavior as in MAUI where we only added the password history if the names of the old field and the new field would match. Maybe something for Product to make decision on.

https://github.com/bitwarden/mobile/blob/44849cb0a25019f240bc0064048a90f063000a71/src/Core/Services/CipherService.cs#L160-L169

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ran it with product and we will keep consistency with the other clients. So if the name changes it will log old name + old password.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Screen.Recording.2024-10-24.at.15.37.05.mov

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed the nil name check because it was not necessary

@andrebispo5 andrebispo5 enabled auto-merge (squash) October 24, 2024 14:59
@andrebispo5 andrebispo5 merged commit 6c0402f into main Oct 24, 2024
7 checks passed
@andrebispo5 andrebispo5 deleted the pm-12695/hidden-field-history branch October 24, 2024 15:00
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.

2 participants
0