easyauth 0.1.3
A toolkit to set up web application with authentication and authorization functionalities based on Django.
License: BSD
Keywords: Authentication based on Django,Authorization based on Django,Python web application Auth

easyauth: A toolkit to set up web application with authentication and authorization functionalities based on Django
====================================================================================================================
Don't need any code for authentication and authorization checking, just use configuration to enable authorization

.. image:: https://img.shields.io/travis/qingfeng0820/easyauth/master.svg
    :target: https://travis-ci.org/qingfeng0820/easyauth


`中文README <https://github.com/qingfeng0820/easyauth/blob/master/README-CN.rst>`_

License
-------

`BSD License <LICENSE.txt>`_


Source code
-----------
`Github <https://github.com/qingfeng0820/easyauth>`_


Develop environment
-------------------
- Python 2.7
- pip
- npm (can install nodejs which contains npm)


Installation
------------

from pypi

.. code-block:: shell

    pip install easyauth



Setup your own project via easyauth
-----------------------------------

1. Create your app by below command(it will create project under current folder):
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. code-block:: shell

    > make_project {your_app_name}

2. Setup backend which based on Django
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

1). Change configurations of the easyauth and Django based project
    - You can keep the configuration as default if you just want to trial
    - Main configurations are in {your_app_name}/settings/production.py
    - {your_app_name}/settings/local.py is used for some special configuration only need enabled in development

2). Change your User mode located in {your_app_name}/models.py
    - see below example code (don't need add serializer, view and permission classes for User model)

.. code-block:: python

    from easyauth.models import AbstractUser


    class User(AbstractUser):
        # you can define additional fields
        {Other field} = models.CharField(...)
        # You can specify the some other field to be USERNAME_FIELD.
        # USERNAME_FIELD is unique field to identity an user for login
        # Default is phone
        USERNAME_FIELD = {Other field to stand for username}

        # You can specify the USER_DEPART_FIELD field if you user model is grouped by department or company
        # For with depart field case, the permitted user can only maintain the users in the same department.
        # Default value is None for USER_DEPART_FIELD.
        USER_DEPART_FIELD = "company"
        company = models.ForeignKey(Company, related_name='users', null=True)

        # Filter properties are defined in view classed for filtering.
        # But the User model related views are defined in easyauth, you cannot create or change filter properties
        # Thus there is another way to add filter properties like below in user model
        FILTER_FIELDS = ('company__name', ...)
        SEARCH_FIELDS = (...)
        ORDERING_FIELDS = ('company__name', ...)

        # PS: You can have no implementation of this class (just add 'pass' in this class) if you just want to trial.

3). Create your own models, serializers and views
    - modify {your_app_name}/models.py to add your own models

.. code-block:: python

    class DummyModel(models.Model):
        name = models.CharField(max_length=100, unique=True)
        created_time = models.DateTimeField(auto_now_add=True)

        class Meta:
            permissions = (
                ("maintain_dummy_model", _("Can maintain dummy model")),   # <=== define a permission in your model
            )
            ordering = ('id', )

- create {your_app_name}/serializers.py to add your own serializers

.. code-block:: python

        from rest_framework import serializers

        from {your_app_name} import models

        class DummyModelSerializer(serializers.ModelSerializer):

            class Meta:
                model = models.DummyModel
                fields = '__all__'
                depth = 1


- create {your_app_name}/permissions.py to add your own permission classes

.. code-block:: python

    from easyauth.permissions import DBBasedPermissionsAll


    class DummyModelMaintainPermission(DBBasedPermissionsAll):
        required_permission_names = ["{your_app_name}.maintain_dummy_model", ]    # <=== permission check class for the defined permission in your model

- create {your_app_name}/views.py to add your own views

.. code-block:: python

    from rest_framework import viewsets, permissions

    from {your_app_name} import models, serializers
    from {your_app_name}.permissions import DummyModelMaintainPermission

    class DummyModelViewSet(viewsets.ModelViewSet):
        queryset = models.DummyModel.objects.all()
        serializer_class = serializers.DummyModelSerializer
        permission_classes = (DummyModelMaintainPermission, )                   # <=== use the defined permission class
        # Or you can the common permission class DjangoModelPermissions instead, which provided by Rest framework lib
        #  permission_classes = (permissions.DjangoModelPermissions, )

- modify {your_app_name}/urls.py to add your API urls

.. code-block:: python

    from django.conf.urls import url, include
    from rest_framework import routers

    from easyauth import urls as auth_urls
    from easyauth import admin_urls as user_admin_urls

    from {your_app_name} import views

    router = routers.DefaultRouter(trailing_slash=False)
    # app apis
    router.register(r'api/dummy_models', views.DummyModelViewSet, base_name='dummy_model') # <=== define your API url

    urlpatterns = router.urls

    urlpatterns += [
        # url(r'^admin/', admin.site.urls),
        # auth apis including login, password reset
        url(r'^api-auth/', include(auth_urls)),            # <=== authentication APIs provided by easyauth
        # user crud apis - only used by administrator
        url(r'^api/', include(user_admin_urls)),           # <=== user/group/permission admin APIs provided by easyauth
    ]

