Use vault bindings for remote opens
This commit is contained in:
@@ -207,271 +207,273 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ui struct {
|
type ui struct {
|
||||||
mode string
|
mode string
|
||||||
theme *material.Theme
|
theme *material.Theme
|
||||||
fileExplorer *explorer.Explorer
|
fileExplorer *explorer.Explorer
|
||||||
logoHorizontal paint.ImageOp
|
logoHorizontal paint.ImageOp
|
||||||
splashSquare paint.ImageOp
|
splashSquare paint.ImageOp
|
||||||
search widget.Editor
|
search widget.Editor
|
||||||
vaultPath widget.Editor
|
vaultPath widget.Editor
|
||||||
saveAsPath widget.Editor
|
saveAsPath widget.Editor
|
||||||
remoteBaseURL widget.Editor
|
remoteBaseURL widget.Editor
|
||||||
remotePath widget.Editor
|
remotePath widget.Editor
|
||||||
remoteUsername widget.Editor
|
remoteUsername widget.Editor
|
||||||
remotePassword widget.Editor
|
remotePassword widget.Editor
|
||||||
masterPassword widget.Editor
|
masterPassword widget.Editor
|
||||||
keyFilePath widget.Editor
|
keyFilePath widget.Editor
|
||||||
apiTokenName widget.Editor
|
apiTokenName widget.Editor
|
||||||
apiTokenClientName widget.Editor
|
apiTokenClientName widget.Editor
|
||||||
apiTokenExpiresAt widget.Editor
|
apiTokenExpiresAt widget.Editor
|
||||||
apiPolicyOperation widget.Editor
|
apiPolicyOperation widget.Editor
|
||||||
apiPolicyPath widget.Editor
|
apiPolicyPath widget.Editor
|
||||||
apiPolicyEntryID widget.Editor
|
apiPolicyEntryID widget.Editor
|
||||||
securityCipher widget.Editor
|
securityCipher widget.Editor
|
||||||
securityKDF widget.Editor
|
securityKDF widget.Editor
|
||||||
entryID widget.Editor
|
entryID widget.Editor
|
||||||
entryTitle widget.Editor
|
entryTitle widget.Editor
|
||||||
entryUsername widget.Editor
|
entryUsername widget.Editor
|
||||||
entryPassword widget.Editor
|
entryPassword widget.Editor
|
||||||
entryURL widget.Editor
|
entryURL widget.Editor
|
||||||
entryNotes widget.Editor
|
entryNotes widget.Editor
|
||||||
entryTags widget.Editor
|
entryTags widget.Editor
|
||||||
entryPath widget.Editor
|
entryPath widget.Editor
|
||||||
entryFields widget.Editor
|
entryFields widget.Editor
|
||||||
customFieldKeys []widget.Editor
|
customFieldKeys []widget.Editor
|
||||||
customFieldValues []widget.Editor
|
customFieldValues []widget.Editor
|
||||||
historyIndex widget.Editor
|
historyIndex widget.Editor
|
||||||
groupName widget.Editor
|
groupName widget.Editor
|
||||||
groupParentPath widget.Editor
|
groupParentPath widget.Editor
|
||||||
passwordProfile widget.Editor
|
passwordProfile widget.Editor
|
||||||
attachmentName widget.Editor
|
attachmentName widget.Editor
|
||||||
attachmentPath widget.Editor
|
attachmentPath widget.Editor
|
||||||
exportAttachmentPath widget.Editor
|
exportAttachmentPath widget.Editor
|
||||||
autofillBrowserAllowlist widget.Editor
|
autofillBrowserAllowlist widget.Editor
|
||||||
autofillAppAllowlist widget.Editor
|
autofillAppAllowlist widget.Editor
|
||||||
autofillPackageRules widget.Editor
|
autofillPackageRules widget.Editor
|
||||||
list widget.List
|
list widget.List
|
||||||
groupList widget.List
|
groupList widget.List
|
||||||
detailList widget.List
|
detailList widget.List
|
||||||
apiPolicyList widget.List
|
apiPolicyList widget.List
|
||||||
lifecycleList widget.List
|
lifecycleList widget.List
|
||||||
phonePanelList widget.List
|
phonePanelList widget.List
|
||||||
securityDialogList widget.List
|
securityDialogList widget.List
|
||||||
remotePrefsDialogList widget.List
|
remotePrefsDialogList widget.List
|
||||||
recentVaultListState widget.List
|
recentVaultListState widget.List
|
||||||
recentRemoteListState widget.List
|
recentRemoteListState widget.List
|
||||||
copyUser widget.Clickable
|
copyUser widget.Clickable
|
||||||
copyPass widget.Clickable
|
copyPass widget.Clickable
|
||||||
copyURL widget.Clickable
|
copyURL widget.Clickable
|
||||||
lockVault widget.Clickable
|
lockVault widget.Clickable
|
||||||
unlockVault widget.Clickable
|
unlockVault widget.Clickable
|
||||||
createVault widget.Clickable
|
createVault widget.Clickable
|
||||||
openVault widget.Clickable
|
openVault widget.Clickable
|
||||||
saveVault widget.Clickable
|
saveVault widget.Clickable
|
||||||
saveAsVault widget.Clickable
|
saveAsVault widget.Clickable
|
||||||
openRemote widget.Clickable
|
openRemote widget.Clickable
|
||||||
changeMasterKey widget.Clickable
|
changeMasterKey widget.Clickable
|
||||||
synchronizeVault widget.Clickable
|
synchronizeVault widget.Clickable
|
||||||
toggleSyncMenu widget.Clickable
|
toggleSyncMenu widget.Clickable
|
||||||
toggleMainMenu widget.Clickable
|
toggleMainMenu widget.Clickable
|
||||||
openAdvancedSync widget.Clickable
|
openAdvancedSync widget.Clickable
|
||||||
openSecuritySettings widget.Clickable
|
openSecuritySettings widget.Clickable
|
||||||
openRemotePrefsHelp widget.Clickable
|
openRemotePrefsHelp widget.Clickable
|
||||||
closeAdvancedSync widget.Clickable
|
closeAdvancedSync widget.Clickable
|
||||||
closeSecuritySettings widget.Clickable
|
closeSecuritySettings widget.Clickable
|
||||||
closeRemotePrefsHelp widget.Clickable
|
closeRemotePrefsHelp widget.Clickable
|
||||||
runAdvancedSync widget.Clickable
|
runAdvancedSync widget.Clickable
|
||||||
saveSecuritySettings widget.Clickable
|
saveSecuritySettings widget.Clickable
|
||||||
settingsDensityDense widget.Clickable
|
settingsDensityDense widget.Clickable
|
||||||
settingsDensityComfortable widget.Clickable
|
settingsDensityComfortable widget.Clickable
|
||||||
settingsContrastStandard widget.Clickable
|
settingsContrastStandard widget.Clickable
|
||||||
settingsContrastHigh widget.Clickable
|
settingsContrastHigh widget.Clickable
|
||||||
settingsReducedMotionOff widget.Clickable
|
settingsReducedMotionOff widget.Clickable
|
||||||
settingsReducedMotionOn widget.Clickable
|
settingsReducedMotionOn widget.Clickable
|
||||||
settingsKeyboardFocusStandard widget.Clickable
|
settingsKeyboardFocusStandard widget.Clickable
|
||||||
settingsKeyboardFocusProminent widget.Clickable
|
settingsKeyboardFocusProminent widget.Clickable
|
||||||
showSettingsSyncLocal widget.Clickable
|
showSettingsSyncLocal widget.Clickable
|
||||||
showSettingsSyncRemote widget.Clickable
|
showSettingsSyncRemote widget.Clickable
|
||||||
showSettingsSyncPull widget.Clickable
|
showSettingsSyncPull widget.Clickable
|
||||||
showSettingsSyncPush widget.Clickable
|
showSettingsSyncPush widget.Clickable
|
||||||
editEntry widget.Clickable
|
editEntry widget.Clickable
|
||||||
cancelEdit widget.Clickable
|
cancelEdit widget.Clickable
|
||||||
pickVaultPath widget.Clickable
|
pickVaultPath widget.Clickable
|
||||||
pickKeyFile widget.Clickable
|
pickKeyFile widget.Clickable
|
||||||
pickSyncLocalPath widget.Clickable
|
pickSyncLocalPath widget.Clickable
|
||||||
clearVaultSelection widget.Clickable
|
clearVaultSelection widget.Clickable
|
||||||
clearRemoteSelection widget.Clickable
|
clearRemoteSelection widget.Clickable
|
||||||
dismissBanner widget.Clickable
|
dismissBanner widget.Clickable
|
||||||
addEntry widget.Clickable
|
addEntry widget.Clickable
|
||||||
saveEntry widget.Clickable
|
saveEntry widget.Clickable
|
||||||
duplicateEntry widget.Clickable
|
duplicateEntry widget.Clickable
|
||||||
deleteEntry widget.Clickable
|
deleteEntry widget.Clickable
|
||||||
restoreEntry widget.Clickable
|
restoreEntry widget.Clickable
|
||||||
saveTemplate widget.Clickable
|
saveTemplate widget.Clickable
|
||||||
deleteTemplate widget.Clickable
|
deleteTemplate widget.Clickable
|
||||||
instantiateTemplate widget.Clickable
|
instantiateTemplate widget.Clickable
|
||||||
addAttachment widget.Clickable
|
addAttachment widget.Clickable
|
||||||
replaceAttachment widget.Clickable
|
replaceAttachment widget.Clickable
|
||||||
removeAttachment widget.Clickable
|
removeAttachment widget.Clickable
|
||||||
exportAttachment widget.Clickable
|
exportAttachment widget.Clickable
|
||||||
restoreHistory widget.Clickable
|
restoreHistory widget.Clickable
|
||||||
generatePassword widget.Clickable
|
generatePassword widget.Clickable
|
||||||
goToRootGroup widget.Clickable
|
goToRootGroup widget.Clickable
|
||||||
goToParentGroup widget.Clickable
|
goToParentGroup widget.Clickable
|
||||||
createGroup widget.Clickable
|
createGroup widget.Clickable
|
||||||
moveGroup widget.Clickable
|
moveGroup widget.Clickable
|
||||||
renameGroup widget.Clickable
|
renameGroup widget.Clickable
|
||||||
deleteGroup widget.Clickable
|
deleteGroup widget.Clickable
|
||||||
confirmDeleteGroup widget.Clickable
|
confirmDeleteGroup widget.Clickable
|
||||||
cancelDeleteGroup widget.Clickable
|
cancelDeleteGroup widget.Clickable
|
||||||
addCustomField widget.Clickable
|
addCustomField widget.Clickable
|
||||||
toggleGroupControls widget.Clickable
|
toggleGroupControls widget.Clickable
|
||||||
toggleLifecycleAdvanced widget.Clickable
|
toggleLifecycleAdvanced widget.Clickable
|
||||||
toggleHistory widget.Clickable
|
toggleHistory widget.Clickable
|
||||||
togglePasswordInline widget.Clickable
|
togglePasswordInline widget.Clickable
|
||||||
toggleSyncPassword widget.Clickable
|
toggleSyncPassword widget.Clickable
|
||||||
setStatusBannerShort widget.Clickable
|
setStatusBannerShort widget.Clickable
|
||||||
setStatusBannerStandard widget.Clickable
|
setStatusBannerStandard widget.Clickable
|
||||||
setStatusBannerLong widget.Clickable
|
setStatusBannerLong widget.Clickable
|
||||||
showAllAutofillNotices widget.Clickable
|
showAllAutofillNotices widget.Clickable
|
||||||
showApprovalAutofillOnly widget.Clickable
|
showApprovalAutofillOnly widget.Clickable
|
||||||
hideAutofillNotices widget.Clickable
|
hideAutofillNotices widget.Clickable
|
||||||
showEntries widget.Clickable
|
showEntries widget.Clickable
|
||||||
showTemplates widget.Clickable
|
showTemplates widget.Clickable
|
||||||
showRecycle widget.Clickable
|
showRecycle widget.Clickable
|
||||||
showAPITokens widget.Clickable
|
showAPITokens widget.Clickable
|
||||||
showAPIAudit widget.Clickable
|
showAPIAudit widget.Clickable
|
||||||
showAbout widget.Clickable
|
showAbout widget.Clickable
|
||||||
showLocalLifecycle widget.Clickable
|
showLocalLifecycle widget.Clickable
|
||||||
showRemoteLifecycle widget.Clickable
|
showRemoteLifecycle widget.Clickable
|
||||||
showSyncLocal widget.Clickable
|
showSyncLocal widget.Clickable
|
||||||
showSyncRemote widget.Clickable
|
showSyncRemote widget.Clickable
|
||||||
showSyncPull widget.Clickable
|
showSyncPull widget.Clickable
|
||||||
showSyncPush widget.Clickable
|
showSyncPush widget.Clickable
|
||||||
showAutofillApprovalAsk widget.Clickable
|
showAutofillApprovalAsk widget.Clickable
|
||||||
showAutofillApprovalAllow widget.Clickable
|
showAutofillApprovalAllow widget.Clickable
|
||||||
showAutofillApprovalBlock widget.Clickable
|
showAutofillApprovalBlock widget.Clickable
|
||||||
allowApproval widget.Clickable
|
allowApproval widget.Clickable
|
||||||
denyApproval widget.Clickable
|
denyApproval widget.Clickable
|
||||||
cancelApproval widget.Clickable
|
cancelApproval widget.Clickable
|
||||||
cancelLifecycleProgress widget.Clickable
|
cancelLifecycleProgress widget.Clickable
|
||||||
retryLifecycleOpen widget.Clickable
|
retryLifecycleOpen widget.Clickable
|
||||||
approvalPermanent widget.Bool
|
approvalPermanent widget.Bool
|
||||||
apiPolicyAllow widget.Bool
|
apiPolicyAllow widget.Bool
|
||||||
apiPolicyGroupScopeW widget.Bool
|
apiPolicyGroupScopeW widget.Bool
|
||||||
apiTokenDisabled widget.Bool
|
apiTokenDisabled widget.Bool
|
||||||
settingsGroupControls widget.Bool
|
settingsGroupControls widget.Bool
|
||||||
settingsLifecycleAdvanced widget.Bool
|
settingsLifecycleAdvanced widget.Bool
|
||||||
settingsHistory widget.Bool
|
settingsHistory widget.Bool
|
||||||
settingsDenseLayout widget.Bool
|
settingsDenseLayout widget.Bool
|
||||||
entryClicks []widget.Clickable
|
entryClicks []widget.Clickable
|
||||||
apiTokenClicks []widget.Clickable
|
apiTokenClicks []widget.Clickable
|
||||||
apiPolicyRemoves []widget.Clickable
|
apiPolicyRemoves []widget.Clickable
|
||||||
apiAuditClicks []widget.Clickable
|
apiAuditClicks []widget.Clickable
|
||||||
apiAuditTokenFilters []widget.Clickable
|
apiAuditTokenFilters []widget.Clickable
|
||||||
apiAuditDecisionFilters []widget.Clickable
|
apiAuditDecisionFilters []widget.Clickable
|
||||||
apiAuditOperationFilters []widget.Clickable
|
apiAuditOperationFilters []widget.Clickable
|
||||||
clearAPIAuditFilters widget.Clickable
|
clearAPIAuditFilters widget.Clickable
|
||||||
historyClicks []widget.Clickable
|
historyClicks []widget.Clickable
|
||||||
attachmentClicks []widget.Clickable
|
attachmentClicks []widget.Clickable
|
||||||
breadcrumbs []widget.Clickable
|
breadcrumbs []widget.Clickable
|
||||||
groupClicks []widget.Clickable
|
groupClicks []widget.Clickable
|
||||||
recentVaultClicks []widget.Clickable
|
recentVaultClicks []widget.Clickable
|
||||||
recentRemoteClicks []widget.Clickable
|
recentRemoteClicks []widget.Clickable
|
||||||
removeCustomFields []widget.Clickable
|
removeCustomFields []widget.Clickable
|
||||||
state appstate.State
|
state appstate.State
|
||||||
visible []entry
|
visible []entry
|
||||||
currentPath []string
|
currentPath []string
|
||||||
syncedPath []string
|
syncedPath []string
|
||||||
selectedHistoryIndex int
|
selectedHistoryIndex int
|
||||||
showPassword bool
|
showPassword bool
|
||||||
generatedPasswordDraft bool
|
generatedPasswordDraft bool
|
||||||
togglePassword widget.Clickable
|
togglePassword widget.Clickable
|
||||||
copyAPITokenSecret widget.Clickable
|
copyAPITokenSecret widget.Clickable
|
||||||
issueAPIToken widget.Clickable
|
issueAPIToken widget.Clickable
|
||||||
saveAPIToken widget.Clickable
|
saveAPIToken widget.Clickable
|
||||||
rotateAPIToken widget.Clickable
|
rotateAPIToken widget.Clickable
|
||||||
disableAPIToken widget.Clickable
|
disableAPIToken widget.Clickable
|
||||||
revokeAPIToken widget.Clickable
|
revokeAPIToken widget.Clickable
|
||||||
deleteAPIToken widget.Clickable
|
deleteAPIToken widget.Clickable
|
||||||
useCurrentGroupForPolicy widget.Clickable
|
useCurrentGroupForPolicy widget.Clickable
|
||||||
useSelectedEntryForPolicy widget.Clickable
|
useSelectedEntryForPolicy widget.Clickable
|
||||||
clearAPIPolicyTarget widget.Clickable
|
clearAPIPolicyTarget widget.Clickable
|
||||||
addAPIPolicyRule widget.Clickable
|
addAPIPolicyRule widget.Clickable
|
||||||
phoneSplit widget.Float
|
phoneSplit widget.Float
|
||||||
splitDrag gesture.Drag
|
splitDrag gesture.Drag
|
||||||
splitBase float32
|
splitBase float32
|
||||||
splitStartY float32
|
splitStartY float32
|
||||||
phoneSpan int
|
phoneSpan int
|
||||||
phoneGroupBrowserExpanded bool
|
phoneGroupBrowserExpanded bool
|
||||||
eyeIcon *widget.Icon
|
eyeIcon *widget.Icon
|
||||||
eyeOffIcon *widget.Icon
|
eyeOffIcon *widget.Icon
|
||||||
copyIcon *widget.Icon
|
copyIcon *widget.Icon
|
||||||
expandMoreIcon *widget.Icon
|
expandMoreIcon *widget.Icon
|
||||||
expandLessIcon *widget.Icon
|
expandLessIcon *widget.Icon
|
||||||
chevronRightIcon *widget.Icon
|
chevronRightIcon *widget.Icon
|
||||||
chevronDownIcon *widget.Icon
|
chevronDownIcon *widget.Icon
|
||||||
settingsIcon *widget.Icon
|
settingsIcon *widget.Icon
|
||||||
menuIcon *widget.Icon
|
menuIcon *widget.Icon
|
||||||
clipboardWriter clipboard.Writer
|
clipboardWriter clipboard.Writer
|
||||||
loadingMessage string
|
loadingMessage string
|
||||||
loadingActionLabel string
|
loadingActionLabel string
|
||||||
lifecycleMode string
|
lifecycleMode string
|
||||||
syncSourceMode syncSourceMode
|
syncSourceMode syncSourceMode
|
||||||
syncDirection syncDirection
|
syncDirection syncDirection
|
||||||
syncLocalImportName string
|
syncLocalImportName string
|
||||||
syncLocalImportContent []byte
|
syncLocalImportContent []byte
|
||||||
syncLocalPath widget.Editor
|
syncLocalPath widget.Editor
|
||||||
syncRemoteBaseURL widget.Editor
|
syncRemoteBaseURL widget.Editor
|
||||||
syncRemotePath widget.Editor
|
syncRemotePath widget.Editor
|
||||||
syncRemoteUsername widget.Editor
|
syncRemoteUsername widget.Editor
|
||||||
syncRemotePassword widget.Editor
|
syncRemotePassword widget.Editor
|
||||||
syncDialogOpen bool
|
syncDialogOpen bool
|
||||||
syncMenuOpen bool
|
syncMenuOpen bool
|
||||||
mainMenuOpen bool
|
mainMenuOpen bool
|
||||||
selectedRemoteConnection bool
|
selectedRemoteConnection bool
|
||||||
securityDialogOpen bool
|
selectedVaultRemoteProfileID string
|
||||||
remotePrefsDialogOpen bool
|
selectedVaultRemoteCredentialEntryID string
|
||||||
showSyncPassword bool
|
securityDialogOpen bool
|
||||||
keyboardFocus focusID
|
remotePrefsDialogOpen bool
|
||||||
defaultSaveAsPath string
|
showSyncPassword bool
|
||||||
recentVaultsPath string
|
keyboardFocus focusID
|
||||||
settingsPath string
|
defaultSaveAsPath string
|
||||||
uiPreferencesPath string
|
recentVaultsPath string
|
||||||
recentRemotesPath string
|
settingsPath string
|
||||||
autofillCachePath string
|
uiPreferencesPath string
|
||||||
editingEntry bool
|
recentRemotesPath string
|
||||||
syncDefaultSourceMode syncSourceMode
|
autofillCachePath string
|
||||||
syncDefaultDirection syncDirection
|
editingEntry bool
|
||||||
groupControlsHidden bool
|
syncDefaultSourceMode syncSourceMode
|
||||||
lifecycleAdvancedHidden bool
|
syncDefaultDirection syncDirection
|
||||||
historyHidden bool
|
groupControlsHidden bool
|
||||||
denseLayout bool
|
lifecycleAdvancedHidden bool
|
||||||
statusBannerTTL time.Duration
|
historyHidden bool
|
||||||
autofillNoticePreference autofillNoticeMode
|
denseLayout bool
|
||||||
autofillFirstFillApprovalMode autofillFirstFillApprovalMode
|
statusBannerTTL time.Duration
|
||||||
accessibilityPrefs accessibilityPreferences
|
autofillNoticePreference autofillNoticeMode
|
||||||
settingsDraft settingsDraft
|
autofillFirstFillApprovalMode autofillFirstFillApprovalMode
|
||||||
recentVaults []string
|
accessibilityPrefs accessibilityPreferences
|
||||||
recentRemotes []recentRemoteRecord
|
settingsDraft settingsDraft
|
||||||
recentVaultGroups map[string][]string
|
recentVaults []string
|
||||||
recentVaultUsedAt map[string]time.Time
|
recentRemotes []recentRemoteRecord
|
||||||
entriesState entriesSectionState
|
recentVaultGroups map[string][]string
|
||||||
deleteGroupPath []string
|
recentVaultUsedAt map[string]time.Time
|
||||||
apiPolicyGroupScope bool
|
entriesState entriesSectionState
|
||||||
apiTokenSecret string
|
deleteGroupPath []string
|
||||||
selectedAuditIndex int
|
apiPolicyGroupScope bool
|
||||||
statusExpiresAt time.Time
|
apiTokenSecret string
|
||||||
now func() time.Time
|
selectedAuditIndex int
|
||||||
apiHost *api.Host
|
statusExpiresAt time.Time
|
||||||
auditLog *apiaudit.Log
|
now func() time.Time
|
||||||
grpcAddress string
|
apiHost *api.Host
|
||||||
backgroundResults chan backgroundActionResult
|
auditLog *apiaudit.Log
|
||||||
backgroundActionSerial int
|
grpcAddress string
|
||||||
activeBackgroundAction int
|
backgroundResults chan backgroundActionResult
|
||||||
lastLifecycleAction string
|
backgroundActionSerial int
|
||||||
requestMasterPassFocus bool
|
activeBackgroundAction int
|
||||||
invalidate func()
|
lastLifecycleAction string
|
||||||
|
requestMasterPassFocus bool
|
||||||
|
invalidate func()
|
||||||
}
|
}
|
||||||
|
|
||||||
type backgroundActionResult struct {
|
type backgroundActionResult struct {
|
||||||
@@ -1069,6 +1071,22 @@ func (u *ui) openRemoteAction() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if binding, resolved, ok, err := u.resolvedSelectedVaultRemoteBinding(); err != nil {
|
||||||
|
return err
|
||||||
|
} else if ok {
|
||||||
|
if err := u.state.OpenBoundRemoteVault(binding, key); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
u.remoteBaseURL.SetText(resolved.Profile.BaseURL)
|
||||||
|
u.remotePath.SetText(resolved.Profile.Path)
|
||||||
|
u.noteRecentRemote(resolved.Profile.BaseURL, resolved.Profile.Path)
|
||||||
|
u.resetPasswordPeek()
|
||||||
|
u.restoreRecentRemoteGroup(resolved.Profile.BaseURL, resolved.Profile.Path)
|
||||||
|
u.loadSecuritySettingsFromSession()
|
||||||
|
u.editingEntry = false
|
||||||
|
u.filter()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
client := webdav.Client{
|
client := webdav.Client{
|
||||||
BaseURL: strings.TrimSpace(u.remoteBaseURL.Text()),
|
BaseURL: strings.TrimSpace(u.remoteBaseURL.Text()),
|
||||||
Username: strings.TrimSpace(u.remoteUsername.Text()),
|
Username: strings.TrimSpace(u.remoteUsername.Text()),
|
||||||
@@ -1108,6 +1126,20 @@ func (u *ui) startOpenRemoteAction() {
|
|||||||
Password: u.remotePassword.Text(),
|
Password: u.remotePassword.Text(),
|
||||||
}
|
}
|
||||||
remotePath := strings.TrimSpace(u.remotePath.Text())
|
remotePath := strings.TrimSpace(u.remotePath.Text())
|
||||||
|
if _, resolved, ok, err := u.resolvedSelectedVaultRemoteBinding(); err != nil {
|
||||||
|
u.state.ErrorMessage = u.describeActionError("open remote vault", err)
|
||||||
|
u.requestMasterPassFocus = true
|
||||||
|
return
|
||||||
|
} else if ok {
|
||||||
|
client = webdav.Client{
|
||||||
|
BaseURL: resolved.Profile.BaseURL,
|
||||||
|
Username: resolved.Credentials.Username,
|
||||||
|
Password: resolved.Credentials.Password,
|
||||||
|
}
|
||||||
|
remotePath = resolved.Profile.Path
|
||||||
|
u.remoteBaseURL.SetText(resolved.Profile.BaseURL)
|
||||||
|
u.remotePath.SetText(resolved.Profile.Path)
|
||||||
|
}
|
||||||
u.lastLifecycleAction = "open remote vault"
|
u.lastLifecycleAction = "open remote vault"
|
||||||
u.runBackgroundAction("open remote vault", func() (func() error, error) {
|
u.runBackgroundAction("open remote vault", func() (func() error, error) {
|
||||||
prepared, err := session.PrepareRemoteOpen(client, remotePath, key)
|
prepared, err := session.PrepareRemoteOpen(client, remotePath, key)
|
||||||
@@ -1904,6 +1936,33 @@ func (u *ui) availableRemoteCredentialEntries() []vault.Entry {
|
|||||||
return entries
|
return entries
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *ui) selectedVaultRemoteBinding() (appstate.RemoteBinding, bool) {
|
||||||
|
if strings.TrimSpace(u.selectedVaultRemoteProfileID) == "" || strings.TrimSpace(u.selectedVaultRemoteCredentialEntryID) == "" {
|
||||||
|
return appstate.RemoteBinding{}, false
|
||||||
|
}
|
||||||
|
return appstate.RemoteBinding{
|
||||||
|
LocalVaultPath: strings.TrimSpace(u.vaultPath.Text()),
|
||||||
|
RemoteProfileID: strings.TrimSpace(u.selectedVaultRemoteProfileID),
|
||||||
|
CredentialEntryID: strings.TrimSpace(u.selectedVaultRemoteCredentialEntryID),
|
||||||
|
}, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ui) resolvedSelectedVaultRemoteBinding() (appstate.RemoteBinding, appstate.ResolvedRemoteBinding, bool, error) {
|
||||||
|
binding, ok := u.selectedVaultRemoteBinding()
|
||||||
|
if !ok {
|
||||||
|
return appstate.RemoteBinding{}, appstate.ResolvedRemoteBinding{}, false, nil
|
||||||
|
}
|
||||||
|
model, err := u.state.Session.Current()
|
||||||
|
if err != nil {
|
||||||
|
return appstate.RemoteBinding{}, appstate.ResolvedRemoteBinding{}, false, err
|
||||||
|
}
|
||||||
|
resolved, err := binding.Resolve(model)
|
||||||
|
if err != nil {
|
||||||
|
return appstate.RemoteBinding{}, appstate.ResolvedRemoteBinding{}, false, err
|
||||||
|
}
|
||||||
|
return binding, resolved, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (u *ui) noteCurrentRemotePath() {
|
func (u *ui) noteCurrentRemotePath() {
|
||||||
status, ok := u.state.Session.(sessionStatus)
|
status, ok := u.state.Session.(sessionStatus)
|
||||||
if !ok || !status.IsRemote() || status.IsLocked() {
|
if !ok || !status.IsRemote() || status.IsLocked() {
|
||||||
|
|||||||
+140
@@ -83,6 +83,22 @@ func (s summarySession) HasVault() bool
|
|||||||
func (s summarySession) IsLocked() bool { return s.locked }
|
func (s summarySession) IsLocked() bool { return s.locked }
|
||||||
func (s summarySession) IsRemote() bool { return s.remote }
|
func (s summarySession) IsRemote() bool { return s.remote }
|
||||||
|
|
||||||
|
type remoteOpenCaptureSession struct {
|
||||||
|
model vault.Model
|
||||||
|
remoteClient webdav.Client
|
||||||
|
remotePath string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *remoteOpenCaptureSession) Current() (vault.Model, error) {
|
||||||
|
return s.model, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *remoteOpenCaptureSession) OpenRemote(client webdav.Client, path string, _ vault.MasterKey) error {
|
||||||
|
s.remoteClient = client
|
||||||
|
s.remotePath = path
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func TestUIFiltersUsingVaultModelPathsAndSearch(t *testing.T) {
|
func TestUIFiltersUsingVaultModelPathsAndSearch(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -1658,6 +1674,130 @@ func TestUIOpenRemoteReportsTransportFailure(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUIOpenRemoteActionUsesSelectedVaultBinding(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
sess := &remoteOpenCaptureSession{
|
||||||
|
model: vault.Model{
|
||||||
|
Entries: []vault.Entry{{
|
||||||
|
ID: "remote-creds-1",
|
||||||
|
Title: "WebDAV Sign-In",
|
||||||
|
Username: "tjulian",
|
||||||
|
Password: "token-1",
|
||||||
|
Path: []string{"Crew", "Internet"},
|
||||||
|
}},
|
||||||
|
RemoteProfiles: []vault.RemoteProfile{{
|
||||||
|
ID: "family-webdav",
|
||||||
|
Name: "Family Vault",
|
||||||
|
Backend: vault.RemoteBackendWebDAV,
|
||||||
|
BaseURL: "https://dav.example.invalid/remote.php/dav",
|
||||||
|
Path: "files/family/keepass.kdbx",
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
u := newUIWithSession("desktop", sess)
|
||||||
|
u.masterPassword.SetText("correct horse battery staple")
|
||||||
|
u.selectedVaultRemoteProfileID = "family-webdav"
|
||||||
|
u.selectedVaultRemoteCredentialEntryID = "remote-creds-1"
|
||||||
|
|
||||||
|
if err := u.openRemoteAction(); err != nil {
|
||||||
|
t.Fatalf("openRemoteAction() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if got := sess.remoteClient.BaseURL; got != "https://dav.example.invalid/remote.php/dav" {
|
||||||
|
t.Fatalf("remoteClient.BaseURL = %q, want remote.php/dav URL", got)
|
||||||
|
}
|
||||||
|
if got := sess.remoteClient.Username; got != "tjulian" {
|
||||||
|
t.Fatalf("remoteClient.Username = %q, want tjulian", got)
|
||||||
|
}
|
||||||
|
if got := sess.remoteClient.Password; got != "token-1" {
|
||||||
|
t.Fatalf("remoteClient.Password = %q, want token-1", got)
|
||||||
|
}
|
||||||
|
if got := sess.remotePath; got != "files/family/keepass.kdbx" {
|
||||||
|
t.Fatalf("remotePath = %q, want files/family/keepass.kdbx", got)
|
||||||
|
}
|
||||||
|
if got := u.remoteBaseURL.Text(); got != "https://dav.example.invalid/remote.php/dav" {
|
||||||
|
t.Fatalf("remoteBaseURL = %q, want resolved profile base URL", got)
|
||||||
|
}
|
||||||
|
if got := u.remotePath.Text(); got != "files/family/keepass.kdbx" {
|
||||||
|
t.Fatalf("remotePath editor = %q, want resolved profile path", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUIStartOpenRemoteActionUsesSelectedVaultBinding(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
localKey := vault.MasterKey{Password: "correct horse battery staple"}
|
||||||
|
localModel := vault.Model{
|
||||||
|
Entries: []vault.Entry{{
|
||||||
|
ID: "remote-creds-1",
|
||||||
|
Title: "WebDAV Sign-In",
|
||||||
|
Username: "tjulian",
|
||||||
|
Password: "token-1",
|
||||||
|
Path: []string{"Crew", "Internet"},
|
||||||
|
}},
|
||||||
|
RemoteProfiles: []vault.RemoteProfile{{
|
||||||
|
ID: "family-webdav",
|
||||||
|
Name: "Family Vault",
|
||||||
|
Backend: vault.RemoteBackendWebDAV,
|
||||||
|
BaseURL: "",
|
||||||
|
Path: "files/family/keepass.kdbx",
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteModel := vault.Model{
|
||||||
|
Entries: []vault.Entry{{
|
||||||
|
ID: "vault-console",
|
||||||
|
Title: "Vault Console",
|
||||||
|
Username: "dannyocean",
|
||||||
|
Password: "token-1",
|
||||||
|
URL: "https://vault.crew.example.invalid",
|
||||||
|
Path: []string{"Root", "Internet"},
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != http.MethodGet {
|
||||||
|
t.Fatalf("unexpected method %s", r.Method)
|
||||||
|
}
|
||||||
|
var encoded bytes.Buffer
|
||||||
|
if err := vault.SaveKDBXWithKey(&encoded, remoteModel, localKey); err != nil {
|
||||||
|
t.Fatalf("SaveKDBXWithKey() error = %v", err)
|
||||||
|
}
|
||||||
|
w.Header().Set("ETag", "\"v1\"")
|
||||||
|
_, _ = w.Write(encoded.Bytes())
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
localModel.RemoteProfiles[0].BaseURL = server.URL
|
||||||
|
|
||||||
|
manager := &session.Manager{}
|
||||||
|
if err := manager.Create(localModel, localKey); err != nil {
|
||||||
|
t.Fatalf("manager.Create() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
u := newUIWithSession("desktop", manager)
|
||||||
|
u.masterPassword.SetText(localKey.Password)
|
||||||
|
u.selectedVaultRemoteProfileID = "family-webdav"
|
||||||
|
u.selectedVaultRemoteCredentialEntryID = "remote-creds-1"
|
||||||
|
|
||||||
|
u.startOpenRemoteAction()
|
||||||
|
|
||||||
|
result := waitForBackgroundResult(t, u)
|
||||||
|
u.applyBackgroundResult(result)
|
||||||
|
|
||||||
|
if got := u.state.ErrorMessage; got != "" {
|
||||||
|
t.Fatalf("ErrorMessage after apply = %q, want empty", got)
|
||||||
|
}
|
||||||
|
if got := u.remoteBaseURL.Text(); got != server.URL {
|
||||||
|
t.Fatalf("remoteBaseURL = %q, want server URL from selected profile", got)
|
||||||
|
}
|
||||||
|
if got := u.remotePath.Text(); got != "files/family/keepass.kdbx" {
|
||||||
|
t.Fatalf("remotePath = %q, want selected profile path", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestUIRemoteSaveConflictShowsVisibleErrorAndKeepsDirtyState(t *testing.T) {
|
func TestUIRemoteSaveConflictShowsVisibleErrorAndKeepsDirtyState(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user