Browse Source

Committing current state

master
Bernd Zeimetz 7 years ago
parent
commit
8a70294dbf
4 changed files with 285 additions and 4 deletions
  1. 131
    0
      flaskext/flask_shelve.py
  2. 2
    0
      monkeystore/config.py
  3. 138
    0
      monkeystore/pwstore.py
  4. 14
    4
      monkeystore/web.py

+ 131
- 0
flaskext/flask_shelve.py View File

@@ -0,0 +1,131 @@
"""Integrate the shelve module with flask."""
import os
import shelve
import fcntl
import time

import flask
from flask import _request_ctx_stack


LOCK_POLL_SECS = 0.02


def init_app(app):
"""Initialize the flask app.

Before calling this function, the `SHELVE_FILENAME` config
value must be set, e.g.::

app.config['SHELVE_FILENAME'] = 'my_db_file'

This function will associate an object with the current flask
app, which is accessible using the ``get_shelve`` function.

"""
if 'SHELVE_FILENAME' not in app.config:
raise RuntimeError("SHELVE_FILENAME is required in the "
"app configuration.")
app.config.setdefault('SHELVE_PROTOCOL', None)
app.config.setdefault('SHELVE_WRITEBACK', False)
app.config.setdefault('SHELVE_LOCKFILE',
app.config['SHELVE_FILENAME'] + '.lock')
app.extensions['shelve'] = _Shelve(app)


def get_shelve(mode='c'):
"""Get an instance of shelve.

This function will return a ``shelve.Shelf`` instance.
It does this by finding the shelve object associated with
the current flask app (using ``flask.current_app``).

"""
return flask.current_app.extensions['shelve'].open_db(mode=mode)


class _Shelve(object):
def __init__(self, app):
self.app = app
self.app.teardown_request(self.close_db)
self._lock = _FileLock(app.config['SHELVE_LOCKFILE'])
# "touch" the db file so that view functions can
# open the db with mode='r' and not have to worry
# about the db not existing.
self._open_db('c').close()

def open_db(self, mode='r'):
if self._is_write_mode(mode):
fileno = self._lock.acquire_write_lock()
writer = self._open_db(mode)
writer.fileno = fileno
_request_ctx_stack.top.shelve_writer = writer
return writer
else:
fileno = self._lock.acquire_read_lock()
reader = self._open_db(mode)
reader.fileno = fileno
_request_ctx_stack.top.shelve_reader = reader
return reader

def _is_write_mode(self, mode):
return mode in ('c', 'w', 'n')

def _open_db(self, flag):
cfg = self.app.config
return shelve.open(
filename=cfg['SHELVE_FILENAME'],
flag=flag,
protocol=cfg['SHELVE_PROTOCOL'],
writeback=cfg['SHELVE_WRITEBACK']
)

def close_db(self, ignore_arg):
top = _request_ctx_stack.top
if hasattr(top, 'shelve_writer'):
writer = top.shelve_writer
writer.close()
self._lock.release_write_lock(writer.fileno)
elif hasattr(top, 'shelve_reader'):
reader = top.shelve_reader
reader.close()
self._lock.release_read_lock(reader.fileno)


class _FileLock(object):
def __init__(self, lockfile):
self._filename = lockfile
self._waiting_for_write_lock = False
self._waiting_for_read_lock = False
# Touch the file so we can acquire read locks.
open(self._filename, 'w').close()

def acquire_read_lock(self):
# Keep in mind that we're operating in a multithreaded environment.
# If someone is waiting on a write lock, we can essentially keep them
# waiting indefinitely if we keep on handing on read locks (there
# can be multiple read locks issued at any time). So instead, if
# someone is waiting on a write lock, we poll until they've
# acquired the lock and then block on aquiring a lock.
while self._waiting_for_write_lock:
time.sleep(LOCK_POLL_SECS)
fileno = os.open(self._filename, os.O_RDWR)
self._waiting_for_read_lock = True
fcntl.flock(fileno, fcntl.LOCK_SH)
self._waiting_for_read_lock = False
return fileno

def acquire_write_lock(self):
fileno = os.open(self._filename, os.O_RDWR)
self._waiting_for_write_lock = True
fcntl.flock(fileno, fcntl.LOCK_EX)
self._waiting_for_write_lock = False
return fileno

def release_read_lock(self, fileno):
fcntl.flock(fileno, fcntl.LOCK_UN)
os.close(fileno)

def release_write_lock(self, fileno):
fcntl.flock(fileno, fcntl.LOCK_UN)
os.close(fileno)

+ 2
- 0
monkeystore/config.py View File

@@ -1,6 +1,8 @@
DEBUG = 1

# FIXME !!!!!!
PASSWORD_DIRECTORY = '/tmp/monkeystore'
SHELVE_FILENAME = 'monkeystore.shelve'

