Commit 90203986 authored by Sunil Mohan Adapa's avatar Sunil Mohan Adapa

Remove modules 'expert_mode' and 'lib'.

The login/logout URLs are now in the 'users' module.
parent 3abc5e92
......@@ -22,7 +22,6 @@ otherwise.
- doc/Makefile :: -
- doc/modules.mdwn :: -
- doc/scripts.mdwn :: -
- doc/security.mdwn :: -
- doc/themes.mdwn :: -
- static/themes/default/FreedomBox-Identity-Manual.pdf :: -
- static/themes/default/FreedomBox-Logo.7z :: [[http://thread.gmane.org/gmane.linux.debian.freedombox.user/4124/focus=4439][GPL3+/CC-BY-SA]]
......
......@@ -30,13 +30,6 @@ to get newer, better shinier functions. The modules will
automatically integrate into the existing menu system so you can
control all of the box's parts from one central location.
The interface will eventually have a 'basic' and an 'expert' mode. In
basic mode, much of Plinth's configuration and capability are hidden.
Sane defaults are chosen whenever possible. In expert mode, you can
get down into the details and configure things the average user never
thinks about. For example, experts can turn off ntp or switch ntp
servers. Basic users should never even know those options exist.
## Getting Started
See the INSTALL file for additional details and dependencies. To install run:
......
......@@ -6,7 +6,7 @@ PDFLATEX=pdflatex
# List text files in the order in which you want them to appear in the
# complete manual:
SOURCES=README.mdwn INSTALL.mdwn themes.mdwn hacking.mdwn TODO.mdwn modules.mdwn scripts.mdwn security.mdwn faq.mdwn COPYING.mdwn colophon.mdwn
SOURCES=README.mdwn INSTALL.mdwn themes.mdwn hacking.mdwn TODO.mdwn modules.mdwn scripts.mdwn faq.mdwn COPYING.mdwn colophon.mdwn
OTHER=
PYTHON_SOURCES:=$(shell find .. -name \*.py)
TODO_SOURCES=$(patsubst TODO.mdwn,,$(SOURCES)) $(PYTHON_SOURCES)
......
......@@ -2,7 +2,7 @@
Almost all of the front end's functionality is contained in small,
separate modules that reside in the directory tree beneath
`/modules/installed`. Some are installed by default, some are
`plinth/modules`. Some are installed by default, some are
required for operation, and some are entirely optional.
## Installing and Loading Modules
......@@ -12,45 +12,15 @@ installation is as simple as `aptitude install freedombox-foo`. As an
intermediate step, we'll start scripting module installation. But
until then, we can install them manually.
Modules are installed by copying them into the tree beneath the
`/modules/installed` directory. They are activated by linking their
.py files into the modules directory. (Freedom Box will not load
modules unless they are symlinks.) If the module provides other
resources, they can be linked from wherever in the working tree they
need to be. So if a module provides a template, that template can be
linked from `/templates`.
Modules can be organized into subdirectories beneath the installed
directory. As an initial matter, I've made separate directories for
each major tab. Modules that extend the functionality of the Freedom
Box code base (as opposed to being user-visible interface modules
under specific major tabs) go in `modules/installed/lib`. This
convention can be adjusted or ignored as needed. In fact, modules can
be installed anywhere in the file system as long as the symlinks are
in `/modules`.
The names of the symlinks in the `modules` directory can be arbitrary,
and if a name is already taken, (e.g. if `router/info.py` and
`apps/info.p`y both exist), append a counter to the end (e.g. link
`info.py` to `router/info.py` and `info1.py` to `apps/info.py`).
If a module cannot be imported for any reason (e.g. it's a dead
symlink), freedombox.py will log an error but will otherwise just keep
going.
A module can be represented inside of Plinth via a django app.
To enable a module, put a file containing its python path into
`data/etc/plinth/modules-enabled/`. Django is configured to use all
modules of the `modules-enabled` folder as apps. With the python path
you can also include modules/apps outside of plinth.
TODO: automatically prune dead links to clear out old module installs.
This is something the install scripts should do.
## Pluggable Module Structure
Plugin interfaces are contained in `plugin_mount.py`. Each interface
is a metaclass. Classes that implement a given interface should
inherit the metaclass and then provide the indicated properties. New
metaclasses can be created to make new classes of plugins.
Any place that might be affected by arbitrary addition of modules
should have its own metaclass.
## Coding Practices for Modules
All the
......
# Security
## Password Storage
Here is an overview of how user passwords are currently being stored in Plinth.
### Storing a password (add_user function in auth module):
1. We check if the username or password is empty. If so, return an error message.
2. Use bcrypt (from passlib) to hash the password with a random salt. bcrypt returns the hash in the format:
$2a$<base 10 number of rounds>$<22-character salt><31-character checksum>
This hashed string will be used in step 5.
3. If the password length is over 4096, bcrypt raises an exception. We catch this exception and return an error message.
4. Check if the username exists in user store. If so, return an error message.
5. If no error has occurred so far, create the new user. The username, hashed password, and salt are stored in the user store database. The salt is a substring of the hash output by bcrypt.
### Checking password at login (check_credentials function in auth module):
1. We check if the username or password is empty. If so, return an error message.
2. Use bcrypt to hash the supplied password. This step is performed regardless of whether the user already exists. If the user exists, use the salt value stored for that user in the database, otherwise, a random salt is used.
3. If the password length is over 4096, bcrypt raises an exception. We catch this exception and return an error message.
4. Check if the user doesn't exist, or if the hashed password doesn't match the stored hash. Return an error message "Bad user-name or password" if either of these conditions are true.
5. If no error has occurred so far, return None to indicate that the supplied credentials are valid.
......@@ -198,9 +198,9 @@ def configure_django():
DEBUG=cfg.debug,
INSTALLED_APPS=applications,
LOGGING=logging_configuration,
LOGIN_URL='lib:login',
LOGIN_URL='users:login',
LOGIN_REDIRECT_URL='apps:index',
LOGOUT_URL='lib:logout',
LOGOUT_URL='users:logout',
MIDDLEWARE_CLASSES=(
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
......
......@@ -105,8 +105,7 @@ def index(request):
form = None
is_expert = request.user.groups.filter(name='Expert').exists()
if request.method == 'POST' and is_expert:
if request.method == 'POST':
form = ConfigurationForm(request.POST, prefix='configuration')
# pylint: disable-msg=E1101
if form.is_valid():
......@@ -119,8 +118,7 @@ def index(request):
return TemplateResponse(request, 'config.html',
{'title': _('General Configuration'),
'form': form,
'is_expert': is_expert})
'form': form})
def get_status():
......
......@@ -22,7 +22,6 @@
{% block content %}
{% if is_expert %}
<form class="form" method="post">
{% csrf_token %}
......@@ -33,11 +32,5 @@
</form>
{% else %}
<p>Only members of the expert group are allowed to see and modify
the system setup.</p>
{% endif %}
{% endblock %}
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
"""
Plinth module for expert mode configuration
"""
from . import expert_mode
from .expert_mode import init
__all__ = ['expert_mode', 'init']
depends = ['plinth.modules.system']
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from django import forms
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.template.response import TemplateResponse
from gettext import gettext as _
from plinth import cfg
from plinth.modules.lib.auth import get_or_create_group
class ExpertsForm(forms.Form): # pylint: disable-msg=W0232
"""Form to configure expert mode"""
expert_mode = forms.BooleanField(
label=_('Enable Expert Mode'), required=False)
def init():
"""Initialize the module"""
menu = cfg.main_menu.get('system:index')
menu.add_urlname(_('Expert Mode'), 'glyphicon-wrench',
'expert_mode:index', 10)
@login_required
def index(request):
"""Serve the configuration form"""
status = get_status(request)
form = None
if request.method == 'POST':
form = ExpertsForm(request.POST, prefix='experts')
# pylint: disable-msg=E1101
if form.is_valid():
_apply_changes(request, form.cleaned_data)
status = get_status(request)
form = ExpertsForm(initial=status, prefix='experts')
else:
form = ExpertsForm(initial=status, prefix='experts')
return TemplateResponse(request, 'expert_mode.html',
{'title': _('Expert Mode'),
'form': form})
def get_status(request):
"""Return the current status"""
return {'expert_mode': request.user.groups.filter(name='Expert').exists()}
def _apply_changes(request, new_status):
"""Apply expert mode configuration"""
message = (messages.info, _('Settings unchanged'))
expert_group = get_or_create_group('Expert')
if new_status['expert_mode']:
if expert_group not in request.user.groups.all():
request.user.groups.add(expert_group)
message = (messages.success, _('Expert mode enabled'))
else:
if expert_group in request.user.groups.all():
request.user.groups.remove(expert_group)
message = (messages.success, _('Expert mode disabled'))
message[0](request, message[1])
{% extends "base.html" %}
{% comment %}
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
{% endcomment %}
{% load bootstrap %}
{% block content %}
<h2>{{ title }}</h2>
<p>The {{ cfg.box_name }} can be administered in two modes, 'basic'
and 'expert'. Basic mode hides a lot of features and configuration
options that most users will never need to think about. Expert mode
allows you to get into the details.</p>
<p>Most users can operate the {{ cfg.box_name }} by configuring the
limited number of options visible in Basic mode. For the sake of
simplicity and ease of use, we hid most of {{ cfg.product_name }}'s
less frequently used options. But if you want more sophisticated
features, you can enable Expert mode, and {{ cfg.product_name }}
will present more advanced menu options.</p>
<p>You should be aware that it might be possible to render your
{{ cfg.box_name }} inaccessible via Expert mode options.</p>
<form class="form" method="post">
{% csrf_token %}
{{ form|bootstrap }}
<input type="submit" class="btn btn-primary" value="Submit"/>
</form>
{% endblock %}
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
"""
URLs for the Expert Mode module
"""
from django.conf.urls import patterns, url
urlpatterns = patterns( # pylint: disable-msg=C0103
'plinth.modules.expert_mode.expert_mode',
url(r'^sys/expert/$', 'index', name='index'),
)
......@@ -56,7 +56,6 @@ than 63 characters in length.'),
config.set_hostname(self.cleaned_data['hostname'])
user = super(State0Form, self).save(commit=False)
user.set_password(self.cleaned_data['password'])
user.is_expert = True
if commit:
user.save()
self.login_user()
......
......@@ -25,7 +25,7 @@
{% if user.is_authenticated %}
<p>Have fun with your {{ cfg.box_name }}!</p>
{% else %}
<p>Proceed to <a href="{% url 'lib:login' %}">login</a>.</p>
<p>Proceed to <a href="{% url 'users:login' %}">login</a>.</p>
{% endif %}
{% endblock %}
......
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
"""
Plinth library modules
"""
from . import auth
__all__ = ['auth']
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from django.contrib.auth.models import Group, User
def add_user(username, passphrase, name='', email='', expert=False):
"""Add a new user with specified username and passphrase"""
if not username:
return 'Must specify a username!'
if not passphrase:
return 'Must specify a passphrase!'
user = User.objects.create_user(username, email=email,
password=passphrase)
user.first_name = name
user.save()
if expert:
user.groups.add(get_or_create_group('Expert'))
def get_or_create_group(name):
"""Return an existing or newly created group with given name"""
try:
group = Group.objects.get(name__exact=name)
except Group.DoesNotExist:
group = Group(name=name)
group.save()
return group
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
"""
URLs for the Lib module
"""
from django.conf.urls import patterns, url
from django.core.urlresolvers import reverse_lazy
urlpatterns = patterns( # pylint: disable-msg=C0103
'',
url(r'^accounts/login/$', 'django.contrib.auth.views.login',
{'template_name': 'login.html'}, name='login'),
url(r'^accounts/logout/$', 'django.contrib.auth.views.logout',
{'next_page': reverse_lazy('index')}, name='logout')
)
......@@ -21,6 +21,7 @@ URLs for the Users module
from django.conf.urls import patterns, url
from django.contrib.auth.decorators import login_required
from django.core.urlresolvers import reverse_lazy
from . import views
......@@ -38,4 +39,9 @@ urlpatterns = patterns(
url(r'^sys/users/(?P<slug>[\w.@+-]+)/change_password/$',
login_required(views.UserChangePassword.as_view()),
name='change_password'),
# add djangos login/logout urls
url(r'^accounts/login/$', 'django.contrib.auth.views.login',
{'template_name': 'login.html'}, name='login'),
url(r'^accounts/logout/$', 'django.contrib.auth.views.logout',
{'next_page': reverse_lazy('index')}, name='logout'),
)
......@@ -110,14 +110,14 @@
<p class="navbar-text pull-right">
<i class="glyphicon glyphicon-user nav-icon"></i>
Logged in as <a href="#">{{ user.username }}</a>.
<a href="{% url 'lib:logout' %}" title="Log out">
<a href="{% url 'users:logout' %}" title="Log out">
Log out</a>.
</p>
{% else %}
<p class="navbar-text pull-right">
Not logged in.
<i class="glyphicon glyphicon-user nav-icon"></i>
<a href="{% url 'lib:login' %}" title="Log in">
<a href="{% url 'users:login' %}" title="Log in">
Log in</a>.
</p>
{% endif %}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment