├── utils ├── __init__.py ├── billing.py └── encryption.py ├── vault ├── __init__.py ├── migrations │ ├── __init__.py │ ├── 0001_initial.py │ ├── 0004_auto__add_field_credentialgroup_name__add_field_credentialgroup_descri.py │ ├── 0003_auto__del_field_credential_group.py │ ├── 0005_auto__add_field_credential_uuid__add_field_credentialgroup_uuid.py │ ├── 0006_auto__del_field_credential_key__add_field_credential_username__add_fie.py │ └── 0002_auto__add_credentialgroup__del_field_credential_owner__add_field_crede.py ├── tests.py ├── admin.py ├── forms.py ├── urls.py ├── models.py ├── templates │ └── vault │ │ ├── index.html │ │ └── group.html └── views.py ├── accounts ├── __init__.py ├── migrations │ ├── __init__.py │ ├── 0002_auto__add_field_userprofile_is_pro.py │ ├── 0001_initial.py │ ├── 0003_auto__add_field_userprofile_pro_join_date.py │ ├── 0004_auto__add_field_userprofile_customer_id.py │ ├── 0005_auto__chg_field_userprofile_customer_id.py │ └── 0006_auto__add_field_userprofile_activation_code.py ├── templates │ └── accounts │ │ ├── details.html │ │ ├── login.html │ │ ├── signup.html │ │ └── activate.html ├── tests.py ├── urls.py ├── forms.py ├── models.py └── views.py ├── locksmith ├── __init__.py ├── middleware │ ├── __init__.py │ └── threadlocal.py ├── templatetags │ ├── __init__.py │ └── locksmith.py ├── urls.py ├── admin.py ├── wsgi.py ├── context_processors.py ├── views.py ├── api_v1.py └── settings.py ├── static ├── css │ ├── bg.css │ ├── fonts │ │ ├── entypo.eot │ │ ├── entypo.ttf │ │ ├── entypo.woff │ │ ├── entypo-social.eot │ │ ├── entypo-social.ttf │ │ ├── entypo-social.woff │ │ ├── entypo.svg │ │ └── entypo-social.svg │ ├── fonts.css │ ├── app.css │ └── bootstrap-responsive.min.css ├── img │ ├── bg.png │ ├── lock.png │ ├── cards.png │ ├── favicon.ico │ ├── group.png │ ├── glyphicons-halflings.png │ └── glyphicons-halflings-white.png ├── libs │ └── zc.swf └── js │ ├── app.js │ ├── zc.min.js │ └── holder.js ├── .gitignore ├── templates ├── 404.html ├── index.html ├── about.html └── base.html ├── wsgi.py ├── manage.py ├── requirements.txt ├── license └── readme.md /utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vault/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /accounts/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /locksmith/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vault/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /accounts/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /locksmith/middleware/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /locksmith/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/css/bg.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: url('/static/img/bg.png'); 3 | } 4 | -------------------------------------------------------------------------------- /static/img/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehazlett/locksmith/HEAD/static/img/bg.png -------------------------------------------------------------------------------- /static/img/lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehazlett/locksmith/HEAD/static/img/lock.png -------------------------------------------------------------------------------- /static/libs/zc.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehazlett/locksmith/HEAD/static/libs/zc.swf -------------------------------------------------------------------------------- /static/img/cards.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehazlett/locksmith/HEAD/static/img/cards.png -------------------------------------------------------------------------------- /static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehazlett/locksmith/HEAD/static/img/favicon.ico -------------------------------------------------------------------------------- /static/img/group.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehazlett/locksmith/HEAD/static/img/group.png -------------------------------------------------------------------------------- /static/css/fonts/entypo.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehazlett/locksmith/HEAD/static/css/fonts/entypo.eot -------------------------------------------------------------------------------- /static/css/fonts/entypo.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehazlett/locksmith/HEAD/static/css/fonts/entypo.ttf -------------------------------------------------------------------------------- /static/css/fonts/entypo.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehazlett/locksmith/HEAD/static/css/fonts/entypo.woff -------------------------------------------------------------------------------- /static/css/fonts/entypo-social.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehazlett/locksmith/HEAD/static/css/fonts/entypo-social.eot -------------------------------------------------------------------------------- /static/css/fonts/entypo-social.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehazlett/locksmith/HEAD/static/css/fonts/entypo-social.ttf -------------------------------------------------------------------------------- /static/css/fonts/entypo-social.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehazlett/locksmith/HEAD/static/css/fonts/entypo-social.woff -------------------------------------------------------------------------------- /static/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehazlett/locksmith/HEAD/static/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.swp 3 | *.swo 4 | *.un* 5 | *.db 6 | local_settings.py 7 | *.log 8 | newrelic.ini 9 | *DS_Store* 10 | -------------------------------------------------------------------------------- /static/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehazlett/locksmith/HEAD/static/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /templates/404.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load i18n %} 3 | 4 | {% block main_content %} 5 |
[\W,\w]+)/$', 'confirm', name='accounts.confirm'),
22 | url(r'^activate/$', 'activate', name='accounts.activate'),
23 | url(r'^hook/$', 'hook', name='accounts.hook'),
24 | )
25 |
--------------------------------------------------------------------------------
/locksmith/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for locksmith project.
3 |
4 | This module contains the WSGI application used by Django's development server
5 | and any production WSGI deployments. It should expose a module-level variable
6 | named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
7 | this application via the ``WSGI_APPLICATION`` setting.
8 |
9 | Usually you will have the standard Django WSGI application here, but it also
10 | might make sense to replace the whole Django WSGI application with a custom one
11 | that later delegates to the Django one. For example, you could introduce WSGI
12 | middleware here, or combine a Django application with an application of another
13 | framework.
14 |
15 | """
16 | import os
17 |
18 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "locksmith.settings")
19 |
20 | # This application object is used by any WSGI server configured to use this
21 | # file. This includes Django's development server, if the WSGI_APPLICATION
22 | # setting points here.
23 | from django.core.wsgi import get_wsgi_application
24 | application = get_wsgi_application()
25 |
26 | # Apply WSGI middleware here.
27 | # from helloworld.wsgi import HelloWorldApplication
28 | # application = HelloWorldApplication(application)
29 |
--------------------------------------------------------------------------------
/templates/index.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% load i18n %}
3 |
4 | {% block main_content %}
5 |
6 |
7 | {% trans 'Open Source Password Management' %}
8 |
9 | {% blocktrans %}Locksmith encrypts and stores your accounts in one place. No longer do you need to have applications installed or files to keep track. Access them anywhere with Locksmith.{% endblocktrans %}
10 |
11 | {% if SIGNUP_ENABLED %}
12 |
13 | {% trans "Signup" %}
14 |
15 | {% endif %}
16 |
17 |
28 |
29 | {% endblock %}
30 |
--------------------------------------------------------------------------------
/locksmith/context_processors.py:
--------------------------------------------------------------------------------
1 | # Copyright 2013 Evan Hazlett and contributors
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | from django.conf import settings
15 | from django.core.cache import cache
16 | from utils.encryption import get_user_encryption_key
17 |
18 | def app_info(request):
19 | return {
20 | 'APP_NAME': getattr(settings, 'APP_NAME'),
21 | 'APP_REVISION': getattr(settings, 'APP_REVISION'),
22 | }
23 |
24 | def google_analytics_code(request):
25 | return { 'GOOGLE_ANALYTICS_CODE': getattr(settings, 'GOOGLE_ANALYTICS_CODE')}
26 |
27 | def stripe_info(request):
28 | return {
29 | 'STRIPE_API_KEY': getattr(settings, 'STRIPE_API_KEY'),
30 | 'STRIPE_PUBLISHABLE_KEY': getattr(settings, 'STRIPE_PUBLISHABLE_KEY'),
31 | }
32 |
33 | def intercom_app_id(request):
34 | return { 'INTERCOM_APP_ID': getattr(settings, 'INTERCOM_APP_ID')}
35 |
36 | def encryption_key(request):
37 | u = request.user
38 | key = get_user_encryption_key(u.username)
39 | return { 'ENCRYPTION_KEY': key }
40 |
41 | def signup_enabled(request):
42 | return { 'SIGNUP_ENABLED': getattr(settings, 'SIGNUP_ENABLED')}
43 |
--------------------------------------------------------------------------------
/accounts/templates/accounts/login.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 | {% load i18n %}
3 |
4 | {% block main_content %}
5 |
6 |
19 |
24 |
25 |
39 | {% endblock %}
40 |
--------------------------------------------------------------------------------
/accounts/forms.py:
--------------------------------------------------------------------------------
1 | # Copyright 2013 Evan Hazlett and contributors
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | from django import forms
15 | from django.contrib.auth.models import User
16 | from accounts.models import UserProfile
17 |
18 | class AccountForm(forms.ModelForm):
19 | # override the default fields to force them to be required
20 | # (the django User model doesn't require them)
21 | def __init__(self, *args, **kwargs):
22 | super(AccountForm, self).__init__(*args, **kwargs)
23 | self.fields['first_name'].required = True
24 | self.fields['last_name'].required = True
25 | self.fields['email'].required = True
26 | class Meta:
27 | model = User
28 | fields = ('first_name', 'last_name', 'email')
29 |
30 | class UserProfileForm(forms.ModelForm):
31 | # override the default fields to force them to be required
32 | # (the django User model doesn't require them)
33 | def __init__(self, *args, **kwargs):
34 | super(UserProfileForm, self).__init__(*args, **kwargs)
35 | self.fields['encryption_key_timeout'].required = True
36 | class Meta:
37 | model = UserProfile
38 | fields = ('encryption_key_timeout',)
39 |
--------------------------------------------------------------------------------
/accounts/models.py:
--------------------------------------------------------------------------------
1 | # Copyright 2013 Evan Hazlett and contributors
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | from django.db import models
15 | from django.contrib.auth.models import User
16 | from django.db.models.signals import post_save
17 | from django.dispatch import receiver
18 | from uuid import uuid4
19 | from tastypie.models import create_api_key
20 |
21 | class UserProfile(models.Model):
22 | """
23 | User profile
24 |
25 | """
26 | user = models.ForeignKey(User, unique=True)
27 | encryption_key_timeout = models.IntegerField(default=3600, blank=True,
28 | null=True)
29 | is_pro = models.NullBooleanField(default=False, null=True, blank=True)
30 | pro_join_date = models.DateTimeField(null=True, blank=True)
31 | customer_id = models.CharField(max_length=64, null=True, blank=True)
32 | activation_code = models.CharField(max_length=64, null=True, blank=True)
33 |
34 | def __unicode__(self):
35 | return self.user.username
36 |
37 | # create user profile upon save
38 | @receiver(post_save, sender=User)
39 | def create_profile(sender, instance, created, **kwargs):
40 | if created:
41 | profile, created = UserProfile.objects.get_or_create(user=instance)
42 |
43 | models.signals.post_save.connect(create_api_key, sender=User)
44 |
45 |
--------------------------------------------------------------------------------
/accounts/templates/accounts/signup.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 | {% load i18n %}
3 |
4 | {% block main_content %}
5 |
6 |
44 |
45 |
53 | {% endblock %}
54 |
--------------------------------------------------------------------------------
/locksmith/views.py:
--------------------------------------------------------------------------------
1 | # Copyright 2013 Evan Hazlett and contributors
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | from django.shortcuts import redirect, render_to_response
15 | from django.template import RequestContext
16 | from django.http import HttpResponse
17 | from django.contrib.auth import authenticate, login as login_user
18 | from django.core.urlresolvers import reverse
19 | from locksmith.api_v1 import UserResource
20 | from social_auth.decorators import dsa_view
21 | import datetime
22 |
23 | def index(request):
24 | ctx = {}
25 | if request.user.is_authenticated():
26 | return redirect(reverse('vault.views.index'))
27 | else:
28 | return render_to_response('index.html', ctx,
29 | context_instance=RequestContext(request))
30 |
31 | def about(request):
32 | ctx = {}
33 | return render_to_response('about.html', ctx,
34 | context_instance=RequestContext(request))
35 |
36 | @dsa_view()
37 | def register_by_access_token(request, backend, *args, **kwargs):
38 | access_token = request.GET.get('access_token')
39 | user = backend.do_auth(access_token)
40 | code = 200
41 | if user and user.is_active:
42 | login_user(request, user)
43 | ur = UserResource()
44 | user_data = ur.obj_get(request, username=user.username)
45 | bundle = ur.build_bundle(obj=user_data, request=request)
46 | data = ur.serialize(None, ur.full_dehydrate(bundle),
47 | 'application/json')
48 | else:
49 | data = json.dumps({'error': 'Access denied'})
50 | code = 403
51 | return HttpResponse(data, status=code,
52 | content_type='application/json')
53 |
54 |
--------------------------------------------------------------------------------
/templates/about.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% load i18n %}
3 |
4 | {% block main_content %}
5 |
6 |
7 | {% trans "What is Locksmith?" %}
8 | {% blocktrans %}Locksmith is a password storage service. We all have many accounts across
9 | many services. We wanted a simple, secure way to store the account credentials
10 | with an easy way to retrieve them.{% endblocktrans %}
11 |
12 |
13 | {% trans "How secure is it?" %}
14 | {% blocktrans %}We take security at the highest priority. Locksmith takes a slightly
15 | different approach from that of a typical web service. Most services will
16 | encrypt or hash your password so in the event that it is compromised, it is not
17 | easily readable. However, they will typically use a single, unique key to
18 | encrypt all data. With Locksmith, we do the same type of encryption however,
19 | every single user also has a unique "key" that is used as an additional
20 | randomizer (salt) during encryption. What this means is that even if your data
21 | is compromised, which we take great measure to prevent, every single user's
22 | data is completely unique making it even more difficult to decrypt.{% endblocktrans %}
23 |
24 |
25 | {% trans "How much does it cost?" %}
26 | {% blocktrans %}Basic accounts are free. Currently, there is no difference between basic and pro accounts.
27 | In the future we will offer certain pro features but the current feature
28 | set will always be free.{% endblocktrans %}
29 |
30 | {% trans "If you want to support development now, pro accounts are $12 per year." %}
31 |
32 | {% trans "Any mobile apps?" %}
33 | {% trans "In the works. Stay tuned!" %}
34 |
35 | {% trans "Why Open Source?" %}
36 | {% blocktrans %}First of all, we love open source. We try to commit as much as we
37 | can back to the open source community. Second, we believe that making
38 | Locksmith open source allows for community review and improvement. We feel
39 | this is especially important since our application contains your private data.{% endblocktrans %}
40 |
41 |
42 | {% trans 'View Source' %}
43 |
44 |
45 |
46 | {% endblock %}
47 |
--------------------------------------------------------------------------------
/utils/billing.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # Copyright 2012 Evan Hazlett
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | import stripe
16 | from django.conf import settings
17 | from django.utils.translation import ugettext as _
18 |
19 | # set stripe api key
20 | stripe.api_key = getattr(settings, 'STRIPE_API_KEY')
21 |
22 | def create_customer(token=None, plan=None, email=None):
23 | customer = stripe.Customer.create(
24 | card=token,
25 | plan=plan,
26 | email=email
27 | )
28 | return customer
29 |
30 | def charge(amount=None, currency='usd', card_number=None,
31 | card_exp_month=None, card_exp_year=None, card_cvc=None, card_name=None,
32 | description='Locksmith Payment'):
33 | """
34 | Charges specified credit card for account
35 |
36 | :param amount: Amount in dollars
37 | :param currency: Currency (default: usd)
38 | :param card_number: Credit card number
39 | :param card_exp_month: Credit card expiration month (two digit integer)
40 | :param card_exp_year: Credit card expiration year (two or four digit integer)
41 | :param card_cvc: Credit card CVC
42 | :param card_name: Credit cardholder name
43 | :param description: Charge description (default: Locksmith Payment)
44 |
45 | """
46 | # convert amount to cents
47 | amount = int(amount * 100)
48 | card_info = {
49 | 'number': card_number.replace('-', ''),
50 | 'exp_month': card_exp_month,
51 | 'exp_year': card_exp_year,
52 | 'cvc': card_cvc,
53 | 'name': card_name,
54 | }
55 | data = {}
56 | try:
57 | charge = stripe.Charge.create(amount=amount, currency=currency, card=card_info,
58 | description=description)
59 | if charge.paid:
60 | data['status'] = True
61 | data['message'] = _('Thanks!')
62 | else:
63 | data['status'] = False
64 | data['message'] = charge.failure_message
65 | data['created'] = charge.created
66 | except Exception, e:
67 | data['status'] = False
68 | data['message'] = e
69 | return data
70 |
--------------------------------------------------------------------------------
/vault/models.py:
--------------------------------------------------------------------------------
1 | # Copyright 2013 Evan Hazlett and contributors
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | from django.db import models
15 | from django.contrib.auth.models import User
16 | from django.conf import settings
17 | from django.core.cache import cache
18 | from uuid import uuid4
19 | from utils.encryption import encrypt, get_user_encryption_key
20 | from locksmith.middleware import threadlocal
21 |
22 | def generate_uuid():
23 | return str(uuid4())
24 |
25 | class CredentialGroup(models.Model):
26 | uuid = models.CharField(max_length=36, blank=True, null=True,
27 | default=generate_uuid)
28 | name = models.CharField(max_length=64, blank=True, null=True)
29 | description = models.TextField(blank=True, null=True)
30 | owner = models.ForeignKey(User, blank=True, null=True,
31 | related_name='credential_group_owner')
32 | members = models.ManyToManyField(User, blank=True, null=True,
33 | related_name='credential_group_members')
34 |
35 | def __unicode__(self):
36 | return '{0}: {1}'.format(self.owner.username, self.name)
37 |
38 | def get_credentials(self):
39 | return Credential.objects.filter(groups__in=[self])
40 |
41 | class Credential(models.Model):
42 | uuid = models.CharField(max_length=36, blank=True, null=True,
43 | default=generate_uuid)
44 | name = models.CharField(max_length=64, blank=True, null=True)
45 | description = models.TextField(blank=True, null=True)
46 | url = models.URLField(blank=True, null=True)
47 | username = models.CharField(max_length=96, blank=True, null=True)
48 | password = models.TextField(blank=True, null=True)
49 | groups = models.ManyToManyField(CredentialGroup, blank=True, null=True)
50 |
51 | def __unicode__(self):
52 | return '{0}: {1}'.format(','.join([x.name for x in self.groups.all()]),
53 | self.name)
54 |
55 | def save(self, *args, **kwargs):
56 | user = threadlocal.get_current_user()
57 | key = get_user_encryption_key(user.username) or kwargs.get('key')
58 | # if no key throw error
59 | if not key:
60 | raise StandardError("If calling save from outside of a request, " \
61 | "you must specify 'key' as a kwarg")
62 | self.password = encrypt(self.password, key)
63 | super(Credential, self).save(*args, **kwargs)
64 |
--------------------------------------------------------------------------------
/vault/templates/vault/index.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 | {% load i18n %}
3 | {% load bootstrap_tags %}
4 |
5 | {% block main_content %}
6 |
31 |
32 |
35 |
36 | {% if credential_groups.all %}
37 |
38 | {% for group in credential_groups.all %}
39 | -
40 |
41 | 📄
42 |
43 |
{{group.name|truncatechars:"15"}}
44 |
45 |
46 |
47 | {% endfor %}
48 |
49 | {% else %}
50 | {% trans 'No groups' %}
51 | {% endif %}
52 |
53 |
77 | {% endblock %}
78 |
--------------------------------------------------------------------------------
/utils/encryption.py:
--------------------------------------------------------------------------------
1 | # Copyright 2013 Evan Hazlett and contributors
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | import hashlib
15 | from Crypto.Cipher import AES
16 | from django.utils.translation import ugettext as _
17 | import base64
18 | from django.conf import settings
19 | from django.core.cache import cache
20 | from django.contrib.auth.models import User
21 | import string
22 | import random
23 |
24 | def generate_password(length=16):
25 | """
26 | Generates a new random password
27 |
28 | :param length: Length of password
29 |
30 | """
31 | return ''.join(random.sample(string.letters+string.digits, length))
32 |
33 | def set_user_encryption_key(username=None, key=None, ttl=None):
34 | """
35 | Sets the encryption key for the specified user
36 |
37 | """
38 | if not ttl:
39 | u = User.objects.get(username=username)
40 | ttl = u.get_profile().encryption_key_timeout
41 | cache.set(settings.CACHE_ENCRYPTION_KEY.format(username), key,
42 | timeout=ttl)
43 |
44 | def get_user_encryption_key(username=None):
45 | """
46 | Gets the encryption key for the specified user
47 |
48 | """
49 | return cache.get(settings.CACHE_ENCRYPTION_KEY.format(username))
50 |
51 | def clear_user_encryption_key(username=None):
52 | """
53 | Clears the encryption key for the specified user
54 |
55 | """
56 | cache.delete(settings.CACHE_ENCRYPTION_KEY.format(username))
57 |
58 | def hash_text(text):
59 | """
60 | Hashes text with app key
61 |
62 | :param text: Text to encrypt
63 |
64 | """
65 | h = hashlib.sha256()
66 | h.update(getattr(settings, 'SECRET_KEY'))
67 | h.update(text)
68 | return h.hexdigest()
69 |
70 | def _get_padded_key(key=None):
71 | if len(key) < 16:
72 | pad = 16 - len(key)
73 | k = key + ('^'*pad)
74 | else:
75 | k = key[:16]
76 | return k
77 |
78 | def encrypt(data=None, key=None):
79 | """
80 | Encrypts data
81 |
82 | :param data: Data to encrypt
83 | :param key: Encryption key (salt)
84 |
85 | """
86 | k = _get_padded_key(key)
87 | e = AES.new(k, AES.MODE_CFB, k[::-1])
88 | enc = e.encrypt(data)
89 | return base64.b64encode(enc)
90 |
91 | def decrypt(data=None, key=None):
92 | """
93 | Decrypts data
94 |
95 | :param data: Encrypted data to decrypt
96 | :param key: Encryption key (salt)
97 |
98 | """
99 | k = _get_padded_key(key)
100 | e = AES.new(k, AES.MODE_CFB, k[::-1])
101 | dec = e.decrypt(base64.b64decode(data))
102 | try:
103 | unicode(dec)
104 | except:
105 | dec = ''
106 | return dec
107 |
--------------------------------------------------------------------------------
/vault/views.py:
--------------------------------------------------------------------------------
1 | # Copyright 2013 Evan Hazlett and contributors
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | from django.shortcuts import redirect, render_to_response
15 | from django.template import RequestContext
16 | from django.contrib.auth.decorators import login_required
17 | from django.views.decorators.http import require_http_methods
18 | from django.http import HttpResponse, Http404
19 | from django.utils.translation import ugettext as _
20 | from django.db.models import Q
21 | from django.core.urlresolvers import reverse
22 | from django.core.cache import cache
23 | from django.conf import settings
24 | from django.contrib import messages
25 | from vault.models import CredentialGroup, Credential
26 | from vault.forms import CredentialGroupForm
27 | from utils.encryption import (set_user_encryption_key, clear_user_encryption_key,
28 | get_user_encryption_key, generate_password)
29 | try:
30 | import simplejson as json
31 | except ImportError:
32 | import json
33 |
34 | @login_required
35 | def index(request):
36 | ctx = {}
37 | try:
38 | groups = CredentialGroup.objects.filter(Q(owner=request.user) | \
39 | Q(members__in=[request.user])).order_by('name')
40 | ctx['credential_groups'] = groups
41 | except CredentialGroup.DoesNotExist:
42 | raise Http404()
43 | return render_to_response('vault/index.html', ctx,
44 | context_instance=RequestContext(request))
45 |
46 | @login_required
47 | def group(request, uuid=None):
48 | ctx = {}
49 | try:
50 | group = CredentialGroup.objects.get(Q(owner=request.user) | \
51 | Q(members__in=[request.user]), uuid=uuid)
52 | ctx['group'] = group
53 | except CredentialGroup.DoesNotExist:
54 | raise Http404()
55 | return render_to_response('vault/group.html', ctx,
56 | context_instance=RequestContext(request))
57 |
58 | @login_required
59 | @require_http_methods(["POST"])
60 | def set_key(request):
61 | nxt = request.GET.get('next', reverse('index'))
62 | key = request.POST.get('key')
63 | u = request.user
64 | set_user_encryption_key(u.username, key)
65 | return redirect(nxt)
66 |
67 | @login_required
68 | def lock_session(request):
69 | nxt = request.GET.get('next', reverse('index'))
70 | clear_user_encryption_key(request.user.username)
71 | return redirect(nxt)
72 |
73 | @login_required
74 | def random_password(request):
75 | return HttpResponse(generate_password())
76 |
77 | @login_required
78 | def check_session(request):
79 | key = get_user_encryption_key(request.user.username)
80 | if key:
81 | key = True
82 | else:
83 | key = False
84 | data = {
85 | 'status': key,
86 | }
87 | return HttpResponse(json.dumps(data), content_type='application/json')
88 |
--------------------------------------------------------------------------------
/accounts/templates/accounts/activate.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 | {% load i18n %}
3 |
4 | {% block main_content %}
5 |
6 | {% trans '$12 will be charged to your card anually. Thank you very much!' %}
7 |
70 |
71 |
72 |
73 |
97 | {% endblock %}
98 |
--------------------------------------------------------------------------------
/accounts/migrations/0002_auto__add_field_userprofile_is_pro.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import datetime
3 | from south.db import db
4 | from south.v2 import SchemaMigration
5 | from django.db import models
6 |
7 |
8 | class Migration(SchemaMigration):
9 |
10 | def forwards(self, orm):
11 | # Adding field 'UserProfile.is_pro'
12 | db.add_column('accounts_userprofile', 'is_pro',
13 | self.gf('django.db.models.fields.NullBooleanField')(default=False, null=True, blank=True),
14 | keep_default=False)
15 |
16 |
17 | def backwards(self, orm):
18 | # Deleting field 'UserProfile.is_pro'
19 | db.delete_column('accounts_userprofile', 'is_pro')
20 |
21 |
22 | models = {
23 | 'accounts.userprofile': {
24 | 'Meta': {'object_name': 'UserProfile'},
25 | 'encryption_key_timeout': ('django.db.models.fields.IntegerField', [], {'default': '3600', 'null': 'True', 'blank': 'True'}),
26 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
27 | 'is_pro': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}),
28 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'})
29 | },
30 | 'auth.group': {
31 | 'Meta': {'object_name': 'Group'},
32 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
33 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
34 | 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
35 | },
36 | 'auth.permission': {
37 | 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
38 | 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
39 | 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
40 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
41 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
42 | },
43 | 'auth.user': {
44 | 'Meta': {'object_name': 'User'},
45 | 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
46 | 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
47 | 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
48 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
49 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
50 | 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
51 | 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
52 | 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
53 | 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
54 | 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
55 | 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
56 | 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
57 | 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
58 | },
59 | 'contenttypes.contenttype': {
60 | 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
61 | 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
62 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
63 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
64 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
65 | }
66 | }
67 |
68 | complete_apps = ['accounts']
--------------------------------------------------------------------------------
/static/js/app.js:
--------------------------------------------------------------------------------
1 | function flash(text, status){
2 | var msg = $("");
3 | msg.addClass('alert-'+status);
4 | msg.append("x");
5 | msg.append(''+text+'
');
6 | $("#messages").append(msg);
7 | $("#messages").removeClass('hide');
8 | $(".alert").alert();
9 | $(".alert").delay(5000).fadeOut();
10 | }
11 |
12 | function redirect(url) {
13 | window.location.href = url;
14 | return false;
15 | }
16 |
17 | jQuery.fn.serializeObject = function() {
18 | var arrayData, objectData;
19 | arrayData = this.serializeArray();
20 | objectData = {};
21 | $.each(arrayData, function() {
22 | var value;
23 | if (this.value != null) {
24 | value = this.value;
25 | } else {
26 | value = '';
27 | }
28 | if (objectData[this.name] != null) {
29 | if (!objectData[this.name].push) {
30 | objectData[this.name] = [objectData[this.name]];
31 | }
32 | objectData[this.name].push(value);
33 | } else {
34 | objectData[this.name] = value;
35 | }
36 | });
37 | return objectData;
38 | };
39 |
40 | // TODO: redesign this
41 | // api
42 | var API_URL = '/api/v1/';
43 | function createCredentialGroup(options, callback) {
44 | var opt = options || {};
45 | var data = {
46 | name: opt.name
47 | }
48 | var cb = callback || function(){};
49 | $.ajax({
50 | url: API_URL + 'credentialgroups/',
51 | data: JSON.stringify(data),
52 | type: "POST",
53 | dataType: "application/json",
54 | contentType: "application/json",
55 | complete: function(xhr) {
56 | cb(xhr);
57 | }
58 | });
59 | }
60 | function editCredentialGroup(options, callback) {
61 | var data = options || {};
62 | var cb = callback || function(){};
63 | $.ajax({
64 | url: API_URL + 'credentialgroups/' + data.uuid,
65 | data: JSON.stringify(data),
66 | type: "PATCH",
67 | dataType: "application/json",
68 | contentType: "application/json",
69 | complete: function(xhr) {
70 | cb(xhr);
71 | }
72 | });
73 |
74 | }
75 | function deleteCredentialGroup(uuid, callback) {
76 | var cb = callback || function(){};
77 | $.ajax({
78 | url: API_URL + 'credentialgroups/' + uuid + '/',
79 | type: "DELETE",
80 | complete: function(xhr) {
81 | cb(xhr);
82 | }
83 | });
84 | }
85 | function createCredential(options, callback) {
86 | var opt = options || {};
87 | var data = {
88 | name: opt.name,
89 | description: opt.description,
90 | url: opt.url,
91 | username: opt.username,
92 | password: opt.password,
93 | groups: [API_URL + "credentialgroups/" + opt.groupUUID + "/"]
94 | }
95 | var cb = callback || function(){};
96 | $.ajax({
97 | url: API_URL + 'credentials/',
98 | data: JSON.stringify(data),
99 | type: "POST",
100 | dataType: "application/json",
101 | contentType: "application/json",
102 | complete: function(xhr) {
103 | cb(xhr);
104 | }
105 | });
106 | }
107 | function getCredential(uuid, callback) {
108 | var cb = callback || function(){};
109 | $.ajax({
110 | url: API_URL + 'credentials/' + uuid + '/',
111 | type: "GET",
112 | complete: function(xhr) {
113 | var data = JSON.parse(xhr.responseText);
114 | cb(data, xhr);
115 | }
116 | });
117 | }
118 | function updateCredential(options, callback) {
119 | var opt = options || {}
120 | var cb = callback || function(){};
121 | var data = {
122 | name: opt.name,
123 | description: opt.description,
124 | url: opt.url,
125 | username: opt.username,
126 | password: opt.password
127 | }
128 | $.ajax({
129 | url: API_URL + 'credentials/' + opt.uuid,
130 | data: JSON.stringify(data),
131 | type: "PATCH",
132 | dataType: "application/json",
133 | contentType: "application/json",
134 | complete: function(xhr) {
135 | cb(xhr);
136 | }
137 | });
138 | }
139 | function deleteCredential(uuid, callback) {
140 | var cb = callback || function(){};
141 | $.ajax({
142 | url: API_URL + 'credentials/' + uuid + '/',
143 | type: "DELETE",
144 | complete: function(xhr) {
145 | cb(xhr);
146 | }
147 | });
148 | }
149 |
--------------------------------------------------------------------------------
/accounts/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import datetime
3 | from south.db import db
4 | from south.v2 import SchemaMigration
5 | from django.db import models
6 |
7 |
8 | class Migration(SchemaMigration):
9 |
10 | def forwards(self, orm):
11 | # Adding model 'UserProfile'
12 | db.create_table('accounts_userprofile', (
13 | ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
14 | ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], unique=True)),
15 | ('encryption_key_timeout', self.gf('django.db.models.fields.IntegerField')(default=3600, null=True, blank=True)),
16 | ))
17 | db.send_create_signal('accounts', ['UserProfile'])
18 |
19 |
20 | def backwards(self, orm):
21 | # Deleting model 'UserProfile'
22 | db.delete_table('accounts_userprofile')
23 |
24 |
25 | models = {
26 | 'accounts.userprofile': {
27 | 'Meta': {'object_name': 'UserProfile'},
28 | 'encryption_key_timeout': ('django.db.models.fields.IntegerField', [], {'default': '3600', 'null': 'True', 'blank': 'True'}),
29 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
30 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'})
31 | },
32 | 'auth.group': {
33 | 'Meta': {'object_name': 'Group'},
34 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
35 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
36 | 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
37 | },
38 | 'auth.permission': {
39 | 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
40 | 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
41 | 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
42 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
43 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
44 | },
45 | 'auth.user': {
46 | 'Meta': {'object_name': 'User'},
47 | 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
48 | 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
49 | 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
50 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
51 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
52 | 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
53 | 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
54 | 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
55 | 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
56 | 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
57 | 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
58 | 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
59 | 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
60 | },
61 | 'contenttypes.contenttype': {
62 | 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
63 | 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
64 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
65 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
66 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
67 | }
68 | }
69 |
70 | complete_apps = ['accounts']
--------------------------------------------------------------------------------
/accounts/migrations/0003_auto__add_field_userprofile_pro_join_date.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import datetime
3 | from south.db import db
4 | from south.v2 import SchemaMigration
5 | from django.db import models
6 |
7 |
8 | class Migration(SchemaMigration):
9 |
10 | def forwards(self, orm):
11 | # Adding field 'UserProfile.pro_join_date'
12 | db.add_column('accounts_userprofile', 'pro_join_date',
13 | self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True),
14 | keep_default=False)
15 |
16 |
17 | def backwards(self, orm):
18 | # Deleting field 'UserProfile.pro_join_date'
19 | db.delete_column('accounts_userprofile', 'pro_join_date')
20 |
21 |
22 | models = {
23 | 'accounts.userprofile': {
24 | 'Meta': {'object_name': 'UserProfile'},
25 | 'encryption_key_timeout': ('django.db.models.fields.IntegerField', [], {'default': '3600', 'null': 'True', 'blank': 'True'}),
26 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
27 | 'is_pro': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}),
28 | 'pro_join_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
29 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'})
30 | },
31 | 'auth.group': {
32 | 'Meta': {'object_name': 'Group'},
33 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
34 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
35 | 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
36 | },
37 | 'auth.permission': {
38 | 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
39 | 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
40 | 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
41 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
42 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
43 | },
44 | 'auth.user': {
45 | 'Meta': {'object_name': 'User'},
46 | 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
47 | 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
48 | 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
49 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
50 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
51 | 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
52 | 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
53 | 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
54 | 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
55 | 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
56 | 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
57 | 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
58 | 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
59 | },
60 | 'contenttypes.contenttype': {
61 | 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
62 | 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
63 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
64 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
65 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
66 | }
67 | }
68 |
69 | complete_apps = ['accounts']
--------------------------------------------------------------------------------
/accounts/migrations/0004_auto__add_field_userprofile_customer_id.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import datetime
3 | from south.db import db
4 | from south.v2 import SchemaMigration
5 | from django.db import models
6 |
7 |
8 | class Migration(SchemaMigration):
9 |
10 | def forwards(self, orm):
11 | # Adding field 'UserProfile.customer_id'
12 | db.add_column('accounts_userprofile', 'customer_id',
13 | self.gf('django.db.models.fields.PositiveIntegerField')(null=True, blank=True),
14 | keep_default=False)
15 |
16 |
17 | def backwards(self, orm):
18 | # Deleting field 'UserProfile.customer_id'
19 | db.delete_column('accounts_userprofile', 'customer_id')
20 |
21 |
22 | models = {
23 | 'accounts.userprofile': {
24 | 'Meta': {'object_name': 'UserProfile'},
25 | 'customer_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
26 | 'encryption_key_timeout': ('django.db.models.fields.IntegerField', [], {'default': '3600', 'null': 'True', 'blank': 'True'}),
27 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
28 | 'is_pro': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}),
29 | 'pro_join_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
30 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'})
31 | },
32 | 'auth.group': {
33 | 'Meta': {'object_name': 'Group'},
34 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
35 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
36 | 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
37 | },
38 | 'auth.permission': {
39 | 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
40 | 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
41 | 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
42 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
43 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
44 | },
45 | 'auth.user': {
46 | 'Meta': {'object_name': 'User'},
47 | 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
48 | 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
49 | 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
50 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
51 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
52 | 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
53 | 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
54 | 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
55 | 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
56 | 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
57 | 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
58 | 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
59 | 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
60 | },
61 | 'contenttypes.contenttype': {
62 | 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
63 | 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
64 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
65 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
66 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
67 | }
68 | }
69 |
70 | complete_apps = ['accounts']
--------------------------------------------------------------------------------
/accounts/migrations/0005_auto__chg_field_userprofile_customer_id.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import datetime
3 | from south.db import db
4 | from south.v2 import SchemaMigration
5 | from django.db import models
6 |
7 |
8 | class Migration(SchemaMigration):
9 |
10 | def forwards(self, orm):
11 |
12 | # Changing field 'UserProfile.customer_id'
13 | db.alter_column('accounts_userprofile', 'customer_id', self.gf('django.db.models.fields.CharField')(max_length=64, null=True))
14 |
15 | def backwards(self, orm):
16 |
17 | # Changing field 'UserProfile.customer_id'
18 | db.alter_column('accounts_userprofile', 'customer_id', self.gf('django.db.models.fields.PositiveIntegerField')(null=True))
19 |
20 | models = {
21 | 'accounts.userprofile': {
22 | 'Meta': {'object_name': 'UserProfile'},
23 | 'customer_id': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}),
24 | 'encryption_key_timeout': ('django.db.models.fields.IntegerField', [], {'default': '3600', 'null': 'True', 'blank': 'True'}),
25 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
26 | 'is_pro': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}),
27 | 'pro_join_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
28 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'})
29 | },
30 | 'auth.group': {
31 | 'Meta': {'object_name': 'Group'},
32 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
33 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
34 | 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
35 | },
36 | 'auth.permission': {
37 | 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
38 | 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
39 | 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
40 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
41 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
42 | },
43 | 'auth.user': {
44 | 'Meta': {'object_name': 'User'},
45 | 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
46 | 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
47 | 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
48 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
49 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
50 | 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
51 | 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
52 | 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
53 | 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
54 | 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
55 | 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
56 | 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
57 | 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
58 | },
59 | 'contenttypes.contenttype': {
60 | 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
61 | 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
62 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
63 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
64 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
65 | }
66 | }
67 |
68 | complete_apps = ['accounts']
--------------------------------------------------------------------------------
/accounts/migrations/0006_auto__add_field_userprofile_activation_code.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import datetime
3 | from south.db import db
4 | from south.v2 import SchemaMigration
5 | from django.db import models
6 |
7 |
8 | class Migration(SchemaMigration):
9 |
10 | def forwards(self, orm):
11 | # Adding field 'UserProfile.activation_code'
12 | db.add_column('accounts_userprofile', 'activation_code',
13 | self.gf('django.db.models.fields.CharField')(max_length=64, null=True, blank=True),
14 | keep_default=False)
15 |
16 |
17 | def backwards(self, orm):
18 | # Deleting field 'UserProfile.activation_code'
19 | db.delete_column('accounts_userprofile', 'activation_code')
20 |
21 |
22 | models = {
23 | 'accounts.userprofile': {
24 | 'Meta': {'object_name': 'UserProfile'},
25 | 'activation_code': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}),
26 | 'customer_id': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}),
27 | 'encryption_key_timeout': ('django.db.models.fields.IntegerField', [], {'default': '3600', 'null': 'True', 'blank': 'True'}),
28 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
29 | 'is_pro': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}),
30 | 'pro_join_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
31 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'})
32 | },
33 | 'auth.group': {
34 | 'Meta': {'object_name': 'Group'},
35 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
36 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
37 | 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
38 | },
39 | 'auth.permission': {
40 | 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
41 | 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
42 | 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
43 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
44 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
45 | },
46 | 'auth.user': {
47 | 'Meta': {'object_name': 'User'},
48 | 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
49 | 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
50 | 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
51 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
52 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
53 | 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
54 | 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
55 | 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
56 | 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
57 | 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
58 | 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
59 | 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
60 | 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
61 | },
62 | 'contenttypes.contenttype': {
63 | 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
64 | 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
65 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
66 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
67 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
68 | }
69 | }
70 |
71 | complete_apps = ['accounts']
--------------------------------------------------------------------------------
/vault/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import datetime
3 | from south.db import db
4 | from south.v2 import SchemaMigration
5 | from django.db import models
6 |
7 |
8 | class Migration(SchemaMigration):
9 |
10 | def forwards(self, orm):
11 | # Adding model 'Credential'
12 | db.create_table('vault_credential', (
13 | ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
14 | ('name', self.gf('django.db.models.fields.CharField')(max_length=64, null=True, blank=True)),
15 | ('owner', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)),
16 | ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
17 | ('url', self.gf('django.db.models.fields.URLField')(max_length=200, null=True, blank=True)),
18 | ('key', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
19 | ))
20 | db.send_create_signal('vault', ['Credential'])
21 |
22 |
23 | def backwards(self, orm):
24 | # Deleting model 'Credential'
25 | db.delete_table('vault_credential')
26 |
27 |
28 | models = {
29 | 'auth.group': {
30 | 'Meta': {'object_name': 'Group'},
31 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
32 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
33 | 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
34 | },
35 | 'auth.permission': {
36 | 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
37 | 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
38 | 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
39 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
40 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
41 | },
42 | 'auth.user': {
43 | 'Meta': {'object_name': 'User'},
44 | 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
45 | 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
46 | 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
47 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
48 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
49 | 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
50 | 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
51 | 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
52 | 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
53 | 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
54 | 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
55 | 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
56 | 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
57 | },
58 | 'contenttypes.contenttype': {
59 | 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
60 | 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
61 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
62 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
63 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
64 | },
65 | 'vault.credential': {
66 | 'Meta': {'object_name': 'Credential'},
67 | 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
68 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
69 | 'key': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
70 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}),
71 | 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
72 | 'url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
73 | }
74 | }
75 |
76 | complete_apps = ['vault']
--------------------------------------------------------------------------------
/vault/migrations/0004_auto__add_field_credentialgroup_name__add_field_credentialgroup_descri.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import datetime
3 | from south.db import db
4 | from south.v2 import SchemaMigration
5 | from django.db import models
6 |
7 |
8 | class Migration(SchemaMigration):
9 |
10 | def forwards(self, orm):
11 | # Adding field 'CredentialGroup.name'
12 | db.add_column('vault_credentialgroup', 'name',
13 | self.gf('django.db.models.fields.CharField')(max_length=64, null=True, blank=True),
14 | keep_default=False)
15 |
16 | # Adding field 'CredentialGroup.description'
17 | db.add_column('vault_credentialgroup', 'description',
18 | self.gf('django.db.models.fields.TextField')(null=True, blank=True),
19 | keep_default=False)
20 |
21 |
22 | def backwards(self, orm):
23 | # Deleting field 'CredentialGroup.name'
24 | db.delete_column('vault_credentialgroup', 'name')
25 |
26 | # Deleting field 'CredentialGroup.description'
27 | db.delete_column('vault_credentialgroup', 'description')
28 |
29 |
30 | models = {
31 | 'auth.group': {
32 | 'Meta': {'object_name': 'Group'},
33 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
34 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
35 | 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
36 | },
37 | 'auth.permission': {
38 | 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
39 | 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
40 | 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
41 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
42 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
43 | },
44 | 'auth.user': {
45 | 'Meta': {'object_name': 'User'},
46 | 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
47 | 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
48 | 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
49 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
50 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
51 | 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
52 | 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
53 | 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
54 | 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
55 | 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
56 | 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
57 | 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
58 | 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
59 | },
60 | 'contenttypes.contenttype': {
61 | 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
62 | 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
63 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
64 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
65 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
66 | },
67 | 'vault.credential': {
68 | 'Meta': {'object_name': 'Credential'},
69 | 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
70 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['vault.CredentialGroup']", 'null': 'True', 'blank': 'True'}),
71 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
72 | 'key': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
73 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
74 | 'url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
75 | },
76 | 'vault.credentialgroup': {
77 | 'Meta': {'object_name': 'CredentialGroup'},
78 | 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
79 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
80 | 'members': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'credential_group_members'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}),
81 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}),
82 | 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'credential_group_owner'", 'null': 'True', 'to': "orm['auth.User']"})
83 | }
84 | }
85 |
86 | complete_apps = ['vault']
--------------------------------------------------------------------------------
/vault/migrations/0003_auto__del_field_credential_group.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import datetime
3 | from south.db import db
4 | from south.v2 import SchemaMigration
5 | from django.db import models
6 |
7 |
8 | class Migration(SchemaMigration):
9 |
10 | def forwards(self, orm):
11 | # Deleting field 'Credential.group'
12 | db.delete_column('vault_credential', 'group_id')
13 |
14 | # Adding M2M table for field groups on 'Credential'
15 | db.create_table('vault_credential_groups', (
16 | ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
17 | ('credential', models.ForeignKey(orm['vault.credential'], null=False)),
18 | ('credentialgroup', models.ForeignKey(orm['vault.credentialgroup'], null=False))
19 | ))
20 | db.create_unique('vault_credential_groups', ['credential_id', 'credentialgroup_id'])
21 |
22 |
23 | def backwards(self, orm):
24 | # Adding field 'Credential.group'
25 | db.add_column('vault_credential', 'group',
26 | self.gf('django.db.models.fields.related.ForeignKey')(to=orm['vault.CredentialGroup'], null=True, blank=True),
27 | keep_default=False)
28 |
29 | # Removing M2M table for field groups on 'Credential'
30 | db.delete_table('vault_credential_groups')
31 |
32 |
33 | models = {
34 | 'auth.group': {
35 | 'Meta': {'object_name': 'Group'},
36 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
37 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
38 | 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
39 | },
40 | 'auth.permission': {
41 | 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
42 | 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
43 | 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
44 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
45 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
46 | },
47 | 'auth.user': {
48 | 'Meta': {'object_name': 'User'},
49 | 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
50 | 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
51 | 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
52 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
53 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
54 | 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
55 | 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
56 | 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
57 | 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
58 | 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
59 | 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
60 | 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
61 | 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
62 | },
63 | 'contenttypes.contenttype': {
64 | 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
65 | 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
66 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
67 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
68 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
69 | },
70 | 'vault.credential': {
71 | 'Meta': {'object_name': 'Credential'},
72 | 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
73 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['vault.CredentialGroup']", 'null': 'True', 'blank': 'True'}),
74 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
75 | 'key': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
76 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}),
77 | 'url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
78 | },
79 | 'vault.credentialgroup': {
80 | 'Meta': {'object_name': 'CredentialGroup'},
81 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
82 | 'members': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'credential_group_members'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}),
83 | 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'credential_group_owner'", 'null': 'True', 'to': "orm['auth.User']"})
84 | }
85 | }
86 |
87 | complete_apps = ['vault']
--------------------------------------------------------------------------------
/vault/migrations/0005_auto__add_field_credential_uuid__add_field_credentialgroup_uuid.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import datetime
3 | from south.db import db
4 | from south.v2 import SchemaMigration
5 | from django.db import models
6 |
7 |
8 | class Migration(SchemaMigration):
9 |
10 | def forwards(self, orm):
11 | # Adding field 'Credential.uuid'
12 | db.add_column('vault_credential', 'uuid',
13 | self.gf('django.db.models.fields.CharField')(default='6f2fae29-03f9-4daa-8e71-8bf965d43008', max_length=36, null=True, blank=True),
14 | keep_default=False)
15 |
16 | # Adding field 'CredentialGroup.uuid'
17 | db.add_column('vault_credentialgroup', 'uuid',
18 | self.gf('django.db.models.fields.CharField')(default='5045c41b-f9cb-4265-a4ff-a00fac0d5ae4', max_length=36, null=True, blank=True),
19 | keep_default=False)
20 |
21 |
22 | def backwards(self, orm):
23 | # Deleting field 'Credential.uuid'
24 | db.delete_column('vault_credential', 'uuid')
25 |
26 | # Deleting field 'CredentialGroup.uuid'
27 | db.delete_column('vault_credentialgroup', 'uuid')
28 |
29 |
30 | models = {
31 | 'auth.group': {
32 | 'Meta': {'object_name': 'Group'},
33 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
34 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
35 | 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
36 | },
37 | 'auth.permission': {
38 | 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
39 | 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
40 | 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
41 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
42 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
43 | },
44 | 'auth.user': {
45 | 'Meta': {'object_name': 'User'},
46 | 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
47 | 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
48 | 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
49 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
50 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
51 | 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
52 | 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
53 | 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
54 | 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
55 | 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
56 | 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
57 | 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
58 | 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
59 | },
60 | 'contenttypes.contenttype': {
61 | 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
62 | 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
63 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
64 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
65 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
66 | },
67 | 'vault.credential': {
68 | 'Meta': {'object_name': 'Credential'},
69 | 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
70 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['vault.CredentialGroup']", 'null': 'True', 'blank': 'True'}),
71 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
72 | 'key': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
73 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}),
74 | 'url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
75 | 'uuid': ('django.db.models.fields.CharField', [], {'default': "'e6a4765f-3704-4f55-ad0e-c6d6f3b9823b'", 'max_length': '36', 'null': 'True', 'blank': 'True'})
76 | },
77 | 'vault.credentialgroup': {
78 | 'Meta': {'object_name': 'CredentialGroup'},
79 | 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
80 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
81 | 'members': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'credential_group_members'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}),
82 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}),
83 | 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'credential_group_owner'", 'null': 'True', 'to': "orm['auth.User']"}),
84 | 'uuid': ('django.db.models.fields.CharField', [], {'default': "'c7581278-b58b-479b-bb46-ab2389313cf0'", 'max_length': '36', 'null': 'True', 'blank': 'True'})
85 | }
86 | }
87 |
88 | complete_apps = ['vault']
--------------------------------------------------------------------------------
/vault/migrations/0006_auto__del_field_credential_key__add_field_credential_username__add_fie.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import datetime
3 | from south.db import db
4 | from south.v2 import SchemaMigration
5 | from django.db import models
6 |
7 |
8 | class Migration(SchemaMigration):
9 |
10 | def forwards(self, orm):
11 | # Deleting field 'Credential.key'
12 | db.delete_column('vault_credential', 'key')
13 |
14 | # Adding field 'Credential.username'
15 | db.add_column('vault_credential', 'username',
16 | self.gf('django.db.models.fields.CharField')(max_length=96, null=True, blank=True),
17 | keep_default=False)
18 |
19 | # Adding field 'Credential.password'
20 | db.add_column('vault_credential', 'password',
21 | self.gf('django.db.models.fields.TextField')(null=True, blank=True),
22 | keep_default=False)
23 |
24 |
25 | def backwards(self, orm):
26 | # Adding field 'Credential.key'
27 | db.add_column('vault_credential', 'key',
28 | self.gf('django.db.models.fields.TextField')(null=True, blank=True),
29 | keep_default=False)
30 |
31 | # Deleting field 'Credential.username'
32 | db.delete_column('vault_credential', 'username')
33 |
34 | # Deleting field 'Credential.password'
35 | db.delete_column('vault_credential', 'password')
36 |
37 |
38 | models = {
39 | 'auth.group': {
40 | 'Meta': {'object_name': 'Group'},
41 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
42 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
43 | 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
44 | },
45 | 'auth.permission': {
46 | 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
47 | 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
48 | 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
49 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
50 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
51 | },
52 | 'auth.user': {
53 | 'Meta': {'object_name': 'User'},
54 | 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
55 | 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
56 | 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
57 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
58 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
59 | 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
60 | 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
61 | 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
62 | 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
63 | 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
64 | 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
65 | 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
66 | 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
67 | },
68 | 'contenttypes.contenttype': {
69 | 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
70 | 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
71 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
72 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
73 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
74 | },
75 | 'vault.credential': {
76 | 'Meta': {'object_name': 'Credential'},
77 | 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
78 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['vault.CredentialGroup']", 'null': 'True', 'blank': 'True'}),
79 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
80 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}),
81 | 'password': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
82 | 'url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
83 | 'username': ('django.db.models.fields.CharField', [], {'max_length': '96', 'null': 'True', 'blank': 'True'}),
84 | 'uuid': ('django.db.models.fields.CharField', [], {'default': "'fcfc33de-6751-43c2-8b2b-6720e62959d7'", 'max_length': '36', 'null': 'True', 'blank': 'True'})
85 | },
86 | 'vault.credentialgroup': {
87 | 'Meta': {'object_name': 'CredentialGroup'},
88 | 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
89 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
90 | 'members': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'credential_group_members'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}),
91 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}),
92 | 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'credential_group_owner'", 'null': 'True', 'to': "orm['auth.User']"}),
93 | 'uuid': ('django.db.models.fields.CharField', [], {'default': "'90b3abf8-00fe-46c3-8a37-8c0e0331b4f1'", 'max_length': '36', 'null': 'True', 'blank': 'True'})
94 | }
95 | }
96 |
97 | complete_apps = ['vault']
--------------------------------------------------------------------------------
/vault/migrations/0002_auto__add_credentialgroup__del_field_credential_owner__add_field_crede.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import datetime
3 | from south.db import db
4 | from south.v2 import SchemaMigration
5 | from django.db import models
6 |
7 |
8 | class Migration(SchemaMigration):
9 |
10 | def forwards(self, orm):
11 | # Adding model 'CredentialGroup'
12 | db.create_table('vault_credentialgroup', (
13 | ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
14 | ('owner', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='credential_group_owner', null=True, to=orm['auth.User'])),
15 | ))
16 | db.send_create_signal('vault', ['CredentialGroup'])
17 |
18 | # Adding M2M table for field members on 'CredentialGroup'
19 | db.create_table('vault_credentialgroup_members', (
20 | ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
21 | ('credentialgroup', models.ForeignKey(orm['vault.credentialgroup'], null=False)),
22 | ('user', models.ForeignKey(orm['auth.user'], null=False))
23 | ))
24 | db.create_unique('vault_credentialgroup_members', ['credentialgroup_id', 'user_id'])
25 |
26 | # Deleting field 'Credential.owner'
27 | db.delete_column('vault_credential', 'owner_id')
28 |
29 | # Adding field 'Credential.group'
30 | db.add_column('vault_credential', 'group',
31 | self.gf('django.db.models.fields.related.ForeignKey')(to=orm['vault.CredentialGroup'], null=True, blank=True),
32 | keep_default=False)
33 |
34 |
35 | def backwards(self, orm):
36 | # Deleting model 'CredentialGroup'
37 | db.delete_table('vault_credentialgroup')
38 |
39 | # Removing M2M table for field members on 'CredentialGroup'
40 | db.delete_table('vault_credentialgroup_members')
41 |
42 | # Adding field 'Credential.owner'
43 | db.add_column('vault_credential', 'owner',
44 | self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True),
45 | keep_default=False)
46 |
47 | # Deleting field 'Credential.group'
48 | db.delete_column('vault_credential', 'group_id')
49 |
50 |
51 | models = {
52 | 'auth.group': {
53 | 'Meta': {'object_name': 'Group'},
54 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
55 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
56 | 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
57 | },
58 | 'auth.permission': {
59 | 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
60 | 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
61 | 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
62 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
63 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
64 | },
65 | 'auth.user': {
66 | 'Meta': {'object_name': 'User'},
67 | 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
68 | 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
69 | 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
70 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
71 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
72 | 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
73 | 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
74 | 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
75 | 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
76 | 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
77 | 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
78 | 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
79 | 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
80 | },
81 | 'contenttypes.contenttype': {
82 | 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
83 | 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
84 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
85 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
86 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
87 | },
88 | 'vault.credential': {
89 | 'Meta': {'object_name': 'Credential'},
90 | 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
91 | 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['vault.CredentialGroup']", 'null': 'True', 'blank': 'True'}),
92 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
93 | 'key': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
94 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}),
95 | 'url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
96 | },
97 | 'vault.credentialgroup': {
98 | 'Meta': {'object_name': 'CredentialGroup'},
99 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
100 | 'members': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'credential_group_members'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}),
101 | 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'credential_group_owner'", 'null': 'True', 'to': "orm['auth.User']"})
102 | }
103 | }
104 |
105 | complete_apps = ['vault']
--------------------------------------------------------------------------------
/accounts/views.py:
--------------------------------------------------------------------------------
1 | # Copyright 2013 Evan Hazlett and contributors
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | from django.shortcuts import render_to_response, redirect
15 | from django.core.urlresolvers import reverse
16 | from django.template import RequestContext
17 | from django.http import HttpResponse
18 | from django.views.decorators.http import require_http_methods
19 | from django.contrib.auth.decorators import login_required
20 | from django.contrib.auth import (authenticate, login as login_user,
21 | logout as logout_user)
22 | from django.contrib.auth.models import User
23 | from django.contrib import messages
24 | from django.conf import settings
25 | from django.core.mail import send_mail
26 | from django.views.decorators.csrf import csrf_exempt
27 | from django.utils.translation import ugettext as _
28 | from accounts.forms import AccountForm, UserProfileForm
29 | from accounts.models import UserProfile
30 | from datetime import datetime
31 | from utils import billing
32 | import random
33 | import string
34 | try:
35 | import simplejson as json
36 | except ImportError:
37 | import json
38 |
39 | @require_http_methods(["GET", "POST"])
40 | def login(request):
41 | if request.method == 'POST':
42 | username = request.POST.get('username')
43 | password = request.POST.get('password')
44 | user = authenticate(username=username, password=password)
45 | if user is not None:
46 | if user.is_active:
47 | login_user(request, user)
48 | return redirect(reverse('index'))
49 | else:
50 | messages.error(request, _('Your account is disabled. Make sure you have activated your account.'))
51 | else:
52 | messages.error(request, _('Invalid username/password'))
53 | return render_to_response('accounts/login.html',
54 | context_instance=RequestContext(request))
55 |
56 | def logout(request):
57 | logout_user(request)
58 | return redirect(reverse('index'))
59 |
60 | @login_required
61 | def details(request):
62 | ctx = {}
63 | form = AccountForm(instance=request.user)
64 | pform = UserProfileForm(instance=request.user.get_profile())
65 | if request.method == 'POST':
66 | form = AccountForm(request.POST, instance=request.user)
67 | pform = UserProfileForm(request.POST,
68 | instance=request.user.get_profile())
69 | if form.is_valid() and pform.is_valid():
70 | form.save()
71 | pform.save()
72 | messages.info(request, _('Account updated.'))
73 | ctx['form'] = form
74 | ctx['pform'] = pform
75 | return render_to_response('accounts/details.html', ctx,
76 | context_instance=RequestContext(request))
77 |
78 | def confirm(request, code=None):
79 | up = UserProfile.objects.get(activation_code=code)
80 | user = up.user
81 | user.is_active = True
82 | user.save()
83 | messages.success(request, _('Thanks! You may now login.'))
84 | return redirect(reverse('accounts.login'))
85 |
86 | def signup(request):
87 | ctx = {}
88 | if not settings.SIGNUP_ENABLED:
89 | messages.warning(request, _('Signup is not enabled at this time.'))
90 | return redirect(reverse('index'))
91 | if request.method == 'POST':
92 | username = request.POST.get('username')
93 | password = request.POST.get('password')
94 | first_name = request.POST.get('first_name')
95 | last_name = request.POST.get('last_name')
96 | username = request.POST.get('username')
97 | email = request.POST.get('email')
98 | user = User(first_name=first_name, last_name=last_name,
99 | email=email)
100 | user.username = username
101 | user.set_password(password)
102 | user.is_active = False
103 | user.save()
104 | # generate code
105 | code = ''.join(random.sample(string.letters+string.digits, 16))
106 | up = user.get_profile()
107 | up.activation_code = code
108 | up.save()
109 | # send welcome
110 | tmpl = """Thanks for signing up!
111 |
112 | Please activate your account by clicking the following link:
113 |
114 | http://{0}{1}
115 |
116 | Please feel free to request features, submit bug reports, check the wiki, etc.
117 | at https://github.com/ehazlett/locksmith/wiki
118 |
119 | If you have any questions please feel free to contact us at support@vitasso.com.
120 |
121 | Thanks!
122 | Locksmith Team
123 | """.format(request.get_host(), reverse('accounts.confirm', args=[code]))
124 | send_mail(_('Welcome to Locksmith!'), tmpl, settings.ADMIN_EMAIL,
125 | [user.email], fail_silently=True)
126 | messages.success(request, _('Thanks! Please check your email to activate.'))
127 | return redirect(reverse('index'))
128 | return render_to_response('accounts/signup.html', ctx,
129 | context_instance=RequestContext(request))
130 |
131 | @login_required
132 | def activate(request):
133 | ctx = {}
134 | if request.method == 'POST':
135 | token = request.POST.get('token')
136 | try:
137 | customer = billing.create_customer(token, settings.ACCOUNT_PLAN,
138 | request.user.email)
139 | up = request.user.get_profile()
140 | up.customer_id = customer.id
141 | up.save()
142 | messages.success(request, _('Thanks for supporting! Please let us know if you have any questions.'))
143 | return redirect(reverse('index'))
144 | except Exception, e:
145 | messages.error(request, '{0}:{1}'.format(
146 | _('Error processing payment'), e))
147 | return render_to_response('accounts/activate.html', ctx,
148 | context_instance=RequestContext(request))
149 |
150 | @csrf_exempt
151 | def hook(request):
152 | event = json.loads(request.body)
153 | print(event)
154 | event_type = event.get('type')
155 | # subscription payment success
156 | if event_type == 'invoice.payment_succeeded':
157 | customer = event.get('data', {}).get('object', {}).get('customer')
158 | up = UserProfile.objects.get(customer_id=customer)
159 | if settings.DEBUG or event.get('livemode'):
160 | up.is_pro = True
161 | up.pro_join_date = datetime.now()
162 | up.save()
163 | # subscription ended
164 | if event_type == 'customer.subscription.deleted' or \
165 | event_type == 'charge.refunded' or event_type == 'charge.failed' or \
166 | event_type == 'customer.subscription.deleted' or \
167 | event_type == 'invoice.payment_failed':
168 | customer = event.get('data', {}).get('object', {}).get('customer')
169 | up = UserProfile.objects.get(customer_id=customer)
170 | if settings.DEBUG or event.get('livemode'):
171 | up.is_pro = False
172 | up.save()
173 | return HttpResponse(status=200)
174 |
--------------------------------------------------------------------------------
/static/css/app.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 50px;
3 | }
4 | .content {
5 | min-height: 300px;
6 | }
7 | .control-label {
8 | color: #777777;
9 | }
10 | a:hover {
11 | text-decoration: none;
12 | }
13 | .alert {
14 | position: fixed;
15 | overflow: visible;
16 | top: 45px;
17 | left: 10px;
18 | right: 10px;
19 | z-index: 999;
20 | text-align: center;
21 | }
22 | .info {
23 | color: #8a8a8a;
24 | }
25 | .brand i {
26 | position: relative;
27 | top: 6px;
28 | margin-right: 5px;
29 | }
30 | .hide {
31 | display: none;
32 | }
33 | .center {
34 | text-align: center;
35 | margin: 0 auto 0 auto;
36 | }
37 | .logo {
38 | }
39 | .entypo {
40 | font-family: "EntypoRegular", sans-serif;
41 | font-size: 28px;
42 | }
43 | .entypo-social {
44 | font-family: "EntypoSocial", arial;
45 | font-size: 64px;
46 | color: #7e7e7e;
47 | }
48 | a.entypo-social:hover {
49 | color: #a9a9a9;
50 | }
51 | table, a.thumbnail, ul.nav-stacked {
52 | background: #fefefe;
53 | }
54 | label.error {
55 | color: #b20000
56 | }
57 | div.caption {
58 | color: #818181;
59 | }
60 | a.cover {
61 | height: 270px;
62 | overflow: hidden;
63 | }
64 | a.thumbnail > img {
65 | width: 160px;
66 | height: 160px;
67 | }
68 | img.media-object.event {
69 | width: 64px;
70 | height: 64px;
71 | }
72 | div.browse-content {
73 | margin-bottom: 64px;
74 | }
75 | div#player {
76 | font-family: "EntypoRegular", sans-serif;
77 | background: #fefefe;
78 | border-top: 1px solid #dddddd;
79 | height: 48px;
80 | opacity: 0.85;
81 | filter:alpha(opacity=75);
82 | }
83 | div.controls {
84 | font-size: 32px;
85 | }
86 | div#player-content {
87 | margin-top: 16px;
88 | }
89 | div#player-playing {
90 | margin: 0 10px 0 0;
91 | }
92 | .control {
93 | cursor: pointer;
94 | }
95 | footer {
96 | margin: 0 auto 20px auto;
97 | width: 100%;
98 | color: #b3b3b3;
99 | text-align: center; /* center align it with the container */
100 | }
101 | div#login {
102 | color: #8a8a8a;
103 | font-size: 24px;
104 | text-align: center;
105 | margin: 10px auto 0 auto;
106 | }
107 | div#nav-left {
108 | margin: 0 10px 10px 0;
109 | }
110 | div.page-toolbar {
111 | margin: 0 0 5px 0;
112 | }
113 | .errorlist {
114 | float: right;
115 | list-style-type: none;
116 | color: #b20000
117 | }
118 | .help-inline {
119 | font-size: 14px;
120 | }
121 | div.section {
122 | padding: 10px;
123 | position: relative;
124 | background: #f3f4f6;
125 | min-height: 50px;
126 | background-image: -moz-linear-gradient(top, #fefefe, #f4f4f4);
127 | background-image: -ms-linear-gradient(top, #fefefe, #f4f4f4);
128 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fefefe), to(#f4f4f4));
129 | background-image: -webkit-linear-gradient(top, #fefefe, #f4f4f4);
130 | background-image: -o-linear-gradient(top, #fefefe, #f4f4f4);
131 | background-image: linear-gradient(top, #fefefe, #f4f4f4);
132 | background-repeat: repeat-x;
133 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fefefe', endColorstr='#f4f4f4', GradientType=0);
134 | background-color: #eff0f3;
135 | background-image: -moz-linear-gradient(top, #fefefe, #f4f4f4);
136 | background-image: -ms-linear-gradient(top, #fefefe, #f4f4f4);
137 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fefefe), to(#f4f4f4));
138 | background-image: -webkit-linear-gradient(top, #fefefe, #f4f4f4);
139 | background-image: -o-linear-gradient(top, #fefefe, #f4f4f4);
140 | background-image: linear-gradient(top, #fefefe, #f4f4f4);
141 | background-repeat: repeat-x;
142 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fefefe', endColorstr='#f4f4f4', GradientType=0);
143 | border: 1px solid #d5d5d5;
144 | -webkit-border-radius: 10px;
145 | -moz-border-radius: 10px;
146 | border-radius: 10px;
147 | -webkit-box-shadow: 0 0 8px rgba(0, 0, 0, 0.15);
148 | box-shadow: 0 0 8px rgba(0, 0, 0, 0.15);
149 | }
150 | div.section table {
151 | background: #fefefe;
152 | }
153 | div.section table td.status {
154 | text-align: center;
155 | }
156 | div.section-title {
157 | padding: 10px 10px 20px 10px;
158 | margin: 0 -10px 15px -10px;
159 | border-bottom: 1px solid #d5d5d5;
160 | font-size: 20px;
161 | background-image: -moz-linear-gradient(top, #fefeff, #fafafa);
162 | background-image: -ms-linear-gradient(top, #fefeff, #fafafa);
163 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fefeff), to(#fafafa));
164 | background-image: -webkit-linear-gradient(top, #fefeff, #fafafa);
165 | background-image: -o-linear-gradient(top, #fefeff, #fafafa);
166 | background-image: linear-gradient(top, #fefeff, #fafafa);
167 | background-repeat: repeat-x;
168 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fefeff', endColorstr='#fafafa', GradientType=0);
169 | background-color: #eff0f3;
170 | background-image: -moz-linear-gradient(top, #fefeff, #fafafa);
171 | background-image: -ms-linear-gradient(top, #fefeff, #fafafa);
172 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fefeff), to(#fafafa));
173 | background-image: -webkit-linear-gradient(top, #fefeff, #fafafa);
174 | background-image: -o-linear-gradient(top, #fefeff, #fafafa);
175 | background-image: linear-gradient(top, #fefeff, #fafafa);
176 | background-repeat: repeat-x;
177 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fefeff', endColorstr='#fafafa', GradientType=0);
178 | }
179 | div.section-toolbar {
180 | margin: 0 0 10px 0;
181 | }
182 | div.modal.span10 {
183 | position: fixed;
184 | left: 10%;
185 | }
186 | div.toolbar {
187 | margin: 0 0 10px 0;
188 | }
189 | div.caption {
190 | text-align: center;
191 | color: #5e5e5e;
192 | }
193 | .entypo.large {
194 | color: #7a7a7a;
195 | margin: 20px 0 0 0;
196 | font-size: 72px;
197 | text-align: center;
198 | }
199 | div.group.title {
200 | margin: 5px 0 0 0;
201 | font-size: 24px;
202 | color: #7a7a7a;
203 | }
204 | div.social-auth {
205 | width: 25%;
206 | margin: 30px auto 0 auto;
207 | text-align: center;
208 | }
209 | span#credential-status {
210 | margin-left: 20px;
211 | }
212 | div.credential-groups {
213 | margin: 45px 0 0 0;
214 | }
215 | div.credentials {
216 | margin: 25px 0 0 0;
217 | }
218 | a.thumbnail {
219 | color: #8e8e8e;
220 | }
221 | a.thumbnail:hover {
222 | color: #cacaca;
223 | }
224 | a.thumbnail > div.entypo {
225 | margin: 15px 0 10px 0;
226 | font-size: 72px;
227 | text-align: center;
228 | }
229 | .logo {
230 | margin: 32px 48px 0 0;
231 | }
232 | .slogan {
233 | font-size: 32px;
234 | margin: 24px 0 24px 0;
235 | }
236 | .btn-join {
237 | border-radius: 25px;
238 | -webkit-border-radius: 25px;
239 | -moz-border-radius: 25px;
240 | width: 125px;
241 | }
242 | div#footer {
243 | width: 100%;
244 | color: #b3b3b3;
245 | margin: 25px 0 0 0;
246 | padding: 10px 0 0 0;
247 | height: 75px;
248 | }
249 | div#footer-info {
250 | width: 100%;
251 | }
252 | div#copyright {
253 | font-family: "EntypoRegular", sans-serif;
254 | font-size: 12px;
255 | text-align: center;
256 | width: 100%;
257 | }
258 | div#copyright a {
259 | text-decoration: none;
260 | }
261 | ul.footer-links {
262 | text-align: center;
263 | list-style-type: none;
264 | margin: 5px 0 20px 0;
265 | font-size: 48px;
266 | }
267 | ul.footer-links li {
268 | display: inline;
269 | }
270 | ul.footer-links li a {
271 | color: #a1a1a1;
272 | text-decoration: none;
273 | }
274 | ul.footer-links li a:hover {
275 | color: #cacaca;
276 | }
277 | form#signup {
278 | text-align: left;
279 | }
280 | div#activate {
281 | width: 75%;
282 | }
283 | #btn-activate {
284 | text-align: center;
285 | margin: 0 auto; }
286 | .pro-label {
287 | margin: 0 2px 0 3px;
288 | }
289 | .app-info {
290 | margin: 0 0 0 10px;
291 | font-size: 12px;
292 | }
293 | .cart-info {
294 | margin: 0 0 0 20px;
295 | font-size: 14px;
296 | }
297 | .cart-info .entypo {
298 | position: relative;
299 | top: 3px;
300 | }
301 | div.404 {
302 | margin: 25px 0 0 0;
303 | font-size: 48px;
304 | }
305 |
--------------------------------------------------------------------------------
/static/js/zc.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * zeroclipboard
3 | * The Zero Clipboard library provides an easy way to copy text to the clipboard using an invisible Adobe Flash movie, and a JavaScript interface.
4 | * Copyright 2012 Jon Rohan, James M. Greene, .
5 | * Released under the MIT license
6 | * http://jonrohan.github.com/ZeroClipboard/
7 | * v1.1.7
8 | */(function(){"use strict";var a=function(a,b){var c=a.style[b];a.currentStyle?c=a.currentStyle[b]:window.getComputedStyle&&(c=document.defaultView.getComputedStyle(a,null).getPropertyValue(b));if(c=="auto"&&b=="cursor"){var d=["a"];for(var e=0;e=0?"&":"?")+"nocache="+(new Date).getTime()},i=function(a){var b=[];return a.trustedDomains&&(typeof a.trustedDomains=="string"?b.push("trustedDomain="+a.trustedDomains):b.push("trustedDomain="+a.trustedDomains.join(","))),b.join("&")},j=function(a,b){if(b.indexOf)return b.indexOf(a);for(var c=0,d=b.length;c ';b=document.createElement("div"),b.id="global-zeroclipboard-html-bridge",b.setAttribute("class","global-zeroclipboard-container"),b.setAttribute("data-clipboard-ready",!1),b.style.position="absolute",b.style.left="-9999px",b.style.top="-9999px",b.style.width="15px",b.style.height="15px",b.style.zIndex="9999",b.innerHTML=c,document.body.appendChild(b)}a.htmlBridge=b,a.flashBridge=document["global-zeroclipboard-flash-bridge"]||b.children[0].lastElementChild};l.prototype.resetBridge=function(){this.htmlBridge.style.left="-9999px",this.htmlBridge.style.top="-9999px",this.htmlBridge.removeAttribute("title"),this.htmlBridge.removeAttribute("data-clipboard-text"),f(m,this.options.activeClass),m=null,this.options.text=null},l.prototype.ready=function(){var a=this.htmlBridge.getAttribute("data-clipboard-ready");return a==="true"||a===!0},l.prototype.reposition=function(){if(!m)return!1;var a=g(m);this.htmlBridge.style.top=a.top+"px",this.htmlBridge.style.left=a.left+"px",this.htmlBridge.style.width=a.width+"px",this.htmlBridge.style.height=a.height+"px",this.htmlBridge.style.zIndex=a.zIndex+1,this.setSize(a.width,a.height)},l.dispatch=function(a,b){l.prototype._singleton.receiveEvent(a,b)},l.prototype.on=function(a,b){var c=a.toString().split(/\s/g);for(var d=0;d
3 |
4 |
5 |
6 | {{APP_NAME}}{% block title %}{% endblock %}
7 |
8 |
9 |
10 |
11 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | {% block extra_head_css %}{% endblock %}
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | {% block extra_head_js %}{% endblock %}
31 |
32 |
33 | {% if request.user.is_authenticated and not ENCRYPTION_KEY %}
34 |
55 | {% endif %}
56 |
57 | {% if messages %}
58 | {% for message in messages %}
59 |
63 | {% endfor %}
64 |
67 | {% endif %}
68 |
69 |
110 |
111 | {% block base_content %}
112 |
113 | {% block main_content %}{% endblock %}
114 |
115 | {% endblock %}
116 |
117 |
129 |
130 |
131 |
132 |
133 |
134 |
135 | {% block extra_js %}{% endblock %}
136 |
192 | {% if GOOGLE_ANALYTICS_CODE %}
193 |
204 | {% endif %}
205 | {% if INTERCOM_APP_ID %}
206 |
213 |
214 |
215 | {% endif %}
216 |
217 |
218 |
--------------------------------------------------------------------------------
/locksmith/api_v1.py:
--------------------------------------------------------------------------------
1 | # Copyright 2013 Evan Hazlett and contributors
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | from tastypie.resources import ModelResource
15 | from tastypie.authorization import (DjangoAuthorization, Authorization)
16 | from tastypie.authentication import ApiKeyAuthentication, Authentication
17 | from tastypie.utils import trailing_slash
18 | from tastypie.bundle import Bundle
19 | from tastypie import fields
20 | from django.core.urlresolvers import reverse
21 | from django.conf.urls import patterns, url, include
22 | from django.core.paginator import Paginator, InvalidPage
23 | from django.http import Http404, HttpResponse
24 | from django.contrib.auth.models import User
25 | from django.contrib.auth import authenticate, login as login_user
26 | from django.views.decorators.csrf import csrf_exempt
27 | from django.conf import settings
28 | from django.core.cache import cache
29 | from django.db.models import Q
30 | from vault.models import CredentialGroup, Credential
31 | from tastypie.models import ApiKey
32 | from tastypie.constants import ALL, ALL_WITH_RELATIONS
33 | from utils.encryption import (decrypt, set_user_encryption_key,
34 | get_user_encryption_key)
35 | import simplejson as json
36 | import os
37 |
38 | # set csrf exempt to allow mobile login
39 | @csrf_exempt
40 | def api_login(request):
41 | username = request.POST.get('username')
42 | password = request.POST.get('password')
43 | if not username:
44 | # attempt to parse a json string
45 | data = json.loads(request.body)
46 | username = data.get('username')
47 | password = data.get('password')
48 | user = authenticate(username=username, password=password)
49 | code = 200
50 | if user is not None:
51 | login_user(request, user)
52 | ur = UserResource()
53 | user_data = ur.obj_get(request, username=user.username)
54 | bundle = ur.build_bundle(obj=user_data, request=request)
55 | data = ur.serialize(None, ur.full_dehydrate(bundle),
56 | 'application/json')
57 | else:
58 | data = json.dumps({'error': 'Access denied'})
59 | code = 403
60 | return HttpResponse(data, status=code,
61 | content_type='application/json')
62 |
63 | class AppAuthentication(Authentication):
64 | def is_authenticated(self, request, **kwargs):
65 | # session based
66 | if request.user.is_authenticated():
67 | return True
68 | else: # check api_key
69 | if request.META.has_key('HTTP_AUTHORIZATION'):
70 | auth_header = request.META.get('HTTP_AUTHORIZATION')
71 | key = request.META.get('HTTP_ENCRYPTION_KEY')
72 | try:
73 | username, api_key = auth_header.split()[-1].split(':')
74 | # check auth
75 | user = User.objects.get(username=username)
76 | if user and user.api_key.key == api_key:
77 | # set encryption key
78 | set_user_encryption_key(user.username, key)
79 | # auth successful ; set request.user to user for
80 | # later user (authorization, filtering, etc.)
81 | request.user = user
82 | return True
83 | except:
84 | # invalid auth header
85 | pass
86 | return False
87 |
88 | class CredentialGroupAuthorization(Authorization):
89 | def read_list(self, object_list, bundle):
90 | return object_list.filter(Q(owner=bundle.request.user) | \
91 | Q(members__in=bundle.request.user))
92 |
93 | def read_detail(self, object_list, bundle):
94 | return object_list.filter(Q(owner=bundle.request.user) | \
95 | Q(members__in=[bundle.request.user]))
96 |
97 | def create_list(self, object_list, bundle):
98 | return object_list
99 |
100 | def create_detail(self, object_list, bundle):
101 | return bundle.obj.owner == bundle.request.user
102 |
103 | def update_list(self, object_list, bundle):
104 | allowed = []
105 |
106 | # Since they may not all be saved, iterate over them.
107 | for obj in object_list:
108 | if obj.owner == bundle.request.user:
109 | allowed.append(obj)
110 | return allowed
111 |
112 | def update_detail(self, object_list, bundle):
113 | return bundle.obj.owner == bundle.request.user
114 |
115 | def delete_list(self, object_list, bundle):
116 | return bundle.obj.owner == bundle.request.user
117 |
118 | def delete_detail(self, object_list, bundle):
119 | return bundle.obj.owner == bundle.request.user
120 |
121 | class CredentialAuthorization(Authorization):
122 | def read_list(self, object_list, bundle):
123 | return object_list.filter(groups__owner=bundle.request.user)
124 |
125 | def read_detail(self, object_list, bundle):
126 | return object_list.filter(groups__owner=bundle.request.user)
127 |
128 | def create_list(self, object_list, bundle):
129 | return object_list
130 |
131 | def create_detail(self, object_list, bundle):
132 | return object_list.filter(groups__owner=bundle.request.user)
133 |
134 | def update_list(self, object_list, bundle):
135 | allowed = []
136 |
137 | # Since they may not all be saved, iterate over them.
138 | for obj in object_list:
139 | for g in obj.groups:
140 | if g.owner == bundle.request.user:
141 | allowed.append(obj)
142 | return allowed
143 |
144 | def update_detail(self, object_list, bundle):
145 | return object_list.filter(groups__owner=bundle.request.user)
146 |
147 | def delete_list(self, object_list, bundle):
148 | return object_list.filter(groups__owner=bundle.request.user)
149 |
150 | def delete_detail(self, object_list, bundle):
151 | return object_list.filter(groups__owner=bundle.request.user)
152 |
153 | class UserResource(ModelResource):
154 | class Meta:
155 | queryset = User.objects.all()
156 | excludes = ('id', 'password', 'is_staff', 'is_superuser')
157 | list_allowed_methods = ['get']
158 | authentication = AppAuthentication()
159 | authorization = Authorization()
160 | resource_name = 'accounts'
161 |
162 | def prepend_urls(self):
163 | return [
164 | url(r"^(?P%s)/(?P[\w\d_.-]+)/$" % self._meta.resource_name, self.wrap_view('dispatch_detail'), name="api_dispatch_detail"),
165 | ]
166 |
167 | def get_object_list(self, request):
168 | return super(UserResource, self).get_object_list(request).filter(
169 | username=request.user.username)
170 |
171 | # only let non-admin users see their own account
172 | def apply_authorization_limits(self, request, object_list):
173 | if not request.user.is_superuser:
174 | object_list = object_list.filter(username=request.user.username)
175 | return object_list
176 |
177 | # this is broken in tastypie 0.9.13
178 | ## build custom resource_uri (instead of /resource//)
179 | #def get_resource_uri(self, bundle_or_obj, url_name='api_dispatch_list'):
180 | # kwargs = {
181 | # 'resource_name': self._meta.resource_name,
182 | # }
183 | # if isinstance(bundle_or_obj, Bundle):
184 | # kwargs['pk'] = bundle_or_obj.obj.username
185 | # else:
186 | # kwargs['pk'] = bundle_or_obj.id
187 | # if self._meta.api_name is not None:
188 | # kwargs['api_name'] = self._meta.api_name
189 | # return self._build_reverse_url('api_dispatch_detail', kwargs = kwargs)
190 |
191 | def dehydrate(self, bundle):
192 | # add api_key
193 | bundle.data['api_key'] = bundle.obj.api_key.key
194 | return bundle
195 |
196 | class CredentialGroupResource(ModelResource):
197 | class Meta:
198 | queryset = CredentialGroup.objects.all()
199 | excludes = ('id', )
200 | #list_allowed_methods = ['get']
201 | authentication = AppAuthentication()
202 | authorization = CredentialGroupAuthorization()
203 | resource_name = 'credentialgroups'
204 | filtering = {
205 | "name": ALL,
206 | "description": ALL,
207 | }
208 |
209 | def prepend_urls(self):
210 | return [
211 | url(r"^(?P%s)/(?P[\w\d_.-]+)/$" \
212 | % self._meta.resource_name, self.wrap_view('dispatch_detail'),
213 | name="api_dispatch_detail"),
214 | ]
215 |
216 | def apply_authorization_limits(self, request, object_list):
217 | if not request.user.is_superuser:
218 | object_list = object_list.filter(owner=request.user)
219 | return object_list
220 |
221 | # this is broken in tastypie 0.9.13
222 | # build custom resource_uri (instead of /resource//)
223 | #def get_resource_uri(self, bundle_or_obj, url_name='api_dispatch_list'):
224 | # kwargs = {
225 | # 'resource_name': self._meta.resource_name,
226 | # }
227 | # if isinstance(bundle_or_obj, Bundle):
228 | # kwargs['pk'] = bundle_or_obj.obj.uuid
229 | # else:
230 | # kwargs['pk'] = bundle_or_obj.id
231 | # if self._meta.api_name is not None:
232 | # kwargs['api_name'] = self._meta.api_name
233 | # return self._build_reverse_url('api_dispatch_detail', kwargs = kwargs)
234 |
235 | def obj_create(self, bundle, **kwargs):
236 | # set the owner
237 | kwargs['owner'] = bundle.request.user
238 | return super(CredentialGroupResource, self).obj_create(bundle, **kwargs)
239 |
240 | class CredentialResource(ModelResource):
241 | groups = fields.ToManyField(CredentialGroupResource, 'groups', full=True)
242 |
243 | class Meta:
244 | queryset = Credential.objects.all()
245 | excludes = ('id', )
246 | #list_allowed_methods = ['get']
247 | authentication = AppAuthentication()
248 | authorization = CredentialAuthorization()
249 | resource_name = 'credentials'
250 | pass_request_user_to_django = True
251 | filtering = {
252 | "name": ALL,
253 | "description": ALL,
254 | "url": ALL,
255 | }
256 |
257 | def prepend_urls(self):
258 | return [
259 | url(r"^(?P%s)/(?P[\w\d_.-]+)/$" % self._meta.resource_name,
260 | self.wrap_view('dispatch_detail'), name="api_dispatch_detail"),
261 | ]
262 |
263 | # this is broken in tastypie 0.9.13
264 | # build custom resource_uri (instead of /resource//)
265 | #def get_resource_uri(self, bundle_or_obj, url_name='api_dispatch_list'):
266 | # kwargs = {
267 | # 'resource_name': self._meta.resource_name,
268 | # }
269 | # if isinstance(bundle_or_obj, Bundle):
270 | # kwargs['pk'] = bundle_or_obj.obj.uuid
271 | # else:
272 | # kwargs['pk'] = bundle_or_obj.id
273 | # if self._meta.api_name is not None:
274 | # kwargs['api_name'] = self._meta.api_name
275 | # return self._build_reverse_url('api_dispatch_detail', kwargs = kwargs)
276 |
277 | #def apply_authorization_limits(self, request, object_list):
278 | # return object_list.filter(owner=request.user)
279 |
280 | def dehydrate(self, bundle):
281 | u = bundle.request.user
282 | key = get_user_encryption_key(u.username)
283 | try:
284 | bundle.data['password'] = decrypt(bundle.data['password'],
285 | key)
286 | except:
287 | bundle.data['password'] = None
288 | return bundle
289 |
--------------------------------------------------------------------------------
/vault/templates/vault/group.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 | {% load i18n %}
3 | {% load locksmith %}
4 |
5 | {% block main_content %}
6 | {% if group.owner == request.user %}
7 |
33 |
81 | {% endif %}
82 |
136 |
143 | {{group.name}}
144 | {{group.description|default:""}}
145 |
146 | {% with credentials=group.get_credentials %}
147 | {% if credentials %}
148 |
149 | {% for cred in credentials|dictsort:"name" %}
150 | -
151 |
152 | {% if cred.url %}{% else %}🔒{% endif %}
153 |
154 |
{{cred.name|truncatechars:"15"}}
155 |
156 |
157 |
158 | {% endfor %}
159 |
160 |
161 | {% else %}
162 | {% trans 'No credentials' %}
163 | {% endif %}
164 | {% endwith %}
165 |
268 | {% endblock %}
269 |
--------------------------------------------------------------------------------
/static/js/holder.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Holder - 1.9 - client side image placeholders
4 | (c) 2012-2013 Ivan Malopinsky / http://imsky.co
5 |
6 | Provided under the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0
7 | Commercial use requires attribution.
8 |
9 | */
10 |
11 | var Holder = Holder || {};
12 | (function (app, win) {
13 |
14 | var preempted = false,
15 | fallback = false,
16 | canvas = document.createElement('canvas');
17 |
18 | //getElementsByClassName polyfill
19 | document.getElementsByClassName||(document.getElementsByClassName=function(e){var t=document,n,r,i,s=[];if(t.querySelectorAll)return t.querySelectorAll("."+e);if(t.evaluate){r=".//*[contains(concat(' ', @class, ' '), ' "+e+" ')]",n=t.evaluate(r,t,null,0,null);while(i=n.iterateNext())s.push(i)}else{n=t.getElementsByTagName("*"),r=new RegExp("(^|\\s)"+e+"(\\s|$)");for(i=0;i 1) {
68 | text_height = template.size / (ctx.measureText(text).width / width);
69 | }
70 | //Resetting font size if necessary
71 | ctx.font = "bold " + (text_height * ratio) + "px "+font;
72 | ctx.fillText(text, (width / 2), (height / 2), width);
73 | return canvas.toDataURL("image/png");
74 | }
75 |
76 | function render(mode, el, holder, src) {
77 | var dimensions = holder.dimensions,
78 | theme = holder.theme,
79 | text = holder.text ? decodeURIComponent(holder.text) : holder.text;
80 | var dimensions_caption = dimensions.width + "x" + dimensions.height;
81 | theme = (text ? extend(theme, { text: text }) : theme);
82 | theme = (holder.font ? extend(theme, {font: holder.font}) : theme);
83 |
84 | var ratio = 1;
85 | if(window.devicePixelRatio && window.devicePixelRatio > 1){
86 | ratio = window.devicePixelRatio;
87 | }
88 |
89 | if (mode == "image") {
90 | el.setAttribute("data-src", src);
91 | el.setAttribute("alt", text ? text : theme.text ? theme.text + " [" + dimensions_caption + "]" : dimensions_caption);
92 |
93 | if(fallback || !holder.auto){
94 | el.style.width = dimensions.width + "px";
95 | el.style.height = dimensions.height + "px";
96 | }
97 |
98 | if (fallback) {
99 | el.style.backgroundColor = theme.background;
100 |
101 | }
102 | else{
103 | el.setAttribute("src", draw(ctx, dimensions, theme, ratio));
104 | }
105 | } else {
106 | if (!fallback) {
107 | el.style.backgroundImage = "url(" + draw(ctx, dimensions, theme, ratio) + ")";
108 | el.style.backgroundSize = dimensions.width+"px "+dimensions.height+"px";
109 | }
110 | }
111 | };
112 |
113 | function fluid(el, holder, src) {
114 | var dimensions = holder.dimensions,
115 | theme = holder.theme,
116 | text = holder.text;
117 | var dimensions_caption = dimensions.width + "x" + dimensions.height;
118 | theme = (text ? extend(theme, {
119 | text: text
120 | }) : theme);
121 |
122 | var fluid = document.createElement("div");
123 |
124 | fluid.style.backgroundColor = theme.background;
125 | fluid.style.color = theme.foreground;
126 | fluid.className = el.className + " holderjs-fluid";
127 | fluid.style.width = holder.dimensions.width + (holder.dimensions.width.indexOf("%")>0?"":"px");
128 | fluid.style.height = holder.dimensions.height + (holder.dimensions.height.indexOf("%")>0?"":"px");
129 | fluid.id = el.id;
130 |
131 | el.style.width=0;
132 | el.style.height=0;
133 |
134 | if (theme.text) {
135 | fluid.appendChild(document.createTextNode(theme.text))
136 | } else {
137 | fluid.appendChild(document.createTextNode(dimensions_caption))
138 | fluid_images.push(fluid);
139 | setTimeout(fluid_update, 0);
140 | }
141 |
142 | el.parentNode.insertBefore(fluid, el.nextSibling)
143 |
144 | if(window.jQuery){
145 | jQuery(function($){
146 | $(el).on("load", function(){
147 | el.style.width = fluid.style.width;
148 | el.style.height = fluid.style.height;
149 | $(el).show();
150 | $(fluid).remove();
151 | });
152 | })
153 | }
154 | }
155 |
156 | function fluid_update() {
157 | for (i in fluid_images) {
158 | if(!fluid_images.hasOwnProperty(i)) continue;
159 | var el = fluid_images[i],
160 | label = el.firstChild;
161 |
162 | el.style.lineHeight = el.offsetHeight+"px";
163 | label.data = el.offsetWidth + "x" + el.offsetHeight;
164 | }
165 | }
166 |
167 | function parse_flags(flags, options) {
168 |
169 | var ret = {
170 | theme: settings.themes.gray
171 | }, render = false;
172 |
173 | for (sl = flags.length, j = 0; j < sl; j++) {
174 | var flag = flags[j];
175 | if (app.flags.dimensions.match(flag)) {
176 | render = true;
177 | ret.dimensions = app.flags.dimensions.output(flag);
178 | } else if (app.flags.fluid.match(flag)) {
179 | render = true;
180 | ret.dimensions = app.flags.fluid.output(flag);
181 | ret.fluid = true;
182 | } else if (app.flags.colors.match(flag)) {
183 | ret.theme = app.flags.colors.output(flag);
184 | } else if (options.themes[flag]) {
185 | //If a theme is specified, it will override custom colors
186 | ret.theme = options.themes[flag];
187 | } else if (app.flags.text.match(flag)) {
188 | ret.text = app.flags.text.output(flag);
189 | } else if(app.flags.font.match(flag)){
190 | ret.font = app.flags.font.output(flag);
191 | }
192 | else if(app.flags.auto.match(flag)){
193 | ret.auto = true;
194 | }
195 | }
196 |
197 | return render ? ret : false;
198 |
199 | };
200 |
201 | if (!canvas.getContext) {
202 | fallback = true;
203 | } else {
204 | if (canvas.toDataURL("image/png")
205 | .indexOf("data:image/png") < 0) {
206 | //Android doesn't support data URI
207 | fallback = true;
208 | } else {
209 | var ctx = canvas.getContext("2d");
210 | }
211 | }
212 |
213 | var fluid_images = [];
214 |
215 | var settings = {
216 | domain: "holder.js",
217 | images: "img",
218 | bgnodes: ".holderjs",
219 | themes: {
220 | "gray": {
221 | background: "#eee",
222 | foreground: "#aaa",
223 | size: 12
224 | },
225 | "social": {
226 | background: "#3a5a97",
227 | foreground: "#fff",
228 | size: 12
229 | },
230 | "industrial": {
231 | background: "#434A52",
232 | foreground: "#C2F200",
233 | size: 12
234 | }
235 | },
236 | stylesheet: ".holderjs-fluid {font-size:16px;font-weight:bold;text-align:center;font-family:sans-serif;margin:0}"
237 | };
238 |
239 |
240 | app.flags = {
241 | dimensions: {
242 | regex: /^(\d+)x(\d+)$/,
243 | output: function (val) {
244 | var exec = this.regex.exec(val);
245 | return {
246 | width: +exec[1],
247 | height: +exec[2]
248 | }
249 | }
250 | },
251 | fluid: {
252 | regex: /^([0-9%]+)x([0-9%]+)$/,
253 | output: function (val) {
254 | var exec = this.regex.exec(val);
255 | return {
256 | width: exec[1],
257 | height: exec[2]
258 | }
259 | }
260 | },
261 | colors: {
262 | regex: /#([0-9a-f]{3,})\:#([0-9a-f]{3,})/i,
263 | output: function (val) {
264 | var exec = this.regex.exec(val);
265 | return {
266 | size: settings.themes.gray.size,
267 | foreground: "#" + exec[2],
268 | background: "#" + exec[1]
269 | }
270 | }
271 | },
272 | text: {
273 | regex: /text\:(.*)/,
274 | output: function (val) {
275 | return this.regex.exec(val)[1];
276 | }
277 | },
278 | font: {
279 | regex: /font\:(.*)/,
280 | output: function(val){
281 | return this.regex.exec(val)[1];
282 | }
283 | },
284 | auto: {
285 | regex: /^auto$/
286 | }
287 | }
288 |
289 | for (var flag in app.flags) {
290 | if(!app.flags.hasOwnProperty(flag)) continue;
291 | app.flags[flag].match = function (val) {
292 | return val.match(this.regex)
293 | }
294 | }
295 |
296 | app.add_theme = function (name, theme) {
297 | name != null && theme != null && (settings.themes[name] = theme);
298 | return app;
299 | };
300 |
301 | app.add_image = function (src, el) {
302 | var node = selector(el);
303 | if (node.length) {
304 | for (var i = 0, l = node.length; i < l; i++) {
305 | var img = document.createElement("img")
306 | img.setAttribute("data-src", src);
307 | node[i].appendChild(img);
308 | }
309 | }
310 | return app;
311 | };
312 |
313 | app.run = function (o) {
314 | var options = extend(settings, o), images = [];
315 |
316 | if(options.images instanceof window.NodeList){
317 | imageNodes = options.images;
318 | }
319 | else if(options.images instanceof window.Node){
320 | imageNodes = [options.images];
321 | }
322 | else{
323 | imageNodes = selector(options.images);
324 | }
325 |
326 | if(options.elements instanceof window.NodeList){
327 | bgnodes = options.bgnodes;
328 | }
329 | else if(options.bgnodes instanceof window.Node){
330 | bgnodes = [options.bgnodes];
331 | }
332 | else{
333 | bgnodes = selector(options.bgnodes);
334 | }
335 |
336 | preempted = true;
337 |
338 | for (i = 0, l = imageNodes.length; i < l; i++) images.push(imageNodes[i]);
339 |
340 | var holdercss = document.getElementById("holderjs-style");
341 |
342 | if(!holdercss){
343 | holdercss = document.createElement("style");
344 | holdercss.setAttribute("id", "holderjs-style");
345 | holdercss.type = "text/css";
346 | document.getElementsByTagName("head")[0].appendChild(holdercss);
347 | }
348 |
349 | if(holdercss.styleSheet){
350 | holdercss.styleSheet += options.stylesheet;
351 | }
352 | else{
353 | holdercss.textContent+= options.stylesheet;
354 | }
355 |
356 | var cssregex = new RegExp(options.domain + "\/(.*?)\"?\\)");
357 |
358 | for (var l = bgnodes.length, i = 0; i < l; i++) {
359 | var src = window.getComputedStyle(bgnodes[i], null)
360 | .getPropertyValue("background-image");
361 | var flags = src.match(cssregex);
362 | if (flags) {
363 | var holder = parse_flags(flags[1].split("/"), options);
364 | if (holder) {
365 | render("background", bgnodes[i], holder, src);
366 | }
367 | }
368 | }
369 |
370 | for (var l = images.length, i = 0; i < l; i++) {
371 | var src = images[i].getAttribute("src") || images[i].getAttribute("data-src");
372 | if (src != null && src.indexOf(options.domain) >= 0) {
373 | var holder = parse_flags(src.substr(src.lastIndexOf(options.domain) + options.domain.length + 1)
374 | .split("/"), options);
375 | if (holder) {
376 | if (holder.fluid) {
377 | fluid(images[i], holder, src);
378 | } else {
379 | render("image", images[i], holder, src);
380 | }
381 | }
382 | }
383 | }
384 | return app;
385 | };
386 |
387 | contentLoaded(win, function () {
388 | if (window.addEventListener) {
389 | window.addEventListener("resize", fluid_update, false);
390 | window.addEventListener("orientationchange", fluid_update, false);
391 | } else {
392 | window.attachEvent("onresize", fluid_update)
393 | }
394 | preempted || app.run();
395 | });
396 |
397 | if ( typeof define === "function" && define.amd ) {
398 | define( "Holder", [], function () { return app; } );
399 | }
400 |
401 | })(Holder, window);
402 |
--------------------------------------------------------------------------------
/locksmith/settings.py:
--------------------------------------------------------------------------------
1 | # Copyright 2013 Evan Hazlett and contributors
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | # Django settings for locksmith project.
15 | import os
16 | import subprocess
17 | PROJECT_ROOT = os.path.join(os.path.dirname(__file__), '../')
18 |
19 | DEBUG = True
20 | TEMPLATE_DEBUG = DEBUG
21 |
22 | APP_NAME = 'locksmith'
23 | # get latest git revision
24 | process = subprocess.Popen(['git', 'rev-parse', 'HEAD'], stdout=subprocess.PIPE)
25 | out, err = process.communicate()
26 |
27 | APP_REVISION = out[:6]
28 | ADMINS = (
29 | ('Evan Hazlett', 'ejhazlett@gmail.com'),
30 | )
31 | ADMIN_EMAIL = 'support@vitasso.com'
32 |
33 | AUTH_PROFILE_MODULE = 'accounts.UserProfile'
34 | MANAGERS = ADMINS
35 |
36 | BCRYPT_ENABLED = True
37 | BCRYPT_ROUNDS = 12
38 | BCRYPT_MIGRATE = True
39 |
40 | SENTRY_DSN = ''
41 | SIGNUP_ENABLED = True
42 |
43 | AWS_ACCESS_KEY_ID = ''
44 | AWS_SECRET_ACCESS_KEY = ''
45 | AWS_STORAGE_BUCKET_NAME = 'locksmith'
46 |
47 | CACHE_ENCRYPTION_KEY = '{0}:key'
48 |
49 |
50 | # arcus cloud settings
51 | if 'VCAP_SERVICES' in os.environ:
52 | import json
53 | vcap_services = json.loads(os.environ['VCAP_SERVICES'])
54 | mysql_srv = vcap_services['mysql-5.1'][0]
55 | redis_srv = vcap_services['redis-2.6'][0]
56 | memcached_srv = vcap_services['memcached-1.4'][0]
57 | elasticsearch_srv = vcap_services['elasticsearch-0.19'][0]
58 | mysql_creds = mysql_srv['credentials']
59 | redis_creds = redis_srv['credentials']
60 | memcached_creds = memcached_srv['credentials']
61 | DATABASES = {
62 | 'default': {
63 | 'ENGINE': 'django.db.backends.mysql',
64 | 'NAME': mysql_creds['name'],
65 | 'USER': mysql_creds['user'],
66 | 'PASSWORD': mysql_creds['password'],
67 | 'HOST': mysql_creds['hostname'],
68 | 'PORT': mysql_creds['port'],
69 | }
70 | }
71 | CACHES = {
72 | 'default': {
73 | 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
74 | 'LOCATION': '{0}:{1}'.format(memcached_creds['host'],
75 | memcached_creds['port']),
76 | }
77 | }
78 | REDIS_HOST = redis_creds['host']
79 | REDIS_PORT = redis_creds['port']
80 | REDIS_DB = 0
81 | REDIS_PASSWORD = redis_creds['password']
82 | RQ_QUEUES = {
83 | 'default': {
84 | 'HOST': REDIS_HOST,
85 | 'PORT': REDIS_PORT,
86 | 'DB': REDIS_DB,
87 | 'PASSWORD': REDIS_PASSWORD,
88 | }
89 | }
90 | else:
91 | DATABASES = {
92 | 'default': {
93 | 'ENGINE': 'django.db.backends.sqlite3',
94 | 'NAME': 'locksmith.db',
95 | 'USER': '',
96 | 'PASSWORD': '',
97 | 'HOST': '',
98 | 'PORT': '',
99 | }
100 | }
101 | CACHES = {
102 | 'default': {
103 | 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
104 | }
105 | }
106 | REDIS_HOST = '127.0.0.1'
107 | REDIS_PORT = 6739
108 | REDIS_DB = 0
109 | REDIS_PASSWORD = None
110 | RQ_QUEUES = {
111 | 'default': {
112 | 'HOST': REDIS_HOST,
113 | 'PORT': REDIS_PORT,
114 | 'DB': REDIS_DB,
115 | 'PASSWORD': REDIS_PASSWORD,
116 | }
117 | }
118 |
119 | SESSION_EXPIRE_AT_BROWSER_CLOSE = True
120 |
121 | GOOGLE_ANALYTICS_CODE = ''
122 | INTERCOM_APP_ID = ''
123 | STRIPE_API_KEY = ''
124 | STRIPE_PUBLISHABLE_KEY = ''
125 | ACCOUNT_PLAN = 'locksmith-pro' # stripe plan
126 |
127 | # auth backends
128 | AUTHENTICATION_BACKENDS = (
129 | 'social_auth.backends.twitter.TwitterBackend',
130 | 'social_auth.backends.google.GoogleOAuth2Backend',
131 | 'social_auth.backends.contrib.github.GithubBackend',
132 | 'django.contrib.auth.backends.ModelBackend',
133 | )
134 | # these are placeholders ; set in local_settings.py to deploy
135 | TWITTER_CONSUMER_KEY = ''
136 | TWITTER_CONSUMER_SECRET = ''
137 | FACEBOOK_APP_ID = ''
138 | FACEBOOK_API_SECRET = ''
139 | LINKEDIN_CONSUMER_KEY = ''
140 | LINKEDIN_CONSUMER_SECRET = ''
141 | ORKUT_CONSUMER_KEY = ''
142 | ORKUT_CONSUMER_SECRET = ''
143 | GOOGLE_CONSUMER_KEY = ''
144 | GOOGLE_CONSUMER_SECRET = ''
145 | GOOGLE_OAUTH2_CLIENT_ID = ''
146 | GOOGLE_OAUTH2_CLIENT_SECRET = ''
147 | FOURSQUARE_CONSUMER_KEY = ''
148 | FOURSQUARE_CONSUMER_SECRET = ''
149 | VK_APP_ID = ''
150 | VK_API_SECRET = ''
151 | LIVE_CLIENT_ID = ''
152 | LIVE_CLIENT_SECRET = ''
153 | SKYROCK_CONSUMER_KEY = ''
154 | SKYROCK_CONSUMER_SECRET = ''
155 | YAHOO_CONSUMER_KEY = ''
156 | YAHOO_CONSUMER_SECRET = ''
157 | READABILITY_CONSUMER_SECRET = ''
158 | READABILITY_CONSUMER_SECRET = ''
159 | GITHUB_APP_ID = ''
160 | GITHUB_API_SECRET = ''
161 | GITHUB_EXTENDED_PERMISSIONS = ['user', 'user:email']
162 |
163 | # more social auth settings
164 | #LOGIN_URL = '/login-form/'
165 | LOGIN_URL = '/accounts/login/'
166 | LOGIN_REDIRECT_URL = '/'
167 | LOGIN_ERROR_URL = '/login-error/'
168 | SOCIAL_AUTH_COMPLETE_URL_NAME = 'socialauth_complete'
169 | SOCIAL_AUTH_ASSOCIATE_URL_NAME = 'socialauth_associate_complete'
170 | # needed due to InnoDB storage restriction
171 | # ideally we'd use Postgres, but it has issues in Arcus Cloud at the moment
172 | # see https://github.com/omab/django-social-auth/issues/539 for details
173 | SOCIAL_AUTH_UID_LENGTH = 222
174 | SOCIAL_AUTH_NONCE_SERVER_URL_LENGTH = 200
175 | SOCIAL_AUTH_ASSOCIATION_SERVER_URL_LENGTH = 135
176 | SOCIAL_AUTH_ASSOCIATION_HANDLE_LENGTH = 125
177 |
178 |
179 | # Local time zone for this installation. Choices can be found here:
180 | # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
181 | # although not all choices may be available on all operating systems.
182 | # In a Windows environment this must be set to your system time zone.
183 | TIME_ZONE = 'America/New_York'
184 |
185 | # Language code for this installation. All choices can be found here:
186 | # http://www.i18nguy.com/unicode/language-identifiers.html
187 | LANGUAGE_CODE = 'en-us'
188 |
189 | SITE_ID = 1
190 |
191 | # If you set this to False, Django will make some optimizations so as not
192 | # to load the internationalization machinery.
193 | USE_I18N = True
194 |
195 | # If you set this to False, Django will not format dates, numbers and
196 | # calendars according to the current locale.
197 | USE_L10N = True
198 |
199 | # If you set this to False, Django will not use timezone-aware datetimes.
200 | USE_TZ = True
201 |
202 | # Absolute filesystem path to the directory that will hold user-uploaded files.
203 | # Example: "/home/media/media.lawrence.com/media/"
204 | MEDIA_ROOT = ''
205 |
206 | # URL that handles the media served from MEDIA_ROOT. Make sure to use a
207 | # trailing slash.
208 | # Examples: "http://media.lawrence.com/media/", "http://example.com/media/"
209 | MEDIA_URL = ''
210 |
211 | # Absolute path to the directory static files should be collected to.
212 | # Don't put anything in this directory yourself; store your static files
213 | # in apps' "static/" subdirectories and in STATICFILES_DIRS.
214 | # Example: "/home/media/media.lawrence.com/static/"
215 | STATIC_ROOT = ''
216 |
217 | # URL prefix for static files.
218 | # Example: "http://media.lawrence.com/static/"
219 | STATIC_URL = '/static/'
220 |
221 | # Additional locations of static files
222 | STATICFILES_DIRS = (
223 | os.path.join(PROJECT_ROOT, 'static'),
224 | )
225 |
226 | # List of finder classes that know how to find static files in
227 | # various locations.
228 | STATICFILES_FINDERS = (
229 | 'django.contrib.staticfiles.finders.FileSystemFinder',
230 | 'django.contrib.staticfiles.finders.AppDirectoriesFinder',
231 | # 'django.contrib.staticfiles.finders.DefaultStorageFinder',
232 | )
233 |
234 | # Make this unique, and don't share it with anybody.
235 | SECRET_KEY = 'z)h81*4eitd6k=8%&i164h0fukf3p(fe8cpo*g&vc2h@n8aba%'
236 |
237 | # List of callables that know how to import templates from various sources.
238 | TEMPLATE_LOADERS = (
239 | 'django.template.loaders.filesystem.Loader',
240 | 'django.template.loaders.app_directories.Loader',
241 | # 'django.template.loaders.eggs.Loader',
242 | )
243 | TEMPLATE_CONTEXT_PROCESSORS = (
244 | "django.contrib.auth.context_processors.auth",
245 | "django.core.context_processors.debug",
246 | "django.core.context_processors.i18n",
247 | "django.core.context_processors.media",
248 | "django.core.context_processors.static",
249 | "django.core.context_processors.request",
250 | "django.core.context_processors.tz",
251 | "django.contrib.messages.context_processors.messages",
252 | "locksmith.context_processors.app_info",
253 | "locksmith.context_processors.google_analytics_code",
254 | "locksmith.context_processors.encryption_key",
255 | "locksmith.context_processors.intercom_app_id",
256 | "locksmith.context_processors.signup_enabled",
257 | "locksmith.context_processors.stripe_info",
258 | 'social_auth.context_processors.social_auth_by_name_backends',
259 | 'social_auth.context_processors.social_auth_backends',
260 | 'social_auth.context_processors.social_auth_login_redirect',
261 | )
262 |
263 | MIDDLEWARE_CLASSES = (
264 | 'django.middleware.common.CommonMiddleware',
265 | 'django.contrib.sessions.middleware.SessionMiddleware',
266 | 'django.middleware.csrf.CsrfViewMiddleware',
267 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
268 | 'django.contrib.messages.middleware.MessageMiddleware',
269 | 'locksmith.middleware.threadlocal.ThreadLocalMiddleware',
270 | # Uncomment the next line for simple clickjacking protection:
271 | # 'django.middleware.clickjacking.XFrameOptionsMiddleware',
272 | )
273 |
274 | ROOT_URLCONF = 'locksmith.urls'
275 |
276 | # Python dotted path to the WSGI application used by Django's runserver.
277 | WSGI_APPLICATION = 'locksmith.wsgi.application'
278 |
279 | TEMPLATE_DIRS = (
280 | os.path.join(PROJECT_ROOT, 'templates'),
281 | )
282 |
283 | INSTALLED_APPS = (
284 | 'django.contrib.auth',
285 | 'django.contrib.contenttypes',
286 | 'django.contrib.sessions',
287 | 'django.contrib.sites',
288 | 'django.contrib.messages',
289 | 'django.contrib.staticfiles',
290 | 'django.contrib.admin',
291 | 'social_auth',
292 | 'django_forms_bootstrap',
293 | 'south',
294 | 'tastypie',
295 | 'django_bcrypt',
296 | 'locksmith',
297 | 'accounts',
298 | 'vault',
299 | )
300 |
301 | # A sample logging configuration. The only tangible logging
302 | # performed by this configuration is to send an email to
303 | # the site admins on every HTTP 500 error when DEBUG=False.
304 | # See http://docs.djangoproject.com/en/dev/topics/logging for
305 | # more details on how to customize your logging configuration.
306 | LOGGING = {
307 | 'version': 1,
308 | 'disable_existing_loggers': False,
309 | 'root': {
310 | 'level': 'WARNING',
311 | 'handlers': ['console', 'sentry'],
312 | },
313 | 'formatters': {
314 | 'verbose': {
315 | 'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
316 | },
317 | },
318 | 'filters': {
319 | 'require_debug_false': {
320 | '()': 'django.utils.log.RequireDebugFalse'
321 | }
322 | },
323 | 'handlers': {
324 | 'console': {
325 | 'level': 'DEBUG',
326 | 'class': 'logging.StreamHandler',
327 | 'formatter': 'verbose'
328 | },
329 | 'sentry': {
330 | 'level': 'ERROR',
331 | 'class': 'raven.contrib.django.raven_compat.handlers.SentryHandler',
332 | },
333 | 'mail_admins': {
334 | 'level': 'ERROR',
335 | 'filters': ['require_debug_false'],
336 | 'class': 'django.utils.log.AdminEmailHandler'
337 | }
338 | },
339 | 'loggers': {
340 | 'django.request': {
341 | 'handlers': ['mail_admins'],
342 | 'level': 'ERROR',
343 | 'propagate': True,
344 | },
345 | 'raven': {
346 | 'level': 'DEBUG',
347 | 'handlers': ['console'],
348 | 'propagate': False,
349 | },
350 | 'sentry.errors': {
351 | 'level': 'DEBUG',
352 | 'handlers': ['console'],
353 | 'propagate': False,
354 | },
355 | }
356 | }
357 |
358 | try:
359 | from local_settings import *
360 | except ImportError:
361 | pass
362 |
363 | if AWS_ACCESS_KEY_ID:
364 | STATICFILES_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
365 | DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
366 |
367 | # these must come after the above local_settings import in order to
368 | # check for values in local_settings
369 | if SENTRY_DSN:
370 | INSTALLED_APPS = INSTALLED_APPS + (
371 | 'raven.contrib.django.raven_compat',
372 | )
373 | MIDDLEWARE_CLASSES = MIDDLEWARE_CLASSES + (
374 | 'raven.contrib.django.raven_compat.middleware.SentryResponseErrorIdMiddleware',
375 | )
376 |
377 |
--------------------------------------------------------------------------------
/static/css/bootstrap-responsive.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap Responsive v2.3.0
3 | *
4 | * Copyright 2012 Twitter, Inc
5 | * Licensed under the Apache License v2.0
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Designed and built with all the love in the world @twitter by @mdo and @fat.
9 | */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}@-ms-viewport{width:device-width}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}.visible-desktop{display:inherit!important}@media(min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}}@media(max-width:767px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-phone{display:inherit!important}.hidden-phone{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:inherit!important}.hidden-print{display:none!important}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:30px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.564102564102564%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%}.row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%}.row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%}.row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%}.row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%}.row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%}.row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%}.row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%}.row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%}.row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%}.row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%}.row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%}.row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%}.row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%}.row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%}.row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%}.row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%}.row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%}.row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%}.row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%}.row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%}.row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%}.row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%}.row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%}.row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%}.row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%}.row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%}.row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%}.row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%}.row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%}.row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%}.row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%}.row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%}.row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%}.row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:30px}input.span12,textarea.span12,.uneditable-input.span12{width:1156px}input.span11,textarea.span11,.uneditable-input.span11{width:1056px}input.span10,textarea.span10,.uneditable-input.span10{width:956px}input.span9,textarea.span9,.uneditable-input.span9{width:856px}input.span8,textarea.span8,.uneditable-input.span8{width:756px}input.span7,textarea.span7,.uneditable-input.span7{width:656px}input.span6,textarea.span6,.uneditable-input.span6{width:556px}input.span5,textarea.span5,.uneditable-input.span5{width:456px}input.span4,textarea.span4,.uneditable-input.span4{width:356px}input.span3,textarea.span3,.uneditable-input.span3{width:256px}input.span2,textarea.span2,.uneditable-input.span2{width:156px}input.span1,textarea.span1,.uneditable-input.span1{width:56px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.7624309392265194%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%}.row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%}.row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%}.row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%}.row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%}.row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%}.row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%}.row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%}.row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%}.row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%}.row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%}.row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%}.row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%}.row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%}.row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%}.row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%}.row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%}.row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%}.row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%}.row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%}.row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%}.row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%}.row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%}.row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%}.row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%}.row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%}.row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%}.row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%}.row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%}.row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%}.row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%}.row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%}.row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%}.row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%}.row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:710px}input.span11,textarea.span11,.uneditable-input.span11{width:648px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:524px}input.span8,textarea.span8,.uneditable-input.span8{width:462px}input.span7,textarea.span7,.uneditable-input.span7{width:400px}input.span6,textarea.span6,.uneditable-input.span6{width:338px}input.span5,textarea.span5,.uneditable-input.span5{width:276px}input.span4,textarea.span4,.uneditable-input.span4{width:214px}input.span3,textarea.span3,.uneditable-input.span3{width:152px}input.span2,textarea.span2,.uneditable-input.span2{width:90px}input.span1,textarea.span1,.uneditable-input.span1{width:28px}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom,.navbar-static-top{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}.thumbnails>li{float:none;margin-left:0}[class*="span"],.uneditable-input[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:100%;margin-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.span12,.row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="offset"]:first-child{margin-left:0}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}.controls-row [class*="span"]+[class*="span"]{margin-left:0}.modal{position:fixed;top:20px;right:20px;left:20px;width:auto;margin:0}.modal.fade{top:-100px}.modal.fade.in{top:20px}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:20px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.media .pull-left,.media .pull-right{display:block;float:none;margin-bottom:10px}.media-object{margin-right:0;margin-left:0}.modal{top:10px;right:10px;left:10px}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:20px}.navbar-fixed-bottom{margin-top:20px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 10px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#777;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .nav>li>a:focus,.nav-collapse .dropdown-menu a:hover,.nav-collapse .dropdown-menu a:focus{background-color:#f2f2f2}.navbar-inverse .nav-collapse .nav>li>a,.navbar-inverse .nav-collapse .dropdown-menu a{color:#999}.navbar-inverse .nav-collapse .nav>li>a:hover,.navbar-inverse .nav-collapse .nav>li>a:focus,.navbar-inverse .nav-collapse .dropdown-menu a:hover,.navbar-inverse .nav-collapse .dropdown-menu a:focus{background-color:#111}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:none;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .open>.dropdown-menu{display:block}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .nav>li>.dropdown-menu:before,.nav-collapse .nav>li>.dropdown-menu:after{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar-inverse .nav-collapse .navbar-form,.navbar-inverse .nav-collapse .navbar-search{border-top-color:#111;border-bottom-color:#111}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}}
10 |
--------------------------------------------------------------------------------