Skip to content

Commit

Permalink
Fix handling concurrent runs of dummy secret tool
Browse files Browse the repository at this point in the history
  • Loading branch information
arkq committed Apr 26, 2022
1 parent f2b6703 commit fc239d9
Showing 1 changed file with 83 additions and 36 deletions.
119 changes: 83 additions & 36 deletions integrations/docker/images/chip-build-tizen/secret-tool.py
Original file line number Diff line number Diff line change
@@ -1,80 +1,127 @@
#!/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
from argparse import ArgumentParser


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):
def _load(self):
try:
with open(self.filename, "rb") as f:
self.secrets = pickle.load(f)
except (IOError, ValueError) as e:
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):
def _save(self):
try:
with open(self.filename, "wb") as f:
pickle.dump(self.secrets, f)
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 = ArgumentParser()
subparsers = parser.add_subparsers(dest='command', required=True)
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description="""
Dummy Password Manager for Tizen Studio CLI.
parser_clear = subparsers.add_parser("clear")
parser_clear.add_argument("-l", "--label", action='store', required=True)
parser_clear.add_argument("kw", nargs='*')
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).
parser_store = subparsers.add_parser("store")
parser_store.add_argument("-l", "--label", action='store', required=True)
parser_store.add_argument("-p", "--password", action='store', required=True)
parser_store.add_argument("kw", nargs='*')
Please, DO NOT store real secrets in it!""")
subparsers = parser.add_subparsers(dest='command', required=True)

parser_lookup = subparsers.add_parser("lookup")
parser_lookup.add_argument("-l", "--label", action='store', required=True)
parser_lookup.add_argument("kw", nargs='*')
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 open(os.path.expanduser("~/.secretsdb.log"), "a") as f:
f.write("%s: %s: %s\n" % (args.command, args.label, kw))

secrets = Secrets(os.path.expanduser("~/.secretsdb"))
secrets.load()

if args.command == "clear":
secrets.clear(args.label, **kw)
secrets.save()
elif args.command == "store":
secrets.store(args.label, args.password, **kw)
secrets.save()
elif args.command == "lookup":
password = secrets.lookup(args.label, **kw)
print(password)
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)

0 comments on commit fc239d9

Please sign in to comment.