Initial commit but hopefully working application.
This commit is contained in:
commit
107be1af8c
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
*.pyc
|
||||||
|
env
|
||||||
|
.project
|
||||||
|
.pydevproject
|
||||||
|
*/__pycache__/
|
||||||
|
.idea/
|
||||||
|
*.egg-info/
|
4
CHANGES.txt
Normal file
4
CHANGES.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
0.0
|
||||||
|
---
|
||||||
|
|
||||||
|
- Initial version
|
2
MANIFEST.in
Normal file
2
MANIFEST.in
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
include *.txt *.ini *.cfg *.rst
|
||||||
|
recursive-include bifrost *.txt
|
10
README.txt
Normal file
10
README.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
This application provides a dyndns server style updater for using webfaction domain as dyndns.
|
||||||
|
the url to be used in updating is as follows
|
||||||
|
http://[USERNAME]:[PASSWORD]@tanshu.com/update?hostname=[DOMAIN]
|
||||||
|
or with custom ip address
|
||||||
|
http://[USERNAME]:[PASSWORD]@tanshu.com/update?hostname=[DOMAIN]&ip=[IP]
|
||||||
|
The users are maintained in the .htpasswd file in the production.ini directory.
|
||||||
|
The way to add/update users is "htpasswd .htpasswd [USERNAME]
|
||||||
|
the domains should be in the "dns_info" file in the format "domain:ipaddress"
|
||||||
|
The location of both the files can be changed in the ini file.
|
||||||
|
Also, the webfaction username and password needs to be configured in the ini file.
|
39
bifrost/__init__.py
Normal file
39
bifrost/__init__.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
from crypt import crypt
|
||||||
|
import os
|
||||||
|
from pyramid.authentication import BasicAuthAuthenticationPolicy
|
||||||
|
from pyramid.authorization import ACLAuthorizationPolicy
|
||||||
|
from pyramid.config import Configurator
|
||||||
|
from pyramid.security import Authenticated, Allow, Everyone
|
||||||
|
|
||||||
|
|
||||||
|
def main(global_config, **settings):
|
||||||
|
config = Configurator(settings=settings, authentication_policy=BasicAuthAuthenticationPolicy(htpasswd),
|
||||||
|
authorization_policy=ACLAuthorizationPolicy(), root_factory='bifrost.RootFactory')
|
||||||
|
config.add_route('update', '/update')
|
||||||
|
config.scan()
|
||||||
|
return config.make_wsgi_app()
|
||||||
|
|
||||||
|
|
||||||
|
def htpasswd(username, password, request):
|
||||||
|
settings = request.registry.settings
|
||||||
|
file = settings['biforst.auth']
|
||||||
|
if not os.path.isfile(file):
|
||||||
|
return None
|
||||||
|
users = {}
|
||||||
|
with open(file) as f:
|
||||||
|
for line in f:
|
||||||
|
login, pwd = line.split(':')
|
||||||
|
users[login] = pwd.rstrip('\n')
|
||||||
|
if username in users and crypt(password, users[username]) == users[username]:
|
||||||
|
return [Authenticated]
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class RootFactory(object):
|
||||||
|
@property
|
||||||
|
def __acl__(self):
|
||||||
|
acl = [(Allow, Authenticated, Authenticated)]
|
||||||
|
return acl
|
||||||
|
|
||||||
|
def __init__(self, request):
|
||||||
|
pass
|
58
bifrost/views.py
Normal file
58
bifrost/views.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import xmlrpc.client
|
||||||
|
|
||||||
|
from pyramid.httpexceptions import HTTPUnauthorized
|
||||||
|
from pyramid.security import forget, Authenticated
|
||||||
|
from pyramid.view import view_config, forbidden_view_config
|
||||||
|
|
||||||
|
log = logging.getLogger('bifrost.updater')
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(route_name='update', request_param='domain', renderer='json', permission=Authenticated)
|
||||||
|
def update_view(request):
|
||||||
|
file = request.registry.settings['biforst.file']
|
||||||
|
username = request.registry.settings['webfaction.username']
|
||||||
|
password = request.registry.settings['webfaction.password']
|
||||||
|
current_ip = request.GET.get('ip',None)
|
||||||
|
if current_ip is None:
|
||||||
|
current_ip = request.remote_addr
|
||||||
|
domain = request.GET['domain']
|
||||||
|
db = load(file)
|
||||||
|
|
||||||
|
if domain in db and db[domain] != current_ip:
|
||||||
|
server = xmlrpc.client.ServerProxy('https://api.webfaction.com/')
|
||||||
|
session_id, account = server.login(username, password)
|
||||||
|
server.delete_dns_override(session_id, domain)
|
||||||
|
server.create_dns_override(session_id, domain, current_ip, '', '', '', '')
|
||||||
|
db[domain] = current_ip
|
||||||
|
update(file, db)
|
||||||
|
|
||||||
|
log.info('{0} updated to {1} at {3}'.format(domain, current_ip))
|
||||||
|
else:
|
||||||
|
log.info('{0} not updated'.format(domain))
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def load(file):
|
||||||
|
if not os.path.isfile(file):
|
||||||
|
return {}
|
||||||
|
|
||||||
|
current = {}
|
||||||
|
with open(file) as f:
|
||||||
|
for line in f:
|
||||||
|
domain, ip = line.split(':')
|
||||||
|
current[domain] = ip.rstrip('\n')
|
||||||
|
return current
|
||||||
|
|
||||||
|
|
||||||
|
def update(file, db):
|
||||||
|
with open(file, 'w') as f:
|
||||||
|
f.writelines(['{0}:{1}'.format(key, value) for key, value in db.items()])
|
||||||
|
|
||||||
|
|
||||||
|
@forbidden_view_config()
|
||||||
|
def basic_challenge(request):
|
||||||
|
response = HTTPUnauthorized()
|
||||||
|
response.headers.update(forget(request))
|
||||||
|
return response
|
63
development.ini
Normal file
63
development.ini
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
###
|
||||||
|
# app configuration
|
||||||
|
# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html
|
||||||
|
###
|
||||||
|
|
||||||
|
[app:main]
|
||||||
|
use = egg:bifrost
|
||||||
|
|
||||||
|
pyramid.reload_templates = false
|
||||||
|
pyramid.debug_authorization = false
|
||||||
|
pyramid.debug_notfound = false
|
||||||
|
pyramid.debug_routematch = false
|
||||||
|
pyramid.default_locale_name = en
|
||||||
|
biforst.file = %(here)s/dns_info
|
||||||
|
biforst.auth = %(here)s/.htpasswd
|
||||||
|
webfaction.username =
|
||||||
|
webfaction.password =
|
||||||
|
|
||||||
|
###
|
||||||
|
# wsgi server configuration
|
||||||
|
###
|
||||||
|
|
||||||
|
[server:main]
|
||||||
|
use = egg:waitress#main
|
||||||
|
host = 0.0.0.0
|
||||||
|
port = 6543
|
||||||
|
|
||||||
|
###
|
||||||
|
# logging configuration
|
||||||
|
# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html
|
||||||
|
###
|
||||||
|
|
||||||
|
[loggers]
|
||||||
|
keys = root, bifrost, bifrost.updater
|
||||||
|
|
||||||
|
[handlers]
|
||||||
|
keys = console
|
||||||
|
|
||||||
|
[formatters]
|
||||||
|
keys = generic
|
||||||
|
|
||||||
|
[logger_root]
|
||||||
|
level = INFO
|
||||||
|
handlers = console
|
||||||
|
|
||||||
|
[logger_bifrost]
|
||||||
|
level = DEBUG
|
||||||
|
handlers =
|
||||||
|
qualname = bifrost
|
||||||
|
|
||||||
|
[logger_bifrost.updater]
|
||||||
|
level = INFO
|
||||||
|
handlers =
|
||||||
|
qualname = bifrost.updater
|
||||||
|
|
||||||
|
[handler_console]
|
||||||
|
class = StreamHandler
|
||||||
|
args = (sys.stderr,)
|
||||||
|
level = NOTSET
|
||||||
|
formatter = generic
|
||||||
|
|
||||||
|
[formatter_generic]
|
||||||
|
format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
|
63
production.ini
Normal file
63
production.ini
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
###
|
||||||
|
# app configuration
|
||||||
|
# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html
|
||||||
|
###
|
||||||
|
|
||||||
|
[app:main]
|
||||||
|
use = egg:bifrost
|
||||||
|
|
||||||
|
pyramid.reload_templates = false
|
||||||
|
pyramid.debug_authorization = false
|
||||||
|
pyramid.debug_notfound = false
|
||||||
|
pyramid.debug_routematch = false
|
||||||
|
pyramid.default_locale_name = en
|
||||||
|
biforst.file = %(here)s/dns_info
|
||||||
|
biforst.auth = %(here)s/.htpasswd
|
||||||
|
webfaction.username =
|
||||||
|
webfaction.password =
|
||||||
|
|
||||||
|
###
|
||||||
|
# wsgi server configuration
|
||||||
|
###
|
||||||
|
|
||||||
|
[server:main]
|
||||||
|
use = egg:waitress#main
|
||||||
|
host = 0.0.0.0
|
||||||
|
port = 6543
|
||||||
|
|
||||||
|
###
|
||||||
|
# logging configuration
|
||||||
|
# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html
|
||||||
|
###
|
||||||
|
|
||||||
|
[loggers]
|
||||||
|
keys = root, bifrost, bifrost.updater
|
||||||
|
|
||||||
|
[handlers]
|
||||||
|
keys = console
|
||||||
|
|
||||||
|
[formatters]
|
||||||
|
keys = generic
|
||||||
|
|
||||||
|
[logger_root]
|
||||||
|
level = WARN
|
||||||
|
handlers = console
|
||||||
|
|
||||||
|
[logger_bifrost]
|
||||||
|
level = WARN
|
||||||
|
handlers =
|
||||||
|
qualname = bifrost
|
||||||
|
|
||||||
|
[logger_bifrost.updater]
|
||||||
|
level = INFO
|
||||||
|
handlers =
|
||||||
|
qualname = bifrost.updater
|
||||||
|
|
||||||
|
[handler_console]
|
||||||
|
class = StreamHandler
|
||||||
|
args = (sys.stderr,)
|
||||||
|
level = NOTSET
|
||||||
|
formatter = generic
|
||||||
|
|
||||||
|
[formatter_generic]
|
||||||
|
format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
|
27
setup.cfg
Normal file
27
setup.cfg
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
[nosetests]
|
||||||
|
match = ^test
|
||||||
|
nocapture = 1
|
||||||
|
cover-package = bifrost
|
||||||
|
with-coverage = 1
|
||||||
|
cover-erase = 1
|
||||||
|
|
||||||
|
[compile_catalog]
|
||||||
|
directory = bifrost/locale
|
||||||
|
domain = bifrost
|
||||||
|
statistics = true
|
||||||
|
|
||||||
|
[extract_messages]
|
||||||
|
add_comments = TRANSLATORS:
|
||||||
|
output_file = bifrost/locale/bifrost.pot
|
||||||
|
width = 80
|
||||||
|
|
||||||
|
[init_catalog]
|
||||||
|
domain = bifrost
|
||||||
|
input_file = bifrost/locale/bifrost.pot
|
||||||
|
output_dir = bifrost/locale
|
||||||
|
|
||||||
|
[update_catalog]
|
||||||
|
domain = bifrost
|
||||||
|
input_file = bifrost/locale/bifrost.pot
|
||||||
|
output_dir = bifrost/locale
|
||||||
|
previous = true
|
38
setup.py
Normal file
38
setup.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
here = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
README = open(os.path.join(here, 'README.txt')).read()
|
||||||
|
CHANGES = open(os.path.join(here, 'CHANGES.txt')).read()
|
||||||
|
|
||||||
|
requires = [
|
||||||
|
'pyramid',
|
||||||
|
'waitress',
|
||||||
|
]
|
||||||
|
|
||||||
|
setup(name='bifrost',
|
||||||
|
version='0.0',
|
||||||
|
description='bifrost',
|
||||||
|
long_description=README + '\n\n' + CHANGES,
|
||||||
|
classifiers=[
|
||||||
|
"Programming Language :: Python",
|
||||||
|
"Framework :: Pyramid",
|
||||||
|
"Topic :: Internet :: WWW/HTTP",
|
||||||
|
"Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
|
||||||
|
],
|
||||||
|
author='',
|
||||||
|
author_email='',
|
||||||
|
url='',
|
||||||
|
keywords='web pyramid pylons',
|
||||||
|
packages=find_packages(),
|
||||||
|
include_package_data=True,
|
||||||
|
zip_safe=False,
|
||||||
|
install_requires=requires,
|
||||||
|
tests_require=requires,
|
||||||
|
test_suite="bifrost",
|
||||||
|
entry_points="""\
|
||||||
|
[paste.app_factory]
|
||||||
|
main = bifrost:main
|
||||||
|
""",
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user