Browse Source

Add basic stuff.

master
Bernd Zeimetz 7 years ago
commit
faf2dde429

+ 1
- 0
flaskext/__init__.py View File

@@ -0,0 +1 @@
__import__('pkg_resources').declare_namespace(__name__)

+ 311
- 0
flaskext/xmlrpc.py View File

@@ -0,0 +1,311 @@
# -*- coding: utf-8 -*-
"""
flaskext.xmlrpc
===============

Adds support for creating XML-RPC APIs to Flask.

:copyright: (c) 2010 by Matthew "LeafStorm" Frazier.
:license: MIT, see LICENSE for more details.
"""

from flask import request, current_app
from SimpleXMLRPCServer import SimpleXMLRPCDispatcher as Dispatcher
import sys
import xmlrpclib

Fault = xmlrpclib.Fault

class XMLRPCHandler(Dispatcher):
"""
This is the basic XML-RPC handler class. To use it, you create it::
handler = XMLRPCHandler('api')
Then, you can register functions with the :meth:`register` method::
@handler.register
def spam():
pass
:meth:`register` is just an alias for :meth:`register_function`, so you
can use that too.
You can also register an instance using the :meth:`register_instance`
method, and any methods on said instance will be exposed if they do not
start with an ``_``.
Then, you connect it to a :class:`~flask.Flask` instance or a Flask
module with the :meth:`connect` method, like this::
handler.connect(app, '/')
:param endpoint_name: The name to use as an endpoint when connected to
an app or module. If not specified here, you specify
when you call :meth:`connect`.
:param instance: The instance to register and expose the methods of.
:param introspection: Whether to register the introspection functions,
like :obj:`system.listMethods`. (It will by
default.)
:param multicall: Whether to register the :obj:`system.multicall`
function. (It won't by default.)
"""
def __init__(self, endpoint_name=None, instance=None, introspection=True,
multicall=False):
if sys.version_info[:2] < (2, 5):
Dispatcher.__init__(self)
else:
Dispatcher.__init__(self, True, 'utf-8')
self.endpoint_name = endpoint_name
if introspection:
self.register_introspection_functions()
if multicall:
self.register_multicall_functions()
if instance:
self.register_instance(instance)
def register(self, *args, **kwargs):
"""
An alias for :meth:`register_function`.
"""
return self.register_function(*args, **kwargs)
def register_function(self, function, name=None):
"""
This will register the given function. There are two ways to use it.
As a plain old method, with or without a name::
handler.register_function(spam)
handler.register_function(spam, 'spam')
As a decorator, also with or without a name::
@handler.register_function
def spam():
pass
@handler.register_function('spam')
def spam():
pass
It's shorter and easier to use :meth:`register`, however, as it does
the exact same thing.
:param function: The function to register. (In the named decorator
form, this is the function's name.)
:param name: The name to use, except in the named decorator form.
If not given, the function's :obj:`__name__` attribute
will be used.
"""
if isinstance(function, basestring):
return lambda fn: self.register_function(fn, function)
return Dispatcher.register_function(self, function, name)
def register_instance(self, instance, allow_dotted_names=False):
"""
This registers any kind of object. If the requested method hasn't been
registered by :meth:`register_function`, it will be checked against
the instance. You can only have one instance at a time, however.
If :obj:`allow_dotted_names` is True, the name will be split on the
dots and the object will be traveled down recursively. However, this
is a **HUGE SECURITY LOOPHOLE**, as while private methods (starting
with ``_``) will not be exposed, it's still possible that someone
could get access to your globals and do very bad things. So don't
do it unless you have a very good reason.
:param instance: The instance to register.
:param allow_dotted_names: Whether to resolve dots in method names.
You probably shouldn't.
"""
# Yes, it's just a wrapper. I know. This way the docs are consistent.
Dispatcher.register_instance(self, instance, allow_dotted_names)
def connect(self, app_module, route, endpoint_name=None):
"""
Connects the handler to an app or module. You have to provide the
app and the URL route to use. The route can't contain any variable
parts, because there is no way to get them to the method. ::
handler.connect(app, '/api')
:param app_module: The app or module to connect the handler to.
:param route: The URL route to use for the handler.
:param endpoint_name: The name to use when connecting the endpoint.
"""
if endpoint_name is None:
endpoint_name = self.endpoint_name
if endpoint_name is None: # still
raise RuntimeError("No endpoint name given!")
app_module.add_url_rule(route, endpoint_name, self.handle_request,
methods=['POST'])
def handle_request(self):
"""
This is the actual request handler that is routed by :meth:`connect`.
It takes the request data, dispatches the method, and sends it back
to the client.
"""
response_data = self._marshaled_dispatch(request.data)
return current_app.response_class(response_data,
content_type='text/xml')
def namespace(self, prefix):
"""
This returns a :class:`XMLRPCNamespace` object, which has
:meth:`~XMLRPCNamespace.register` and
:meth:`~XMLRPCNamespace.register_function` methods. These forward
directly to the :meth:`register_function` method of the parent they
were created from, but they will prepend the given prefix, plus a dot,
to the name registered. For example::
blog = handler.namespace('blog')
@blog.register
def new_post(whatever):
pass
would make :obj:`new_post` available as :obj:`blog.new_post`.
:param prefix: The name to prefix the methods with.
"""
return XMLRPCNamespace(self, prefix)