KEYS = {
'zebe' : '6FF9435F '

+ 138
- 0
monkeystore/pwstore.py View File

@@ -0,0 +1,138 @@
# -*- coding: utf-8 -*-
__author__ = "Bernd Zeimetz"
__contact__ = "bzed@debian.org"
__license__ = """
Copyright (C) 2010 Bernd Zeimetz <bzed@debian.org>
Copyright (C) 2012 Bernd Zeimetz <b.zeimetz@conova.com>

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.

THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""


import os
import tempfile
from git import Repo
from pwgen import pwgen
import gnupg

class GPGpwstoreException(Exception):
pass

#class GPGpwstoreData(object):
# def __init__(self, hostname, service, username, password_gpg, description):
# for i in ["hostname", "service", "username", "password_gpg"]:
# if not locals()[i] then:
# raise GPGpwstoreException("Failed to store password: %s must be set (not %s)!" %
# (i, str(locals()[i])))
# setattr(self, i, locals()[i])
# for i in ["description"]:
# if locals()[i] then:
# setattr(self, i, locals()[i])
# else:
# setattr(self, i, "")

class GPGpwstore(object):

def __init__(self, config):
self.gpg_keys = config['KEYS']
self.users = self.gpg_keys.keys()
self.pw_directory = config['PASSWORD_DIRECTORY']
self.debug = config['DEBUG']
self.category_users = config['CATEGORY_USERS']
self.categories = self.category_users.keys()
self.commit_messages = []
self.__init_directories()
self.gpg = gnupg.GPG()
self.tempdir = tempfile.mkdtemp()
self.cryptstring = pwgen(200, num_pw=1)


def __init_directories(self):
for p in [self.pw_directory]+[ os.path.join(self.pw_directory, c) for c in self.categories]:
if not os.path.exists(p):
os.mkdir(p, mode=0700)
else:
if not os.path.isdir(p):
raise GPGpwstoreException("%s exists but it is not a directory" %(p,))
if not os.path.exists(os.path.join(self.pw_directory, '.git')):
self.repository = Repo.init(self.pw_directory)
else:
self.repository = Repo(self.pw_directory)
if self.repository.untracked_files or self.repository.is_dirty():
self.commit()

def __get_directory__(category, hostname, service, username):
for i in ["category", "hostname", "service", "username"]:
if not locals()[i] then:
raise GPGpwstoreException("Failed to retrieve/set password: %s must be set (not %s)!" %
(i, str(locals()[i])))
if not category in self.categories:
raise GPGpwstoreException("Unknown category: %s" %(category,))

return os.path.join(self.pw_directory,
category,
hostname.strip().encode('hex'),
service.strip().encode('hex'),
username.strip().encode('hex')
)

def __crypt_password__(category, password):
try:
keys = [ self.gpg_keys[x] for x in self.category_users['category'] ]
except KeyError:
raise GPGpwstoreException("Category %s not known!" %(category,))
gpg_data = self.gpg.encrypt(password, keys)
if not data.ok:
raise GPGpwstoreException("Failed to encrypt password: \n%s" %(data.stderr,))
return data.data

def __write_password__(category, hostname, service, username, password):
pwfile = os.path.join(
self.__get_directory__(category, hostname, service, username),
'password.gpg')
data = self.__crypt_password__(category, password)
with open(pwfile, 'w') as f:
f.write(data)

def __read_password_gpg__(category, hostname, service, username):
pwfile = os.path.join(
self.__get_directory__(category, hostname, service, username),
'password.gpg')
with open(pwfile, 'r') as f:
data=f.read()
return data


def commit(self):
if self.repository.untracked_files:
self.repository.git.add(self.repository.untracked_files)
if self.repository.is_dirty():
commit_details=""
if self.commit_messages:
commit_details='\n * '.join([''] + self.commit_messages)
self.repository.git.commit(m="monkeystore commit\n%s" %(commit_details,), a=True)
self.commit_messages = []



+ 14
- 4
monkeystore/web.py View File

@@ -32,9 +32,13 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import flask
import string
from flaskext.xmlrpc import XMLRPCHandler, Fault
from flaskext.flask_shelve import init_app

from pwgen import pwgen

app = flask.Flask(__name__)
init_app(app)

__all__ = ["app"]

app.config.from_object('monkeystore.config')
@@ -44,15 +48,18 @@ api.connect(app, '/')


@api.register
def add_password(category, hostname, service, username, description, password_crypt):
def add_password(pwstore_user, pwstore_token,
category, hostname, service, username, description, password_crypt):
pass

@api.register
def update_password(category, hostname, service, username, password_crypt):
def update_password(pwstore_user, pwstore_token,
category, hostname, service, username, password_crypt):
pass

@api.register
def update_description(category, hostname, service, username, description):
def update_description(pwstore_user, pwstore_token,
category, hostname, service, username, description):
pass

@api.register
@@ -64,7 +71,7 @@ def list_categories():
pass

@api.register
def list_servers():
def list_servers(category):
pass

@api.register
@@ -75,4 +82,7 @@ def generate_password(length=8):
numerals=True, capitalize=True,
symbols=True, allowed_symbols=string.punctuation)

@api.register
def retrieve_token(pwstore_user):
token = generate_password(200)


Loading…
Cancel
Save