Skip to content

Commit

Permalink
Merge pull request #160 from zcutlip/dev/143-support-editing-items
Browse files Browse the repository at this point in the history
issue #143 support editing items
  • Loading branch information
zcutlip authored Oct 31, 2023
2 parents af11ec9 + 73809be commit 9c6a9a8
Show file tree
Hide file tree
Showing 268 changed files with 11,163 additions and 180 deletions.
11 changes: 10 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ celerybeat-schedule
*.sage.py

# Environments
dot_env_files/.env*
.env
.env_secret*
.venv
Expand Down Expand Up @@ -124,7 +125,9 @@ dmypy.json
# Pyre type checker
.pyre/

.vscode
.vscode/*
!/.vscode/settings.json
!/.vscode/launch.json

.idea/
# General
Expand Down Expand Up @@ -166,3 +169,9 @@ Temporary Items

# Makefile stamps
.*stamp

# General
.vagrant/

# Log files (if you are creating logs in debug mode, uncomment this)
# *.log
4 changes: 4 additions & 0 deletions .init/00_pyonepassword
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,9 @@ then
compdef _local_branches deletebranch_pyop pytest_pyop mypy_pyop
fi

if [ -f "$_pyonepasswd_src_root/dot_env_files/.env_pyonepassword_test_rw" ];
then
alias op_sa_rw="\$_pyonepasswd_src_root/scripts/op_env \$_pyonepasswd_src_root/dot_env_files/.env_pyonepassword_test_rw"
fi

unset _realhome
149 changes: 149 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python: Current File",
"type": "python",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"justMyCode": true
},
{
"name": "sanitize.py",
"type": "python",
"request": "launch",
"program": "scripts/sanitize.py",
"console": "integratedTerminal",
"justMyCode": true,
"args": [
"./sanitize.cfg"
]
},
{
"name": "versions.py",
"type": "python",
"request": "launch",
"program": "examples/versions.py",
"console": "integratedTerminal",
"justMyCode": false
},
{
"name": "debug op forget",
"type": "python",
"request": "launch",
"program": "ipython_snippets/debug.py",
"console": "integratedTerminal"
},
{
"name": "load from json",
"type": "python",
"request": "launch",
"program": "ipython_snippets/load_json_item.py",
"console": "integratedTerminal",
"justMyCode": false
},
{
"name": "new server",
"type": "python",
"request": "launch",
"module": "examples.server"
},
{
"name": "create_item",
"type": "python",
"request": "launch",
"program": "examples/create_item.py",
"console": "integratedTerminal"
},
{
"name": "server ssh keys",
"type": "python",
"request": "launch",
"program": "examples/server-ssh-keys.py",
"console": "integratedTerminal",
"args": [
"Mustafar",
"id_ed25519",
"--vault",
"Machine Credentials",
"--outdir",
"./mustafar-keys"
]
},
{
"name": "mock op",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/mop.py",
"console": "integratedTerminal",
"args": [
"get",
"item",
"Example Login 1",
"--vault",
"Test Data"
]
},
{
"name": "Python: example signin, get item",
"type": "python",
"request": "launch",
"program": "examples/example-signin-get-item.py",
"console": "integratedTerminal"
},
{
"name": "Python: example signin, get document",
"type": "python",
"request": "launch",
"program": "examples/example-signin-get-document.py",
"console": "integratedTerminal"
},
{
"name": "Python: example signin, signout",
"type": "python",
"request": "launch",
"program": "examples/example-signin-signout.py",
"console": "integratedTerminal"
},
{
"name": "example-signin-get-vault",
"type": "python",
"request": "launch",
"program": "examples/example-signin-get-vault.py",
"console": "integratedTerminal"
},
{
"name": "example list items",
"type": "python",
"request": "launch",
"program": "examples/list-items.py",
"console": "integratedTerminal"
},
{
"name": "create items",
"type": "python",
"request": "launch",
"program": "examples/create-item.py",
"console": "integratedTerminal"
},
{
"name": "item edit field type",
"type": "python",
"request": "launch",
"program": "examples/debug_item_edit_field_type.py",
"console": "integratedTerminal"
},
{
"name": "example-service-account",
"type": "python",
"request": "launch",
"program": "examples/example-service-account.py",
"envFile": "${workspaceFolder}/debug_env/svc_acct_env",
"console": "integratedTerminal"
},
]
}
16 changes: 16 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"python.testing.pytestEnabled": true,
"python.testing.unittestEnabled": false,
"files.trimTrailingWhitespace": true,
"files.trimFinalNewlines": true,
"files.associations": {
"00_pyonepassword": "shellscript"
},
"files.insertFinalNewline": true,
"isort.args": [
"-m",
"3"
],
"search.useIgnoreFiles": true,

}
34 changes: 30 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,41 @@

All notable changes to this project will be documented in this file.

## [DEVELOPMENT]
## [4.0.0b2] 2023-10-20

### Added
- Additional item editing API:
- `OP.item_edit_add_password_field()`
- `OP.item_edit_delete_field()`

## [4.0.0b1] 2023-10-12

- Minor housekeeping updates
- Finish removing Python 3.8 support


## [4.0.0b0] 2023-10-11

### Added
- Item editing:
- `OP.item_edit_generate_password()`
- `OP.item_edit_set_password()`
- `OP.item_edit_set_title()`
- `OP.item_edit_set_favorite()`
- `OP.item_edit_set_tags()`
- `OP.item_edit_set_url()`
- `OP.item_edit_set_text_field()`
- `OP.item_edit_add_text_field()`
- `OP.item_edit_set_url_field()`

- `OPAbstractItem.field_value_by_section_label()` as a replacement for poorly named `field_value_by_section_title()`
- Support for `op` new `whoami` behvior version 2.20.0
- new `whoami` dict
- On `OP()` initialization, accomodate `whoami` failure when the token hasn't been used recently

### Changed

- Removed Python 3.8 support
- Added Python 3.12 support
- Ensure all methods for section lookup by label raise `OPSectionNotFound` if no section is found matching the given label
- Ensure all methods for field lookup by label rais `OPFieldNotFound` if no field is found matching the given label

Expand All @@ -18,8 +45,7 @@ All notable changes to this project will be documented in this file.
- `OPAbstractItem.field_value_by_section_title()`
- call `OPAbstractItem.field_value_by_section_label()` instead
### Misc

- Updated testing configuration in conjucton with refactored `mock-op`
- Updated testing configuration in conjuncton with refactored `mock-op`
- Add `FUNDING.yml`


Expand Down
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ COV_STAMP=.cov_stamp
TOX_STAMP=.tox_stamp
HTML_REPORT_STAMP=".html_report_stamp"

PYONEPASSWORD_SRC_FILES=$(shell find ./pyonepassword -name \*\.py -print)
# find all *.py and *.json files under pyonepassword
PYONEPASSWORD_SRC_FILES=$(shell find ./pyonepassword -name \*\.py -print -o -name \*\.json -print)
# find all regular files under tests excluding *.pyc, and reports/
# this should include *.py, *.json, *.txt plus files without extensions
PYONEPASSWORD_TEST_FILES=$(shell find tests -name \*\.pyc -prune -o -name reports -prune -o -type f -print)

all:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ A Python API to sign into and query a 1Password account using the `op` command.

## Requirements

- Python >= 3.8
- Python >= 3.9
- 1Password command-line tool >= 2.0.0
- see [1Password Developer Documentation](https://developer.1password.com/docs/cli)
- Internet connectivity to 1Password.com
Expand Down
4 changes: 2 additions & 2 deletions docker_testing/run-individual.sh
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@ fi

if [ -z "$container" ];
then
quit "Speciy py39, py310, py311, or py312" 1
quit "Specify py39, py310, py311, or py312" 1
fi


ret=0
set -x

docker run --rm -it -v "$(pwd):/usr/src/testdir" "$container" /test.sh "$@"
ret="$(($?+ret))";

Expand Down
File renamed without changes.
File renamed without changes.
79 changes: 79 additions & 0 deletions docs/item-editing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# PYONEPASSWORD ITEM EDITING

As of version 4.0.0, `pyonepassword` supports in-place item editing. There is API to match the operations supported by the `op item edit` command. This file describes the API as well as its restrictions and limitations. It is recommended to consult `op item edit --help` for any restrictions in addition those enforced by `pyonepassword` described here.

All of the methods described below are instance methods on the `pyonepassword.OP` class.

The supported operations break down into two groups: operations that work with arbitrary item fields (and optionally sections), and ones that do not. The first group described are the non-field editing operations, followed by the field editing operations.


## Non-field Editing Operations

The following item editing methods do not operate on arbitrary sections and fields:

- `OP.item_edit_set_favorite()`
- `OP.item_edit_generate_password()`
- `OP.item_edit_set_title()`
- `OP.item_edit_set_tags()`
- `OP.item_edit_set_url()`

> **Note**: Technically `OP.item_edit_generate_password()` operates on a field, but it only works on the built-in "password" field of Login and Password items.

## Field Editing Operations

There are three categories of field-editing operations:

- Setting a value on an existing field
- Including changing a field's type. E.g., from "text" to "URL"
- Adding a new field, and optionally a section
- Deleting a field

In all cases, an `item get` operation is first peformed in order to validate the presense or absence of the field being edited. There are different sets of restrictions depending on whether a field is being added, an existing field being assigned a new value, or a field is being deleted. Those restrictions are dicussed below.


### Existing Field Value Setting

If an existing field is being assigned a new value, the original item is retrieved and checked that the requested field/section pairing exist. Any of the following conditions are considered an error:
- A field with the specified label is not found
- If provided, a section with the specified label is not found
- No section is specified, and no matching fields are found that *have no* associated section

If the existing field is a password field, and setting a new value would change it to some other field type, the `password_downgrade=True` kwarg must be passed.

*SECURITY NOTE*: The `OP.item_edit_set_password()` method will include the provided password in cleartext as a command line argument to the `op` command. On most platforms, the arguments, including the password, will be visible to other processes, including processes owned by other users. In order to use this operaton, this insecurity must be acknowledged by passing the `insecure_operation=True` kwarg

The following methods operate on arbitrary fields and sections, provided they already exist:

- `OP.item_edit_set_password()`
- `OP.item_edit_set_text_field()`
- `OP.item_edit_set_url_field()`


### Adding New Fields

If a new field is being added, the original item is retrieved and checked that the requested field/section pairing *do not* exist. Any of the following conditions are considered an error:
- Ambiguous match:
- one or more fields match the field label and no section label was specified
- Explicit match:
- one or more field/section pairings exist that match the field label & section label

The following methods will add arbitrary fields and optionally sections, provided they do not already exist:

- `OP.item_edit_add_text_field()`
- `OP.item_edit_add_password_field()`
- `OP.item_edit_add_url_field()`


### Deleting Fields

When deleting a field, if the field is associated with a section, and the section has no remaining fields, the section will also be deleted.

Deleting fields has the same restrictions as setting values on existing fields. The original item is retrieved and checked that the requested (field, seciton) pairing exist. Any of the following conditions are considered an error:
- A field with the specified label is not found
- If provided, a section with the specified label is not found
- No section is specified, and no matching fields are found that *have no* associated section

The following method will delete arbitary fields and optionally sections, provided they already exist:

- `OP.item_edit_delete_field()`
File renamed without changes.
Loading

0 comments on commit 9c6a9a8

Please sign in to comment.