class XMLRPCNamespace(object):
"""
This is a simple proxy that can register methods, and passes them on to
the :class:`XMLRPCHandler` that created it with a given name added as a
prefix (with a dot). For more nesting, you can create namespaces from
namespaces with the :meth:`namespace` method.
:parameter handler: The handler to pass the methods to.
:parameter prefix: The prefix to give to the assigned methods. A dot will
be appended.
"""
def __init__(self, handler, prefix):
self.handler = handler
self.prefix = prefix
def register_function(self, function, name=None):
"""
Registers a function. Use is the same as with the
:meth:`XMLRPCHandler.register_function` method.
:param function: The function to register. (In the named decorator
form, this is the function's name.)
:param name: The name to use, except in the named decorator form.
If not given, the function's :obj:`__name__` attribute
will be used.
"""
if isinstance(function, basestring):
return lambda fn: self.register_function(fn, function)
if name is None:
name = function.__name__
new_name = self.prefix + '.' + name
self.handler.register_function(function, new_name)
def register(self, *args, **kwargs):
"""
An alias for :meth:`register_function`. As with
:meth:`XMLRPCHandler.register`, it's shorter and easier to type.
"""
return self.register_function(*args, **kwargs)
def namespace(self, name):
"""
Returns another namespace for the same handler, with the given name
postfixed to the current namespace's prefix. For example, ::
handler.namespace('foo').namespace('bar')
gives the same result as::
handler.namespace('foo.bar')
:param prefix: The name to prefix the methods with.
"""
return XMLRPCNamespace(self.handler, self.prefix + '.' + name)


def dump_method_call(method, *params):
"""
This marshals the given method and parameters into a proper XML-RPC method
call. It's very useful for testing.
:param method: The name of the method to call.
:param params: The parameters to pass to the method.
"""
return xmlrpclib.dumps(params, methodname=method)


def load_method_response(response):
"""
This returns the actual value returned from an XML-RPC response. If it's
a :obj:`Fault` instance, it will return the fault instead of the value.
This is also useful for testing.
:param response: The marshaled XML-RPC method response or fault.
"""
try:
return xmlrpclib.loads(response)[0][0]
except Fault, fault:
return fault