4). Go to {your_app_name} folder to initiate database
    - run below commands:

.. code-block:: shell

    {your_app_name}> python manage.py makemigrations
    {your_app_name}> python manage.py makemigrations {your_app_name}
    {your_app_name}> python manage.py migrate

5). Then create a superuser
    - run below command:

.. code-block:: shell

    {your_app_name}> python manage.py createsuperuser

6). Start your backend for your development test
    - run below command:

.. code-block:: shell

    {your_app_name}> python manage.py runserver 0.0.0.0:80

7). Have a test
    - Maintain authentication and authorization test
        - User the created super user to login
        - Create roles/groups
        - Create users, and assign them proper roles or permissions
    - All APIs provided by easyauth
        - user group(role) admin APIs (super user or have related permissions)
            - /api/groups GET: Get all user groups (super user or have 'query_group' permission)
            - /api/groups POST: Create an user group  (super user)
            - /api/groups/[group_id] GET: Get an user group  (super user or have 'query_group' permission)
            - /api/groups/[group_id] PUT or PATCH: Modify an user group (super user)
            - /api/groups/[group_id] DELETE: Delete an user group (super user)
        - user admin APIs (super user or have related permissions, if department enabled for user model, user can only maintain users in the same department if he/she has related permissions)
            - /api/users GET: Get all users (super user or have 'query_user' permission)
            - /api/users POST: Create an user (super user or have 'create_user' permission)
            - /api/users/[user_id] GET: Get a specific user (super user or have 'query_user' permission)
            - /api/users/[user_id] PUT or PATCH: Modify a specific user  (super user or have 'change_user' permission)
            - /api/users/[user_id] DELETE: Delete a specific user   (super user or have 'delete_user' permission)
            - /api/users/[user_id]/reset/password PUT: Reset to default password for a specific user (super user or have 'change_user' permission)
        - query permission APIs (permissions are defined in models code)
            - /api/permissions GET: Get all permissions (super user or have 'query_permission' permission)
            - /api/permissions/[permission_id] GET: Get a specific permission (super user or have 'query_permission' permission)
        - authentication APIs
            - /api-auth/login POST: User login
            - /api-auth/logout POST (or GET if enabled): User logout
            - /api-auth/me PUT or PATCH: Modify current login user
            - /api-auth/me GET: Get current login user
            - /api-auth/password/change PUT: Change the current login user's password
            - /api-auth/register POST: Register User (This API can be disabled by configuration)
        - Your own APIs
            - ...


3. Setup frontend if you need admin dashboard, which based Vue + Element-UI
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1). Go to {your_app_name}/vue-ui folder, and run below command to install dependency libs
    - run below command:

.. code-block:: shell

    {your_app_name}/vue-ui> npm install

2). Configure your backend baseURL by changing {your_app_name}/vue-ui/src/components/config.js
    - see below code:

.. code-block:: javascript

    var baseURL = 'http://localhost';                // <=== baseURL for production
    if (process.env.NODE_ENV == 'development') {
        baseURL = 'http://localhost';                // <=== baseURL for development
    }
    ...

3). Change theme by alias in {your_app_name}/vue-ui/build/webpack.base.conf.js
    - Change the value of alias 'THEME'
    - Now support two themes
        - resolve('src/themes/default')             // <=== default theme
        - resolve('src/themes/green')               // <=== green theme

4). Create your own vue pages to {your_app_name}/vue-ui/src/components/page/
    - Common pages are under {your_app_name}/vue-ui/src/components/common/

5). Change the menu in left slider bar by changing {your_app_name}/vue-ui/src/components/menus.js
    - see below example code:

