| #!/usr/bin/python3 |
| # Dummy Password Manager for Tizen Studio CLI |
| # |
| # Copyright (c) 2021 Project CHIP Authors |
| # |
| # Licensed 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 argparse |
| import fcntl |
| import os |
| import pickle |
| import sys |
| |
| |
| class Secrets: |
| |
| def __init__(self, filename: str): |
| self.filename = filename |
| self.dirty = False |
| self.secrets = {} |
| self.fp = None |
| |
| def __enter__(self): |
| self.fp = open(self.filename, "a+b") |
| fcntl.flock(self.fp.fileno(), fcntl.LOCK_EX) |
| self._load() |
| return self |
| |
| def __exit__(self, _type, value, tb): |
| if self.dirty: |
| self._save() |
| fcntl.flock(self.fp.fileno(), fcntl.LOCK_UN) |
| self.fp.close() |
| |
| @staticmethod |
| def _build_key(label: str, **kw): |
| return label + ":" + str(tuple(sorted(kw.items()))) |
| |
| def _load(self): |
| try: |
| self.fp.seek(0) |
| self.secrets = pickle.load(self.fp) |
| except EOFError: |
| # Unpickling an empty file is not an error for us |
| pass |
| except ValueError as e: |
| print("ERROR: " + str(e), file=sys.stderr) |
| |
| def _save(self): |
| try: |
| self.fp.seek(0) |
| self.fp.truncate() |
| pickle.dump(self.secrets, self.fp) |
| except IOError as e: |
| print("ERROR: " + str(e), file=sys.stderr) |
| |
| def clear(self, label: str, **kw): |
| key = self._build_key(label, **kw) |
| self.secrets.pop(key, None) |
| self.dirty = True |
| |
| def store(self, label: str, password: str, **kw): |
| key = self._build_key(label, **kw) |
| self.secrets[key] = password |
| self.dirty = True |
| |
| def lookup(self, label: str, **kw): |
| key = self._build_key(label, **kw) |
| return self.secrets.get(key, "") |
| |
| |
| parser = argparse.ArgumentParser( |
| formatter_class=argparse.RawDescriptionHelpFormatter, |
| description=""" |
| Dummy Password Manager for Tizen Studio CLI. |
| |
| This simple password manager circumvents the requirement of having functional |
| D-Bus Secrets service (org.freedesktop.secrets) in the Docker container. As a |
| storage this manager uses plain-text file with pickled data (~/.secretsdb). |
| |
| Please, DO NOT store real secrets in it!""") |
| subparsers = parser.add_subparsers(dest='command', required=True) |
| |
| parser_clear = subparsers.add_parser( |
| "clear", help="Remove passward associated with given key value pairs") |
| parser_clear.add_argument("-l", "--label", action='store', required=True, |
| help="label for given key value pairs") |
| parser_clear.add_argument("kw", nargs='*', |
| help="key value pairs") |
| |
| parser_store = subparsers.add_parser( |
| "store", help="Store passward for given key value pairs") |
| parser_store.add_argument("-l", "--label", action='store', required=True, |
| help="label for given key value pairs") |
| parser_store.add_argument("-p", "--password", action='store', required=True, |
| help="password for given key value pairs") |
| parser_store.add_argument("kw", nargs='*', |
| help="key value pairs") |
| |
| parser_lookup = subparsers.add_parser( |
| "lookup", help="Retrieve passward associated with given key value pairs") |
| parser_lookup.add_argument("-l", "--label", action='store', required=True, |
| help="label for given key value pairs") |
| parser_lookup.add_argument("kw", nargs='*', |
| help="key value pairs") |
| |
| args = parser.parse_args() |
| kw = dict(zip(args.kw[:: 2], args.kw[1:: 2])) |
| |
| with Secrets(os.path.expanduser("~/.secretsdb")) as secrets: |
| if args.command == "clear": |
| secrets.clear(args.label, **kw) |
| elif args.command == "store": |
| secrets.store(args.label, args.password, **kw) |
| elif args.command == "lookup": |
| password = secrets.lookup(args.label, **kw) |
| print(password) |