def test_xmlrpc_call(client, rpc_path, method, *params):
"""
This makes a method call using a Werkzeug :obj:`Client`, such as the one
returned by :meth:`flask.Flask.test_client`. It constructs the method
call, makes the request, and then returns the response value or a
:obj:`Fault`.
:param client: A :obj:`werkzeug.Client`.
:param rpc_path: The path to the XML-RPC handler.
:param method: The method to call.
:param params: The parameters to pass to the method.
"""
rv = client.post(
rpc_path,
data=dump_method_call(method, *params),
content_type='text/xml'
)
return load_method_response(rv.data)
test_xmlrpc_call.__test__ = False # prevents Nose from collecting it


class XMLRPCTester(object):
"""
This lets you conveniently make method calls using a Werkzeug
:obj:`Client`, like the one returned by :meth:`flask.Flask.test_client`.
You create it with the :obj:`Client` and the path to the responder, and
then you call it with the method and params.
:param client: A :obj:`werkzeug.Client`.
:param rpc_path: The path to the XML-RPC handler.
"""
__test__ = False # prevents Nose from collecting it
def __init__(self, client, rpc_path):
self.client = client
self.rpc_path = rpc_path
def call(self, method, *params):
"""
This calls the client's :obj:`post` method with the responder path,
the marshaled method call, and a content type of ``text/xml``. It will
return the unmarshaled response or fault.
You can just call the instance like a function for the same effect.
These two calls are equivalent::
tester.call('hello', 'world')
tester('hello', 'world')
:param method: The name of the method to call.
:param params: The parameters to pass to the method.
"""
return test_xmlrpc_call(self.client, self.rpc_path, method, *params)
def __call__(self, method, *params):
return self.call(method, *params)

+ 51
- 0
monkeystore-server.py View File

@@ -0,0 +1,51 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
__author__ = "Bernd Zeimetz"
__contact__ = "bzed@debian.org"
__license__ = """
Copyright (C) 2010-2012 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 sys

try:
from monkeystore.web import app
except ImportError:
import sys
import os
sys.path = [os.path.realpath(os.path.dirname(__file__))] + sys.path
from monkeystore.web import app

if __name__ == '__main__':
if len(sys.argv) > 1:
host, port = sys.argv[1:]
app.run(host=host, port=int(port))
else:
app.run()



+ 43
- 0
monkeystore.fcgi View File

@@ -0,0 +1,43 @@
# -*- 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.
"""

try:
from monkeystore.web import app as application
except ImportError:
import sys
import os
sys.path = [os.path.realpath(os.path.dirname(__file__))] + sys.path
from monkeystore.web import app as application

if __name__ == '__main__':
from flup.server.fcgi import WSGIServer
WSGIServer(application).run()


+ 39
- 0
monkeystore.wsgi View File

@@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
__author__ = "Bernd Zeimetz"
__contact__ = "bzed@debian.org"
__license__ = """
Copyright (C) 2010,2012 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.
"""

try:
from monkeystore.web import app as application
except ImportError:
import sys
import os
sys.path = [os.path.realpath(os.path.dirname(__file__))] + sys.path
from monkeystore.web import app as application


+ 0
- 0
monkeystore/__init__.py View File


+ 19
- 0
monkeystore/config.py View File

@@ -0,0 +1,19 @@
DEBUG = 1

PASSWORD_DIRECTORY = '/tmp/monkeystore'

KEYS = {
'zebe' : '6FF9435F '
}

acl_windows = ('zebe', )
acl_bereitschaft = ('zebe',)
acl_linux = ('zebe',)
acl_network = ('zebe', )

CATEGORY_USERS = {
'windows' : set( acl_windows + acl_bereitschaft ),
'linux' : set( acl_linux + acl_bereitschaft ),
'network' : set( acl_network + acl_bereitschaft )
}


+ 78
- 0
monkeystore/web.py View File

@@ -0,0 +1,78 @@
# -*- 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 flask
import string
from flaskext.xmlrpc import XMLRPCHandler, Fault
from pwgen import pwgen

app = flask.Flask(__name__)
__all__ = ["app"]

app.config.from_object('monkeystore.config')

api = XMLRPCHandler('api')
api.connect(app, '/')


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

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

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

@api.register
def search_password(search_string):
pass

