Arthur de Jong

Open Source / Free Software developer

summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArthur de Jong <arthur@arthurdejong.org>2018-04-05 18:24:48 +0200
committerArthur de Jong <arthur@arthurdejong.org>2018-04-21 11:23:29 +0200
commite91e498be862732d89392fa59c85045a92a584b0 (patch)
tree3db243e9348caf9674924645645274433ffcd867
parentc652eee8eca4ed5832f5befb67df0dd2332b607f (diff)
Add --columns option
This option can be used to override the list of columns as found in the first line of the CSV file or provide a mapping for values found in the first line to PSKC properties.
-rw-r--r--docs/csv2pskc.rst19
-rw-r--r--pskc/scripts/csv2pskc.py27
-rw-r--r--tests/test_csv2pskc.doctest86
3 files changed, 127 insertions, 5 deletions
diff --git a/docs/csv2pskc.rst b/docs/csv2pskc.rst
index 1c82a9f..06aabce 100644
--- a/docs/csv2pskc.rst
+++ b/docs/csv2pskc.rst
@@ -32,6 +32,25 @@ Options
By default :program:`csv2pskc` writes a PSKC file to stdout. This option
can be used to save to a file instead.
+.. option:: -c COL,COL,.., --columns COL,COL,..
+
+ Specify the meaning of the columns in the CSV file. By default the first
+ row of the CSV file is expected to list the names of the columns.
+
+ Any property of :class:`~pskc.key.Key` instances can be used as well as
+ :class:`~pskc.policy.Policy` properties via ``policy``. For example:
+ ``serial``, ``secret``, ``counter``, ``time_offset``, ``time_interval``,
+ ``interval``, ``time_drift``, ``issuer``, ``manufacturer``,
+ ``response_length``, ``policy.pin_min_length``.
+
+ This option can either specify a list of columns or a COL:KEY mapping
+ where COL refers to the value found in the first line of the CSV file and
+ KEY refers to a property as described above.
+
+ It is possible to map a single column in the CSV file to multiple PSKC
+ properties (e.g. use of ``id+serial`` sets both the ID and device serial
+ number to the value found in that column).
+
.. option:: -p PASS/FILE, --password PASS/FILE, --passwd PASS/FILE
Encrypt the PSKC file with the specified password. If the argument refers
diff --git a/pskc/scripts/csv2pskc.py b/pskc/scripts/csv2pskc.py
index 814e756..a815755 100644
--- a/pskc/scripts/csv2pskc.py
+++ b/pskc/scripts/csv2pskc.py
@@ -53,6 +53,9 @@ parser.add_argument(
'-o', '--output', metavar='FILE',
help='write PSKC to file instead of stdout')
parser.add_argument(
+ '-c', '--columns', metavar='COL|COL:LABEL,..',
+ help='list of columns or label to column mapping to import')
+parser.add_argument(
'-p', '--password', '--passwd', metavar='PASS/FILE',
help='password to use for encrypting the PSKC file)')
parser.add_argument(
@@ -103,14 +106,28 @@ def main():
args = parser.parse_args()
# open the CSV file
csvfile = open_csvfile(open(args.input, 'r') if args.input else sys.stdin)
- columns = next(csvfile)
+ # figure out the meaning of the columns
+ columns = [x.lower().replace(' ', '_') for x in next(csvfile)]
+ if args.columns:
+ if ':' in args.columns:
+ # --columns is a list of mappings
+ mapping = dict(
+ (label.lower().replace(' ', '_'), key.lower())
+ for label, key in (
+ column.split(':')
+ for column in args.columns.split(',')))
+ columns = [mapping.get(column, column) for column in columns]
+ else:
+ # --columns is a list of columns
+ columns = [x.lower() for x in args.columns.split(',')]
# store rows in PSKC structure
pskcfile = pskc.PSKC()
for row in csvfile:
- data = dict(
- (key, from_column(key, value, args))
- for key, value in zip(columns, row)
- if value and key not in ('', '-'))
+ data = {}
+ for column, value in zip(columns, row):
+ for key in column.split('+'):
+ if value and key not in ('', '-'):
+ data[key] = from_column(key, value, args)
pskcfile.add_key(**data)
# encrypt the file if needed
if args.secret:
diff --git a/tests/test_csv2pskc.doctest b/tests/test_csv2pskc.doctest
index afa82e2..0c6e8d5 100644
--- a/tests/test_csv2pskc.doctest
+++ b/tests/test_csv2pskc.doctest
@@ -306,6 +306,92 @@ automatically pick up tab-separated files.
</pskc:KeyContainer>
+We can use the --columns option to override using the first row to specify
+the key properties.
+
+>>> f = tempfile.NamedTemporaryFile('w+t')
+>>> x = f.write('''
+... nr,key,start date,info
+... 121232,6848464354638468468835346896846846846846,2017-04-01,something
+... 213422,9843138168168196616849849634548496832446,2017-02-12,else
+... '''.lstrip())
+>>> f.flush()
+>>> sys.argv = ['csv2pskc', f.name, '--columns', 'id+serial,secret,start_date,-']
+>>> main() #doctest: +REPORT_UDIFF +NORMALIZE_WHITESPACE
+<?xml version="1.0" encoding="UTF-8"?>
+<pskc:KeyContainer xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" Version="1.0">
+ <pskc:KeyPackage>
+ <pskc:DeviceInfo>
+ <pskc:SerialNo>121232</pskc:SerialNo>
+ <pskc:StartDate>2017-04-01T00:00:00</pskc:StartDate>
+ </pskc:DeviceInfo>
+ <pskc:Key Id="121232">
+ <pskc:Data>
+ <pskc:Secret>
+ <pskc:PlainValue>aEhGQ1RjhGhGiDU0aJaEaEaEaEY=</pskc:PlainValue>
+ </pskc:Secret>
+ </pskc:Data>
+ </pskc:Key>
+ </pskc:KeyPackage>
+ <pskc:KeyPackage>
+ <pskc:DeviceInfo>
+ <pskc:SerialNo>213422</pskc:SerialNo>
+ <pskc:StartDate>2017-02-12T00:00:00</pskc:StartDate>
+ </pskc:DeviceInfo>
+ <pskc:Key Id="213422">
+ <pskc:Data>
+ <pskc:Secret>
+ <pskc:PlainValue>mEMTgWgWgZZhaEmEljRUhJaDJEY=</pskc:PlainValue>
+ </pskc:Secret>
+ </pskc:Data>
+ </pskc:Key>
+ </pskc:KeyPackage>
+</pskc:KeyContainer>
+
+
+Alternatively, we can provide a mapping for column names found in the CSV
+file to key properties.
+
+>>> f = tempfile.NamedTemporaryFile('w+t')
+>>> x = f.write('''
+... nr,key,start date,info
+... 121232,6848464354638468468835346896846846846846,2017-04-01,something
+... 213422,9843138168168196616849849634548496832446,2017-02-12,else
+... '''.lstrip())
+>>> f.flush()
+>>> sys.argv = ['csv2pskc', f.name, '--columns', 'key:secret,nr:id+serial,info:-']
+>>> main() #doctest: +REPORT_UDIFF +NORMALIZE_WHITESPACE
+<?xml version="1.0" encoding="UTF-8"?>
+<pskc:KeyContainer xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" Version="1.0">
+ <pskc:KeyPackage>
+ <pskc:DeviceInfo>
+ <pskc:SerialNo>121232</pskc:SerialNo>
+ <pskc:StartDate>2017-04-01T00:00:00</pskc:StartDate>
+ </pskc:DeviceInfo>
+ <pskc:Key Id="121232">
+ <pskc:Data>
+ <pskc:Secret>
+ <pskc:PlainValue>aEhGQ1RjhGhGiDU0aJaEaEaEaEY=</pskc:PlainValue>
+ </pskc:Secret>
+ </pskc:Data>
+ </pskc:Key>
+ </pskc:KeyPackage>
+ <pskc:KeyPackage>
+ <pskc:DeviceInfo>
+ <pskc:SerialNo>213422</pskc:SerialNo>
+ <pskc:StartDate>2017-02-12T00:00:00</pskc:StartDate>
+ </pskc:DeviceInfo>
+ <pskc:Key Id="213422">
+ <pskc:Data>
+ <pskc:Secret>
+ <pskc:PlainValue>mEMTgWgWgZZhaEmEljRUhJaDJEY=</pskc:PlainValue>
+ </pskc:Secret>
+ </pskc:Data>
+ </pskc:Key>
+ </pskc:KeyPackage>
+</pskc:KeyContainer>
+
+
We can encrypt the resulting PSKC file with a passphrase.
>>> f = tempfile.NamedTemporaryFile('w+t')