Skip to content

Commit

Permalink
Release version 1.3
Browse files Browse the repository at this point in the history
  • Loading branch information
aubincleme committed Sep 29, 2017
2 parents 661a7b0 + 8d22ed3 commit ff752a4
Show file tree
Hide file tree
Showing 32 changed files with 408 additions and 269 deletions.
4 changes: 4 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[flake8]
max-line-length = 120
statistics = True
exclude = venv,migrations,members/settings.py
7 changes: 7 additions & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ code-coverage-report:
- coverage run manage.py test
- coverage report --include="accounts/*,network/*,cleaning/*"

pep8-syntax:
stage: test
except:
- master
script:
- flake8

deploy_dev:
stage: deploy
environment:
Expand Down
62 changes: 62 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
Contributing
===

Hi there ! Welcome to the contribution documentation of this project !

Developing on members.atilla.org is fairly easy compared to many other Open Source projects, but in order to keep a clean organization and a clean code base, here are some informations and guidelines that we encourage you to follow in your development process.

Project Licensing
---

This project is using a [MIT license](https://opensource.org/licenses/MIT) registered in the name of the Association ATILLA. Therefore, every code addition made on this project repository should agree with this license and, by doing so, the original code author (which can be you !) agrees that he can’t be held liable for the work he has done on the platform (for more resources, see https://tldrlegal.com/license/mit-license).

Issues management
---

Every code added, modified or deleted on the platform should be linked to a GitLab issue. This allows every developer on the project to be kept informed of new features, bugs or proposals.

Please don’t use issues to discuss about a code change you made ; discussions revolving around a patch must be contained in the associated merge request.

Branching model
---

The project is composed of 3 main branches : `master`, `preprod` and `dev`.

* `master` is updated every time a new version or a hotfix is released, every new commit on `master` gets a tag assigned to specify the platform version.
* `preprod` is (sometimes) used to test the platform versions before released.
* `dev` contains the last version of the platform currently in development. Theoretically, every new code added, modified or deleted on the platform should come from a merge request made on `dev`. Then, `dev` is merged onto `preprod` or `master`.

As we need to keep our code base as clean as possible, currently no direct commit are allowed on `dev`, `preprod` or `master` and merge requests can only be accepted if the CI linked to this project does not fail.

The naming of development branches isn’t really restricted, but we encourage you to use prefixes in your branch names such as `feature/` or `fix/` in order to make them more distinguishable.

Syntax
---

Code usually goes through [`pep8`](https://www.python.org/dev/peps/pep-0008/) validation. You can check your code syntax by using `flake8`, a pip package provided in the `requirements-tests.txt` file.

Documentation
---

Currently, we don’t have any guideline regarding code documentation, if we want the project to evolve cleanly, we might have to fix that.

However, if you fell like it, you can contribute to the user / administrator documentation available on [this wiki page](https://wiki.atilla.org/index.php?title=Members.atilla.org) (in french).

Developer workflow guide
===

If you ever feel lost in your development process, here is a simple guide that you can follow :

1. Supposing a bug or a feature has been notified by the users, this has to be reported on the project as an issue with a descriptive description and useful tags.
2. A developer will then create a new branch based on `dev` where he'll make the necessary code changes.
3. In case he needs advice from others, he can publish an early merge request for his branch to land onto `dev`, with the `WIP:` prefix, preventing the branch to be accidentally merged.
4. Discussions revolving around a patch must be contained in the associated merge request.
5. When the feature / bug is ready to review, the assignee is changed to one of the head developer of the project, and the `WIP:` prefix is removed (if present).
6. Comments about the code shall be made directly through the Gitlab code comment feature.
7. People can validate a merge request by posting in the MR comment section "LGTM".
8. When everyone agrees, the MR is accepted.

---

Finally, as a last advice, do not hesitate to contact the project developers if you have any question on this project development. We will be very happy to answer you !

3 changes: 1 addition & 2 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
source 'https://rubygems.org'
gem 'rails', '3.1.0'
gem 'sass-rails'
gem 'sass'
100 changes: 11 additions & 89 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,99 +1,21 @@
GEM
remote: https://rubygems.org/
specs:
actionmailer (3.1.0)
actionpack (= 3.1.0)
mail (~> 2.3.0)
actionpack (3.1.0)
activemodel (= 3.1.0)
activesupport (= 3.1.0)
builder (~> 3.0.0)
erubis (~> 2.7.0)
i18n (~> 0.6)
rack (~> 1.3.2)
rack-cache (~> 1.0.3)
rack-mount (~> 0.8.2)
rack-test (~> 0.6.1)
sprockets (~> 2.0.0)
activemodel (3.1.0)
activesupport (= 3.1.0)
bcrypt-ruby (~> 3.0.0)
builder (~> 3.0.0)
i18n (~> 0.6)
activerecord (3.1.0)
activemodel (= 3.1.0)
activesupport (= 3.1.0)
arel (~> 2.2.1)
tzinfo (~> 0.3.29)
activeresource (3.1.0)
activemodel (= 3.1.0)
activesupport (= 3.1.0)
activesupport (3.1.0)
multi_json (~> 1.0)
arel (2.2.3)
bcrypt-ruby (3.0.1)
builder (3.0.4)
erubis (2.7.0)
hike (1.2.3)
i18n (0.7.0)
json (1.8.6)
mail (2.3.3)
i18n (>= 0.4.0)
mime-types (~> 1.16)
treetop (~> 1.4.8)
mime-types (1.25.1)
multi_json (1.12.1)
polyglot (0.3.5)
rack (1.3.10)
rack-cache (1.0.3)
rack (>= 0.4)
rack-mount (0.8.3)
rack (>= 1.0.0)
rack-ssl (1.3.4)
rack
rack-test (0.6.3)
rack (>= 1.0)
rails (3.1.0)
actionmailer (= 3.1.0)
actionpack (= 3.1.0)
activerecord (= 3.1.0)
activeresource (= 3.1.0)
activesupport (= 3.1.0)
bundler (~> 1.0)
railties (= 3.1.0)
railties (3.1.0)
actionpack (= 3.1.0)
activesupport (= 3.1.0)
rack-ssl (~> 1.3.2)
rake (>= 0.8.7)
rdoc (~> 3.4)
thor (~> 0.14.6)
rake (12.0.0)
rdoc (3.12.2)
json (~> 1.4)
sass (3.4.23)
sass-rails (3.1.7)
actionpack (~> 3.1.0)
railties (~> 3.1.0)
sass (>= 3.1.10)
tilt (~> 1.3.2)
sprockets (2.0.5)
hike (~> 1.2)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
thor (0.14.6)
tilt (1.3.7)
treetop (1.4.15)
polyglot
polyglot (>= 0.3.1)
tzinfo (0.3.52)
ffi (1.9.18)
rb-fsevent (0.10.2)
rb-inotify (0.9.10)
ffi (>= 0.5.0, < 2)
sass (3.5.1)
sass-listen (~> 4.0.0)
sass-listen (4.0.0)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)

PLATFORMS
ruby

DEPENDENCIES
rails (= 3.1.0)
sass-rails
sass

BUNDLED WITH
1.14.3
1.15.1
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2016 ATILLA
Copyright (c) 2016-2017 ATILLA

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
Contributing
---

If you are planning to contribute to the project, please check out our [contribution guide](https://gitlab.atilla.org/atilla/members/blob/dev/CONTRIBUTING.md) first.

Installation
---
This project is using Python 3 and Django in the most part, but also SASS (Ruby) for easy CSS rendering. Before diving into the project, make sure to have the following packages installed on your system :
Expand Down
1 change: 1 addition & 0 deletions accounts/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
default_app_config = 'members.accounts.app.AccountConfig'
3 changes: 3 additions & 0 deletions accounts/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@

class AccountsConfig(AppConfig):
name = 'accounts'

def ready(self):
from . import signals # noqa
164 changes: 164 additions & 0 deletions accounts/ldap/actions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
# Codecs and translitcodec are needed when calling encode() methods
import codecs # noqa
import ldap
import ldap.modlist as modlist
import translitcodec # noqa

from django.conf import settings

from .connection.generic import LDAPGenericConnection
from .connection.LDAPManager import LDAPManagerConnection
from .utils import generate_crypt_password


class LDAPAccountAdder:
"""Allow the insertion of new accounts in an LDAP directory using the standard POSIX user scheme."""

def __init__(self, connection=None):
if connection:
self.__connection = connection
else:
self.__connection = LDAPManagerConnection()

def build_account_attributes(self, user, password):
"""
Generate an account attributes dict that can be used in LDAP addModList.
Also ensure that the account attributes are correctly formatted, and generate a password hash.
"""
attrs = {}
attrs['cn'] = '{} {}'.format(user.first_name, user.last_name)
attrs['uid'] = user.username
attrs['givenName'] = user.first_name
attrs['sn'] = user.last_name
attrs['uidNumber'] = self.get_biggest_LDAP_uid() + 1
attrs['gidNumber'] = settings.LDAP_DEFAULT_GID
attrs['userPassword'] = generate_crypt_password(password)
attrs['mail'] = user.email
attrs['homeDirectory'] = (settings.LDAP_DEFAULT_HOME_PATH + user.username)

# Make sure that every attribute is a ascii string
for key, value in attrs.items():
attrs[key] = str(value).encode('translit/one/ascii', 'replace')

attrs['objectclass'] = [
('inetOrgPerson').encode('translit/one/ascii', 'replace'),
('posixAccount').encode('translit/one/ascii', 'replace'),
('top').encode('translit/one/ascii', 'replace')]

return attrs

def get_biggest_LDAP_uid(self):
"""Get the biggest currently registered LDAP UID."""
search_filter = '(objectClass=posixAccount)'
search_attribute = ["uidNumber"]
search_scope = ldap.SCOPE_SUBTREE

result_id = self.__connection.search(
settings.LDAP_USERS_BASE_DN,
search_scope,
search_filter,
search_attribute)

max_uid = 0

while 1:
try:
result_type, result_data = self.__connection.result(result_id, 0)
except ldap.NO_SUCH_OBJECT:
raise ldap.LDAPError(
'Distinguished name ({}) does not exist.'.format(
settings.LDAP_USERS_BASE_DN))

if result_type == ldap.RES_SEARCH_ENTRY:
try:
uid = int(result_data[0][1]['uidNumber'][0])
if uid > max_uid:
max_uid = uid
except:
pass
else:
break

return max_uid

def test_unique(self, user_id, user_cn):
"""
Test if the given user common name or user ID is already present in the LDAP directory.
Return True if no such user can be found in the directory.
"""

# As we create a byte string, we can't use format() here
query_string = b''.join([b'(|(cn=', user_cn, b')(uid=', user_id, b'))'])

results = self.__connection.search_s(
settings.LDAP_USERS_BASE_DN,
ldap.SCOPE_SUBTREE,
query_string.decode('utf-8'))

return (len(results) == 0)

def add(self, pending_user, password):
"""
Add the given user to the current LDAP directory.
If an account having the same user name as the one of the pending_user, no action will be taken.
Return True if the user has been successfully added.
"""
attrs = self.build_account_attributes(pending_user, password)

if self.test_unique(attrs['uid'], attrs['cn']):
dn = 'cn={} {},{}'.format(
pending_user.first_name.encode(
'translit/one/ascii',
'replace').decode(),
pending_user.last_name.encode(
'translit/one/ascii',
'replace').decode(),
settings.LDAP_USERS_BASE_DN)

ldif = modlist.addModlist(attrs)

try:
self.__connection.add_s(dn, ldif)
return True
except:
return False
else:
return False


class LDAPAccountUpdater:
"""Perform generic actions on a given LDAP account."""

def __init__(self, user_dn):
self.__user_dn = user_dn

def change_password(self, old_password, new_password, connection=None):
"""
Update the password of a given user.
If no LDAP connection is provided, the user old password will be used in a simple bind action.
This allows to check if the user's old password is the correct one.
Returns True if the password has correctly been updated.
"""

try:
connection = LDAPGenericConnection(
settings.LDAP_SERVER_URI,
self.__user_dn,
old_password,
connection)
except (NameError, ldap.LDAPError):
return False

new_crypt_password = generate_crypt_password(new_password)
mod_attrs = [(
ldap.MOD_REPLACE,
'userPassword',
[str(new_crypt_password).encode('ascii', 'ignore')]
)]
connection.modify_s(self.__user_dn, mod_attrs)
return True
Loading

0 comments on commit ff752a4

Please sign in to comment.