Den fre 26 feb. 2021 kl 08:31 skrev Daniel Sahlberg <
daniel.l.sahlb...@gmail.com>:

> Moving from users@ to dev@
>

[...]

I was in a bit of a hurry this morning and didn't test properly. The
previous version (store-plaintext-passwords2.txt) contained a number of
bugs which I hope I've corrected here:

- Didn't properly encode keys/values before checking length. Non-ascii
characters in the values raised an exception.
- Didn't properly add the lenght to K when writing the hash file (added the
key name instead)
- Reorganized prompting for password and checking for new file and the -u
argument to show the error message before prompting for a password [UX
improvement]

New version attached.

Kind regards,
Daniel
Index: contrib/client-side/store-plaintext-password.py
===================================================================
--- contrib/client-side/store-plaintext-password.py     (nonexistent)
+++ contrib/client-side/store-plaintext-password.py     (working copy)
@@ -0,0 +1,127 @@
+#!/usr/bin/env python3
+
+# Script to store password in plaintext in ~/.subversion/auth/svn.simple/
+#
+# ====================================================================
+#    Licensed to the Apache Software Foundation (ASF) under one
+#    or more contributor license agreements.  See the NOTICE file
+#    distributed with this work for additional information
+#    regarding copyright ownership.  The ASF licenses this file
+#    to you under the Apache License, Version 2.0 (the
+#    "License"); you may not use this file except in compliance
+#    with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing,
+#    software distributed under the License is distributed on an
+#    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#    KIND, either express or implied.  See the License for the
+#    specific language governing permissions and limitations
+#    under the License.
+# ====================================================================
+
+import hashlib
+import argparse
+import os
+import getpass
+import sys
+
+def readHashFile(filename):
+    """Read a hashfile as written by svn_hash_write2() to a list of tuples 
(key, value)"""
+    hash = {}
+    with open(authfileName, 'r', encoding='utf-8') as file:
+        while True:
+            # Expecting K [length] or END
+            line = file.readline()
+            if not line:
+                raise Exception('Parse failed, expected K [length] or END, got 
EOF')
+            line = line.strip()
+            if line.strip() == 'END':
+                if file.readline():
+                    raise Exception('Parse failed, unexpected data after END')
+                return hash
+            elif line[:1] != 'K':
+                raise Exception('Parse failed, expected K [length]')
+            
+            # Read keyname
+            key = file.readline()
+            if not line:
+                raise Exception('Parse failed, expected keyname')
+            key = key.strip()
+            if len(key.encode('utf-8')) != int(line[2:]):
+                raise Exception('Parse failed, invalid key length {} expected 
{}'.format(len(key.encode('utf-8')), line[2:]))
+            
+            # Read V [length]
+            line = file.readline()
+            if not line:
+                raise Exception('Parse failed, expected V [length], got EOF')
+            line = line.strip()
+            if line[:1] != 'V':
+                raise Exception('Parse failed, expected V [length]')
+            
+            # Read value
+            value = file.readline()
+            if not value:
+                raise Exception('Parse failed, expected value')
+            value = value.strip()
+            if len(value.encode('utf-8')) != int(line[2:]):
+                raise Exception('Parse failed, invalid value length {} 
expected {}'.format(len(value.encode('utf-8')), line[2:]))
+            
+            # Store
+            hash[key] = value
+
+def writeHashFile(filename, hash):
+    """Write a list of tuples (key, value) to a hashfile of the same format as 
svn_hash_write2()"""
+    with open(filename + '.tmp', 'x', encoding='utf-8') as file:
+        for key, val in hash.items():
+            file.write('K {}\n'.format(len(key.encode('utf-8'))))
+            file.write('{}\n'.format(key))
+            file.write('V {}\n'.format(len(val.encode('utf-8'))))
+            file.write('{}\n'.format(val))
+        file.write('END\n')
+    os.rename(filename + '.tmp', filename)
+
+# Parse arguments
+parser = argparse.ArgumentParser(description='''Store plain-text password in 
~/.subversion/auth/svn.simple/\n\n
+Existing passwords and authentication realms can be inspected by:\n\n
+  svn auth [--show-passwords]\n\n
+The authentication realm can also be found using:\n\n
+  svn info URL\n\n
+Realm will be read from stdin if not provided on the command line.''', 
formatter_class=argparse.RawDescriptionHelpFormatter)
+parser.add_argument('realm', help='Server authentication real')
+parser.add_argument('-u', '--user', help='Set username', nargs='?')
+args = parser.parse_args()
+
+# The file name is the md5encoding of the realm
+m = hashlib.md5()
+m.update(args.realm.encode('utf-8'))
+authfileName = 
os.path.join(os.path.expanduser('~/.subversion/auth/svn.simple/'), 
m.hexdigest())
+
+# If new file, check that username was given as an argument before prompting 
for password
+if not os.path.isfile(authfileName) and args.user is None:
+    print('New file, username required (see -u)\n', file=sys.stderr)
+    parser.print_help()
+    quit()
+    
+# Prompt for password
+password = getpass.getpass("Enter password: ")
+        
+# In an existing file, we add/replace password/username/passtype
+if os.path.isfile(authfileName):
+    hash = readHashFile(authfileName)
+    if args.user is not None:
+        hash['username'] = args.user
+    hash['password'] = password
+    hash['passtype'] = 'simple'
+    
+# For a new file, set realmstring, username, password and passtype
+else:
+    hash = {}
+    hash['svn:realmstring'] = args.realm
+    hash['username'] = args.user
+    hash['passtype'] = 'simple'
+    hash['password'] = password
+
+# Write out the resulting file
+writeHashFile(authfileName, hash)

Property changes on: contrib/client-side/store-plaintext-password.py
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property

Reply via email to