.. code-block:: javascript

    import i18n from '../i18n/i18n'
    import permission from './common/permission'
    import Dashboard from '@/components/page/Dashboard'
    import UserAdmin from '@/components/page/UserAdmin'
    import RoleAdmin from '@/components/page/RoleAdmin'     // RoleAdmin and UserAdmin are default pages, you can just use it.
    import YourSubMenuItem from '@/components/page/YourSubMenuItem'
    import YourSubSubMenuItem from '@/components/page/YourSubSubMenuItem'

    var menu = [
            {
                name: 'Dashboard',
                path: '/dashboard',
                component: Dashboard,
                icon: 'el-icon-lx-home',
                meta: {
                    getTitle: function() {
                        return i18n.t("page.homeTitle")
                    },
                },
            },
            {
                name: 'UserAdmin',
                path: '/userAdmin',
                component: UserAdmin,
                icon: 'el-icon-lx-people',
                meta: {
                    getTitle:  function() {
                        return i18n.t("page.userAdminTitle")
                    },
                    // must have all permissions listed below to access this menu item
                    requiredPermissions: ['query_group', 'query_permission', 'add_user', 'change_user', 'delete_user'],
                },
            },
            {
                name: 'RoleAdmin',
                path: '/roleAdmin',
                component: RoleAdmin,
                icon: 'el-icon-lx-group',
                meta: {
                        getTitle: function() {
                            return i18n.t("page.roleAdminTitle")
                        },
                        permissionCheck: function(user) {
                            return permission.isSuperUser(user)
                        }
                    },
            },
            {
                name: 'YourFolderMenu',
                icon: 'xxx',
                meta: {
                        getTitle: function() {
                            return "Your Folder Menu"
                        },
                        notRequireAuth: true,                       // <=== this configuration item means this menu can access by anonymous user
                    },
                subs: [
                      {
                          name: 'YourSubFolderMenu',
                          meta: {
                              getTitle: function() {
                                 return "Your Sub Folder Menu"
                              },
                              requiredPermissions: [...],
                          },
                          subs: [
                               // only can support three levels menu
                               {
                                   name: 'YourSubSubMenuItem',
                                   path: '/yourSubSubMenuItem',
                                   component: YourSubSubMenuItem,
                                   meta: {
                                       getTitle: function() {
                                           return "Your Sub Sub Menu Item"
                                       },
                                   },
                               },
                               ...

                          ]
                      },
                      {
                          name: 'YourSubMenuItem',
                          path: '/yourSubMenuItem',
                          component: YourSubMenuItem,
                          meta: {
                               getTitle: function() {
                                   return "Your Sub Menu Item"
                               },
                               requiredPermissions: ['maintain_dummy_model', ...],    // <=== Use the defined permission in your model
                          },
                      },
                      ...
                ]
            },
    ]


- screen shot for above menu
.. image:: img/ui.JPG

6). Modify UserAdmin.vue page
    - If you have additional fields in your User model, you can change the UserAdmin.vue to support them
        - Add columns in <el-table> for additional fields
        - Add form items in <el-dialog> of editing user for additional fields
        - Change related javascript code in that page

7). Build you pages
    - run below command:

.. code-block:: shell

    {your_app_name}/vue-ui> npm run build

8). Deploy build results to static folder
    - run below commands:

.. code-block:: shell

    {your_app_name}> mkdir static
    {your_app_name}> cp vue-ui/build/* static/

9). Access you pages
    - Visit http://localhost/static/index.html

10). if you are focus on pages development, you can use use dev model instead of steps 7 - 9
    - run below command, then visit http://localhost:8080:

.. code-block:: shell

    {your_app_name}/vue-ui> npm run build


More configurations
-------------------


Language configuration
^^^^^^^^^^^^^^^^^^^^^^^^
- Change to be Chinese
    - Change LANGUAGE_CODE = 'zh-hans' in {your app name}/settings/production.py (backend)
    - Change defaultLangCode: "zh-hans" in {your_app_name}/vue-ui/src/components/config.js (frontend)

.. code-block:: javascript

    ...
    const config = {
        loginFieldName: "phone",                         // <=== should keep this value same as USERNAME_FIELD in your User model
        backendBaseURL: baseURL,
        requestTimeout: 10000,
        defaultLangCode: "zh-hans",                      // <=== Change here for frontend
    }
    ...


easyauth Configuration
^^^^^^^^^^^^^^^^^^^^^^^^

Change EASYAUTH_CONF in {your_app_name}/settings/product.py if you need:

.. code-block:: python

    EASYAUTH_CONF = {
        'USER_DEFAULT_PWD_MAINTAIN_BY_ADMIN': "12345678",
        'ACCOUNT_LOGOUT_ON_GET': False,
        'DISABLE_REGISTER': False,
        'LANG_PARAM': 'lang',
    }

+----------------------------------------+------------+--------------------------------------------------------------+
| Configuration Item                     | Type       | Description                                                  |
+========================================+============+==============================================================+
| USER_DEFAULT_PWD_MAINTAIN_BY_ADMIN     | string     | Define the default password for maintaining by administrator.|
|                                        |            |                                                              |
|                                        |            | Default value is 123456 for absent                           |
+----------------------------------------+------------+--------------------------------------------------------------+
| ACCOUNT_LOGOUT_ON_GET                  | bool       | Switch for enabling GET method for logout API.               |
|                                        |            |                                                              |
|                                        |            | Default value is False for absent                            |
+----------------------------------------+------------+--------------------------------------------------------------+
| DISABLE_REGISTER                       | bool       | Switch for disabling register API.                           |
|                                        |            |                                                              |
|                                        |            | Default value is False for absent                            |
+----------------------------------------+------------+--------------------------------------------------------------+
| LANG_PARAM                             | string     | Set the language parameter name in http request.             |
|                                        |            | (Usually don't need to change it)                            |
|                                        |            |                                                              |
|                                        |            | Default value is lang, it will be used like                  |
|                                        |            | http://localhost/api/users?lang=zh-hans                      |
|                                        |            |                                                              |
|                                        |            | Tips: Keep the value of lang_param in                        |
|                                        |            | {your_app_name}/vue-ui/src/components/common/easyauth.js     |
|                                        |            | same with this configuration value.                          |
+----------------------------------------+------------+--------------------------------------------------------------+

More examples please see the test app in this repo