@api.register
def list_categories():
pass

@api.register
def list_servers():
pass

@api.register
def generate_password(length=8):
if length < 8:
raise Fault('Minimum password length: 8')
return pwgen(length, num_pw=1,
numerals=True, capitalize=True,
symbols=True, allowed_symbols=string.punctuation)



BIN
pwgen/.__init__.py.swp View File


+ 88
- 0
pwgen/__init__.py View File

@@ -0,0 +1,88 @@
#!/usr/bin/env python
"""

Copyright (C) 2011 Vince Spicer <vinces1979@gmail.com>
Copyright (C) 2012 Bernd Zeimetz <b.zeimetz@conova.com

As long as you retain this notice you can do whatever you want with this stuff.
If we meet some day, and you think this stuff is worth it, you can buy me a
beer in return Regina, SK Canada

"""

import string
import re

from random import SystemRandom
choice = SystemRandom().choice
randint = SystemRandom().randint

LowercaseLetters = string.lowercase
UpperCase = string.uppercase
Digits = string.digits
Symbols = string.punctuation

HasCaps = re.compile("[A-Z]")
HasNumerals = re.compile("[0-9]")
HasSymbols = re.compile(r"[%s]" % re.escape(Symbols))
HasAmbiguous = re.compile("[B8G6I1l|0OQDS5Z2]")

def replaceRandomChar(letter, word, pos=None):
if not pos:
pos = randint(0, len(word))
word = list(word)
word[pos] = letter
return "".join(word)

def pwgen(pw_length=20, num_pw=1, no_numerals=False, no_capitalize=False, capitalize=False,
numerals=False, no_symbols=False, symbols=False, allowed_symbols=None,
no_ambiguous=False):
"""Generate a random password.

@param pw_length: The length of the password to generate [default: 20]
@param num_pw: The number of passwords to generate [default: 1]
@param no_numerals: Don't include numbers in the passwords [default: False]
@param numerals: Enforce at least one number to be in the password [default: False]
@param no_capitalize: Don't include capital letters in the password [default: False]
@param capitalize: Enforce at least one capital letter to be in the password [default: False]
@param no_symbols: Don't include symbols in the password [default: False]
@param symbols: Enforce at least one symbol to be in the password [default: False]
@param allowed_symbols: a string containing allowed symbols [default: string.punctuation]
@param no_ambigous: Don't include ambigous characters [default: False ]

"""

global Symbols, HasSymbols
letters = LowercaseLetters
if not no_capitalize:
letters += UpperCase
if not no_numerals:
letters += Digits
if not no_symbols:
if allowed_symbols is not None:
Symbols = allowed_symbols
HasSymbols = re.compile(r"[%s]" % re.escape(Symbols))
letters += Symbols

passwds = []
while len(passwds) < int(num_pw):
passwd = ''
while len(passwd) < pw_length:
passwd = passwd + "".join(choice(letters) for x in range(pw_length - len(passwd)))
pwarray = list(set(passwd))
SystemRandom().shuffle(pwarray)
passwd = ''.join(pwarray)
if capitalize and not HasCaps.search(passwd):
passwd = replaceRandomChar(choice(UpperCase), passwd)
if numerals and not HasNumerals.search(passwd):
passwd = replaceRandomChar(choice(Digits), passwd)
if symbols and not HasSymbols.search(passwd):
passwd = replaceRandomChar(choice(Symbols), passwd)
if no_ambiguous and HasAmbiguous.search(passwd):
continue
passwds.append(passwd)

if len(passwds) == 1:
return passwds[0]

return passwds

+ 36
- 0
testclient.py View File

@@ -0,0 +1,36 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
__author__ = "Bernd Zeimetz"
__contact__ = "bzed@debian.org"
__license__ = """
Copyright (C) 2010-2012 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 xmlrpclib
server = xmlrpclib.ServerProxy('http://localhost:5000/')
for i in range(5):
print server.generate_password(40)

Loading…
Cancel
Save