You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by ma...@apache.org on 2017/09/21 17:00:02 UTC

[32/54] [abbrv] [partial] airavata-django-portal git commit: Moving admin_view JS into django app and moving django project back

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/apps/auth/models.py
----------------------------------------------------------------------
diff --git a/django_airavata/apps/auth/models.py b/django_airavata/apps/auth/models.py
new file mode 100644
index 0000000..71a8362
--- /dev/null
+++ b/django_airavata/apps/auth/models.py
@@ -0,0 +1,3 @@
+from django.db import models
+
+# Create your models here.

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/apps/auth/templates/django_airavata_auth/auth_error.html
----------------------------------------------------------------------
diff --git a/django_airavata/apps/auth/templates/django_airavata_auth/auth_error.html b/django_airavata/apps/auth/templates/django_airavata_auth/auth_error.html
new file mode 100644
index 0000000..b7f5d58
--- /dev/null
+++ b/django_airavata/apps/auth/templates/django_airavata_auth/auth_error.html
@@ -0,0 +1,11 @@
+<html>
+    <head>
+        <title>Authentication Error</title>
+    </head>
+    <body>
+        <h1>Authentication Error</h1>
+        <p>
+            Failed to process authentication callback. You might want to <a href="{{ login_url }}">try logging in again</a>.
+        </p>
+    </body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/apps/auth/templates/django_airavata_auth/login.html
----------------------------------------------------------------------
diff --git a/django_airavata/apps/auth/templates/django_airavata_auth/login.html b/django_airavata/apps/auth/templates/django_airavata_auth/login.html
new file mode 100644
index 0000000..fb326b0
--- /dev/null
+++ b/django_airavata/apps/auth/templates/django_airavata_auth/login.html
@@ -0,0 +1,28 @@
+{% extends 'main_base.html' %}
+
+{% block content %}
+
+<div class="container">
+    <div class="row">
+        <div class="col-md-6 col-md-offset-3">
+            <div class="panel panel-default">
+                <div class="panel-body">
+                    <h3>Login</h3>
+                    <form action="{% url 'django_airavata_auth:handle_login' %}" method="post">
+                        {% csrf_token %}
+                        <div class="form-group">
+                            <label for="username">Username</label>
+                            <input type="text" class="form-control" id="username" name="username" placeholder="Username" value="{{ username }}">
+                        </div>
+                        <div class="form-group">
+                            <label for="password">Password</label>
+                            <input type="password" class="form-control" id="password" name="password" placeholder="Password">
+                        </div>
+                        <button type="submit" class="btn btn-default">Submit</button>
+                    </form>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+{% endblock content %}

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/apps/auth/tests.py
----------------------------------------------------------------------
diff --git a/django_airavata/apps/auth/tests.py b/django_airavata/apps/auth/tests.py
new file mode 100644
index 0000000..7ce503c
--- /dev/null
+++ b/django_airavata/apps/auth/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/apps/auth/urls.py
----------------------------------------------------------------------
diff --git a/django_airavata/apps/auth/urls.py b/django_airavata/apps/auth/urls.py
new file mode 100644
index 0000000..4cb5b35
--- /dev/null
+++ b/django_airavata/apps/auth/urls.py
@@ -0,0 +1,13 @@
+
+from django.conf.urls import url
+
+from . import views
+
+app_name = 'django_airavata_auth'
+urlpatterns = [
+    url(r'^login$', views.start_login, name='login'),
+    url(r'^handle_login$', views.handle_login, name='handle_login'),
+    url(r'^logout$', views.start_logout, name='logout'),
+    url(r'^callback', views.callback, name='callback'),
+    url(r'^error', views.auth_error, name='error'),
+]
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/apps/auth/utils.py
----------------------------------------------------------------------
diff --git a/django_airavata/apps/auth/utils.py b/django_airavata/apps/auth/utils.py
new file mode 100644
index 0000000..7d0bee4
--- /dev/null
+++ b/django_airavata/apps/auth/utils.py
@@ -0,0 +1,13 @@
+
+from apache.airavata.model.security.ttypes import AuthzToken
+
+from django.conf import settings
+
+def get_authz_token(request):
+    if 'ACCESS_TOKEN' in request.session:
+        access_token = request.session['ACCESS_TOKEN']
+        username = request.user.username
+        gateway_id = settings.GATEWAY_ID
+        return AuthzToken(accessToken=access_token, claimsMap={'gatewayID': gateway_id, 'userName': username})
+    else:
+        return None

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/apps/auth/views.py
----------------------------------------------------------------------
diff --git a/django_airavata/apps/auth/views.py b/django_airavata/apps/auth/views.py
new file mode 100644
index 0000000..a066bc8
--- /dev/null
+++ b/django_airavata/apps/auth/views.py
@@ -0,0 +1,63 @@
+from django.conf import settings
+from django.contrib.auth import authenticate, login, logout
+from django.http import HttpResponse
+from django.shortcuts import render, redirect
+from django.urls import reverse
+
+from requests_oauthlib import OAuth2Session
+
+import logging
+from urllib.parse import quote
+
+logger = logging.getLogger(__name__)
+
+# Create your views here.
+
+def start_login(request):
+    # TODO: If the gateway is configured to not allow username password authentication, then redirect to Keycloak
+    # client_id = settings.KEYCLOAK_CLIENT_ID
+    # base_authorize_url = settings.KEYCLOAK_AUTHORIZE_URL
+    # oauth2_session = OAuth2Session(client_id, scope='openid', redirect_uri=request.build_absolute_uri(reverse('django_airavata_auth:callback')))
+    # authorization_url, state = oauth2_session.authorization_url(base_authorize_url)
+    # logger.debug("authorization_url={}, state={}".format(authorization_url, state))
+    # # Store state in session for later validation
+    # request.session['OAUTH2_STATE'] = state
+    # return redirect(authorization_url)
+    return render(request, 'django_airavata_auth/login.html')
+
+def handle_login(request):
+    username = request.POST['username']
+    password = request.POST['password']
+    user = authenticate(username=username, password=password, request=request)
+    logger.debug("authenticated user: {}".format(user))
+    try:
+        if user is not None:
+            login(request, user)
+            return redirect(settings.LOGIN_REDIRECT_URL)
+        else:
+            # TODO: add error message that login failed
+            return render(request, 'django_airavata_auth/login.html', {
+                'username': username
+            })
+    except Exception as err:
+        logger.exception("An error occurred while logging in with username and password")
+        return redirect(reverse('django_airavata_auth:error'))
+
+def start_logout(request):
+    logout(request)
+    redirect_url = request.build_absolute_uri(reverse(settings.LOGOUT_REDIRECT_URL))
+    return redirect(settings.KEYCLOAK_LOGOUT_URL + "?redirect_uri=" + quote(redirect_url))
+
+def callback(request):
+    try:
+        user = authenticate(request=request)
+        login(request, user)
+        return redirect(settings.LOGIN_REDIRECT_URL)
+    except Exception as err:
+        logger.exception("An error occurred while processing OAuth2 callback: {}".format(request.build_absolute_uri()))
+        return redirect(reverse('django_airavata_auth:error'))
+
+def auth_error(request):
+    return render(request, 'django_airavata_auth/auth_error.html', {
+        'login_url': settings.LOGIN_URL
+    })
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/apps/groups/__init__.py
----------------------------------------------------------------------
diff --git a/django_airavata/apps/groups/__init__.py b/django_airavata/apps/groups/__init__.py
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/apps/groups/admin.py
----------------------------------------------------------------------
diff --git a/django_airavata/apps/groups/admin.py b/django_airavata/apps/groups/admin.py
new file mode 100644
index 0000000..8c38f3f
--- /dev/null
+++ b/django_airavata/apps/groups/admin.py
@@ -0,0 +1,3 @@
+from django.contrib import admin
+
+# Register your models here.

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/apps/groups/apps.py
----------------------------------------------------------------------
diff --git a/django_airavata/apps/groups/apps.py b/django_airavata/apps/groups/apps.py
new file mode 100644
index 0000000..e66c251
--- /dev/null
+++ b/django_airavata/apps/groups/apps.py
@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class GroupsConfig(AppConfig):
+    name = 'django_airavata.apps.groups'
+    label = 'django_airavata_group'

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/apps/groups/forms.py
----------------------------------------------------------------------
diff --git a/django_airavata/apps/groups/forms.py b/django_airavata/apps/groups/forms.py
new file mode 100644
index 0000000..d3ba029
--- /dev/null
+++ b/django_airavata/apps/groups/forms.py
@@ -0,0 +1,31 @@
+# Create your forms here.
+from django import forms
+
+class CreateForm(forms.Form):
+    domain_id = forms.CharField(required=True, widget=forms.HiddenInput)
+    group_name = forms.CharField(required=True)
+    description = forms.CharField(
+        required=True,
+        widget=forms.Textarea
+    )
+    group_owner = forms.CharField(required=True, widget=forms.HiddenInput)
+    CHOICES1 = (('1', 'User Level',), ('2', 'Admin Level',))
+    group_type = forms.ChoiceField(widget=forms.HiddenInput, choices=CHOICES1, required=True)
+    CHOICES2 = (('0', 'Single User',), ('1', 'Multi User',))
+    group_cardinality = forms.ChoiceField(widget=forms.HiddenInput, choices=CHOICES2, required=True)
+
+#class AddForm(forms.Form):
+#    users = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, required=False)
+
+#class RemoveForm(forms.Form):
+#    members = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, required=False)
+
+class AddForm(forms.Form):
+    def __init__(self, data=None, user_choices=None):
+        super().__init__(data=data)
+        self.fields["users"] = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, choices=user_choices)
+
+class RemoveForm(forms.Form):
+    def __init__(self, data=None, user_choices=None):
+        super().__init__(data=data)
+        self.fields["members"] = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, choices=user_choices)

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/apps/groups/migrations/__init__.py
----------------------------------------------------------------------
diff --git a/django_airavata/apps/groups/migrations/__init__.py b/django_airavata/apps/groups/migrations/__init__.py
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/apps/groups/models.py
----------------------------------------------------------------------
diff --git a/django_airavata/apps/groups/models.py b/django_airavata/apps/groups/models.py
new file mode 100644
index 0000000..71a8362
--- /dev/null
+++ b/django_airavata/apps/groups/models.py
@@ -0,0 +1,3 @@
+from django.db import models
+
+# Create your models here.

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/apps/groups/templates/django_airavata_groups/group_details.html
----------------------------------------------------------------------
diff --git a/django_airavata/apps/groups/templates/django_airavata_groups/group_details.html b/django_airavata/apps/groups/templates/django_airavata_groups/group_details.html
new file mode 100644
index 0000000..45364c5
--- /dev/null
+++ b/django_airavata/apps/groups/templates/django_airavata_groups/group_details.html
@@ -0,0 +1,61 @@
+{% extends 'base.html' %}
+
+{% block content %}
+
+<h1 align="center">Group Details</h1>
+
+<div class="jumbotron">
+    <div class="container">
+        {% if user.is_authenticated %}
+        <div class="row">
+            <div class="col-md-12 table-responsive">
+                <table class="table table-striped table-bordered">
+                    <thead>
+                        <tr>
+                            <th style="text-align:center">PROPERTY</th>
+                            <th style="text-align:center">VALUE</th>
+                        </tr>
+                    </thead>
+                    <tbody align="center">
+                        <tr>
+                            <td>Group Name</td>
+                            <td>{{ group.name }}</td>
+                        </tr>
+                        <tr>
+                            <td>Description</td>
+                            <td>{{ group.description }}</td>
+                        </tr>
+                        <tr>
+                            <td>Onwer ID</td>
+                            <td>{{ group.ownerId }}</td>
+                        </tr>
+                        {% if u_id == group.ownerId %}
+                        <tr>
+                            <td>Group Members</td>
+                            <td>
+                            {% for member in members %}
+                            {{ member.userId }}<br>
+                            {% endfor %}
+                            </td>
+                        </tr>
+                        {% endif %}
+                        <tr>
+                            <td>Created</td>
+                            <td>{{ c_time }}</td>
+                        </tr>
+                        <tr>
+                            <td>Updated</td>
+                            <td>{{ u_time }}</td>
+                        </tr>
+                    </tbody>
+                </table>
+            </div>
+        </div>
+        <p align="right"><a class="btn btn-primary btn-xs" href="{% url 'django_airavata_groups:manage' %}" role="button">Back</a></p>
+        {% else %}
+        <p><a class="btn btn-primary btn-lg" href="{% url 'django_airavata_auth:login' %}" role="button">Login »</a></p>
+        {% endif %}
+    </div>
+</div>
+
+{% endblock content %}

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/apps/groups/templates/django_airavata_groups/group_edit.html
----------------------------------------------------------------------
diff --git a/django_airavata/apps/groups/templates/django_airavata_groups/group_edit.html b/django_airavata/apps/groups/templates/django_airavata_groups/group_edit.html
new file mode 100644
index 0000000..507a456
--- /dev/null
+++ b/django_airavata/apps/groups/templates/django_airavata_groups/group_edit.html
@@ -0,0 +1,38 @@
+{% extends 'base.html' %}
+
+{% block content %}
+
+<h1 align="center">Edit Group</h1>
+<br>
+{% block body %}
+<div class="container">
+    {% if user.is_authenticated %}
+    <h5>Group Name: {{ group_name }}</h5>
+    <div class="row">
+        <div class="col-lg-6">
+            <h3>Add</h3>
+            <form action="" method="post">
+                {% csrf_token %}
+                {{ add_form.as_p }}
+                <button class="btn btn-primary btn-xs" type="submit" role="button" name="add">Add</button>
+            </form>
+        </div>
+        <div class="col-lg-6">
+            <h3>Remove</h3>
+            <form action="" method="post">
+                {% csrf_token %}
+                {{ remove_form.as_p }}
+                <button class="btn btn-primary btn-xs" type="submit" role="button" name="remove" onclick="return confirm('Are you sure you want to remove the selected members from the group? This action cannot be undone!')">Remove</button>
+            </form>
+        </div>
+    </div>
+    <br>
+    <br>
+    <p align="right"><a class="btn btn-primary btn-xs" href="{% url 'django_airavata_groups:manage' %}" role="button">Back</a></p>
+    {% else %}
+    <p><a class="btn btn-primary btn-lg" href="{% url 'django_airavata_auth:login' %}" role="button">Login »</a></p>
+    {% endif %}
+</div>
+{% endblock %}
+
+{% endblock content %}

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/apps/groups/templates/django_airavata_groups/groups_create.html
----------------------------------------------------------------------
diff --git a/django_airavata/apps/groups/templates/django_airavata_groups/groups_create.html b/django_airavata/apps/groups/templates/django_airavata_groups/groups_create.html
new file mode 100644
index 0000000..e7ac749
--- /dev/null
+++ b/django_airavata/apps/groups/templates/django_airavata_groups/groups_create.html
@@ -0,0 +1,28 @@
+{% extends 'base.html' %}
+
+{% block content %}
+
+<h1 align="center">Create a Group</h1>
+<br>
+{% block body %}
+<div class="container">
+    {% if user.is_authenticated %}
+    <div class="row">
+        <div class="col-md-12">
+            <h3>Enter Group Details</h3>
+            <form action="" method="post">
+                {% csrf_token %}
+                {{ form.as_p }}
+                <button class="btn btn-primary btn-xs" type="submit" role="button">Submit</button>
+            </form>
+        </div>
+    </div>
+    <br>
+    <p align="right"><a class="btn btn-primary btn-xs" href="{% url 'django_airavata_groups:manage' %}" role="button">Back</a></p>
+    {% else %}
+    <p><a class="btn btn-primary btn-lg" href="{% url 'django_airavata_auth:login' %}" role="button">Login »</a></p>
+    {% endif %}
+</div>
+{% endblock %}
+
+{% endblock content %}

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/apps/groups/templates/django_airavata_groups/groups_manage.html
----------------------------------------------------------------------
diff --git a/django_airavata/apps/groups/templates/django_airavata_groups/groups_manage.html b/django_airavata/apps/groups/templates/django_airavata_groups/groups_manage.html
new file mode 100644
index 0000000..2e7fc0b
--- /dev/null
+++ b/django_airavata/apps/groups/templates/django_airavata_groups/groups_manage.html
@@ -0,0 +1,99 @@
+{% extends 'base.html' %}
+
+{% block content %}
+
+<h1 align="center">Manage Groups</h1>
+<br>
+<div class="container">
+    {% if user.is_authenticated %}
+    <div class="row">
+        <a href="{% url 'django_airavata_groups:create' %}">
+            <button type="button" class="btn btn-default toggle-add-tenant"><span
+                        class="glyphicon glyphicon-plus"></span>Create a New Group
+            </button>
+        </a>
+    </div>
+    <br>
+    <hr>
+    {% if messages %}
+    <div class="alert alert-info">
+        <ul class="messages">
+            {% for message in messages %}
+            <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
+            {% endfor %}
+        </ul>
+    </div>
+    {% endif %}
+    <br>
+    <div class="row">
+        <div class="col-md-12 table-responsive">
+            <h3>Groups you own</h3>
+            <br>
+            <br>
+            <table class="table table-striped table-bordered">
+                <thead>
+                    <tr>
+                        <th style="text-align:center">NAME</th>
+                        <th style="text-align:center">OPTIONS</th>
+                    </tr>
+                </thead>
+                <tbody align="center">
+                    {% for group in owner %}
+                    <tr>
+                        <td>{{ group.name }}</td>
+                        <td>
+                            <a href="{% url 'django_airavata_groups:view' %}?group_id={{ group.groupId | urlencode }}">
+                                <button type="button" class="btn btn-primary btn-xs">View</button>
+                            </a>
+                            <a href="{% url 'django_airavata_groups:edit' %}?group_id={{ group.groupId | urlencode }}">
+                                <button type="button" class="btn btn-primary btn-xs">Edit</button>
+                            </a>
+                            <a href="{% url 'django_airavata_groups:delete' %}?group_id={{ group.groupId | urlencode }}">
+                                <button type="button" class="btn btn-danger btn-xs" onclick="return confirm('Are you sure you want to delete the group? This action cannot be undone!')">Delete</button>
+                            </a>
+                        </td>
+                    </tr>
+                    {% endfor %}
+                </tbody>
+            </table>
+        </div>
+    </div>
+    <br>
+    <hr>
+    <br>
+    <div class="row">
+        <div class="col-md-12 table-responsive">
+            <h3>Groups you're a member of</h3>
+            <br>
+            <br>
+            <table class="table table-striped table-bordered">
+                <thead>
+                    <tr>
+                        <th style="text-align:center">NAME</th>
+                        <th style="text-align:center">OPTIONS</th>
+                    </tr>
+                </thead>
+                <tbody align="center">
+                    {% for group in member %}
+                    <tr>
+                        <td>{{ group.name }}</td>
+                        <td>
+                            <a href="{% url 'django_airavata_groups:view' %}?group_id={{ group.groupId | urlencode }}">
+                                <button type="button" class="btn btn-primary btn-xs">View</button>
+                            </a>
+                            <a href="{% url 'django_airavata_groups:leave' %}?group_id={{ group.groupId | urlencode }}">
+                                <button type="button" class="btn btn-danger btn-xs" onclick="return confirm('Are you sure you want to leave the group? This action cannot be undone!')">Leave</button>
+                            </a>
+                        </td>
+                    </tr>
+                    {% endfor %}
+                </tbody>
+            </table>
+        </div>
+    </div>
+    {% else %}
+    <p><a class="btn btn-primary btn-lg" href="{% url 'django_airavata_auth:login' %}" role="button">Login »</a></p>
+    {% endif %}
+</div>
+
+{% endblock content %}

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/apps/groups/tests.py
----------------------------------------------------------------------
diff --git a/django_airavata/apps/groups/tests.py b/django_airavata/apps/groups/tests.py
new file mode 100644
index 0000000..7ce503c
--- /dev/null
+++ b/django_airavata/apps/groups/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/apps/groups/urls.py
----------------------------------------------------------------------
diff --git a/django_airavata/apps/groups/urls.py b/django_airavata/apps/groups/urls.py
new file mode 100644
index 0000000..d4ed1f3
--- /dev/null
+++ b/django_airavata/apps/groups/urls.py
@@ -0,0 +1,9 @@
+
+from django.conf.urls import url
+
+from . import views
+
+app_name = 'django_airavata_groups'
+urlpatterns = [
+    url(r'^$', views.groups_list, name='groups'),
+]
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/apps/groups/views.py
----------------------------------------------------------------------
diff --git a/django_airavata/apps/groups/views.py b/django_airavata/apps/groups/views.py
new file mode 100644
index 0000000..805ceab
--- /dev/null
+++ b/django_airavata/apps/groups/views.py
@@ -0,0 +1,23 @@
+# Create your views here.
+from django.conf import settings
+from django.contrib.auth.decorators import login_required
+from django.shortcuts import render, redirect
+
+import logging
+
+logger = logging.getLogger(__name__)
+
+
+@login_required
+def groups_list(request):
+
+    gateway_id = settings.GATEWAY_ID
+
+    try:
+        groups = request.sharing_client.getGroups(gateway_id, 0, -1)
+        return render(request, 'django_airavata_groups/groups_list.html', {
+            'groups': groups
+        })
+    except Exception as e:
+        logger.exception("Failed to load groups")
+        return redirect('/')

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/apps/workspace/__init__.py
----------------------------------------------------------------------
diff --git a/django_airavata/apps/workspace/__init__.py b/django_airavata/apps/workspace/__init__.py
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/apps/workspace/admin.py
----------------------------------------------------------------------
diff --git a/django_airavata/apps/workspace/admin.py b/django_airavata/apps/workspace/admin.py
new file mode 100644
index 0000000..8c38f3f
--- /dev/null
+++ b/django_airavata/apps/workspace/admin.py
@@ -0,0 +1,3 @@
+from django.contrib import admin
+
+# Register your models here.

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/apps/workspace/apps.py
----------------------------------------------------------------------
diff --git a/django_airavata/apps/workspace/apps.py b/django_airavata/apps/workspace/apps.py
new file mode 100644
index 0000000..ffa1ad0
--- /dev/null
+++ b/django_airavata/apps/workspace/apps.py
@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class WorkspaceConfig(AppConfig):
+    name = 'django_airavata.apps.workspace'
+    label = 'django_airavata_workspace'

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/apps/workspace/migrations/__init__.py
----------------------------------------------------------------------
diff --git a/django_airavata/apps/workspace/migrations/__init__.py b/django_airavata/apps/workspace/migrations/__init__.py
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/apps/workspace/models.py
----------------------------------------------------------------------
diff --git a/django_airavata/apps/workspace/models.py b/django_airavata/apps/workspace/models.py
new file mode 100644
index 0000000..71a8362
--- /dev/null
+++ b/django_airavata/apps/workspace/models.py
@@ -0,0 +1,3 @@
+from django.db import models
+
+# Create your models here.

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/apps/workspace/templates/django_airavata_workspace/projects_list.html
----------------------------------------------------------------------
diff --git a/django_airavata/apps/workspace/templates/django_airavata_workspace/projects_list.html b/django_airavata/apps/workspace/templates/django_airavata_workspace/projects_list.html
new file mode 100644
index 0000000..4a52225
--- /dev/null
+++ b/django_airavata/apps/workspace/templates/django_airavata_workspace/projects_list.html
@@ -0,0 +1,16 @@
+{% extends 'base.html' %}
+
+{% load static %}
+
+{% block content %}
+
+<h1>Projects</h1>
+
+<div id="app"></div>
+
+{% endblock content %}
+
+{% block scripts %}
+
+<script src="{% static "django_airavata_workspace/build/main.js" %}"></script>
+{% endblock %}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/apps/workspace/tests.py
----------------------------------------------------------------------
diff --git a/django_airavata/apps/workspace/tests.py b/django_airavata/apps/workspace/tests.py
new file mode 100644
index 0000000..7ce503c
--- /dev/null
+++ b/django_airavata/apps/workspace/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/apps/workspace/urls.py
----------------------------------------------------------------------
diff --git a/django_airavata/apps/workspace/urls.py b/django_airavata/apps/workspace/urls.py
new file mode 100644
index 0000000..6bc73a7
--- /dev/null
+++ b/django_airavata/apps/workspace/urls.py
@@ -0,0 +1,9 @@
+
+from django.conf.urls import url
+
+from . import views
+
+app_name = 'django_airavata_workspace'
+urlpatterns = [
+    url(r'^projects$', views.projects_list, name='projects'),
+]
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/apps/workspace/views.py
----------------------------------------------------------------------
diff --git a/django_airavata/apps/workspace/views.py b/django_airavata/apps/workspace/views.py
new file mode 100644
index 0000000..6967f25
--- /dev/null
+++ b/django_airavata/apps/workspace/views.py
@@ -0,0 +1,21 @@
+
+from django_airavata.apps.api.views import ProjectViewSet
+from rest_framework.renderers import JSONRenderer
+from django.conf import settings
+from django.contrib.auth.decorators import login_required
+from django.shortcuts import render, redirect
+
+import logging
+
+logger = logging.getLogger(__name__)
+
+
+@login_required
+def projects_list(request):
+
+    response = ProjectList().get(request)
+    projects_json = JSONRenderer().render(response.data)
+
+    return render(request, 'django_airavata_workspace/projects_list.html', {
+        'projects_data': projects_json
+    })

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/middleware.py
----------------------------------------------------------------------
diff --git a/django_airavata/middleware.py b/django_airavata/middleware.py
new file mode 100644
index 0000000..8954d11
--- /dev/null
+++ b/django_airavata/middleware.py
@@ -0,0 +1,123 @@
+
+from apache.airavata.api import Airavata
+from apache.airavata.api.sharing import SharingRegistryService
+
+from thrift import Thrift
+from thrift.transport import TSSLSocket
+from thrift.transport import TSocket
+from thrift.transport import TTransport
+from thrift.protocol import TBinaryProtocol
+
+from django.conf import settings
+
+import logging
+
+logger = logging.getLogger(__name__)
+
+def get_unsecure_transport(hostname, port):
+    # Create a socket to the Airavata Server
+    transport = TSocket.TSocket(hostname, port)
+
+    # Use Buffered Protocol to speedup over raw sockets
+    transport = TTransport.TBufferedTransport(transport)
+    return transport
+
+def get_secure_transport(hostname, port):
+
+    # Create a socket to the Airavata Server
+    # TODO: validate server certificate
+    transport = TSSLSocket.TSSLSocket(hostname, port, validate=False)
+
+    # Use Buffered Protocol to speedup over raw sockets
+    transport = TTransport.TBufferedTransport(transport)
+    return transport
+
+def get_transport(hostname, port, secure=True):
+    if secure:
+        transport = get_secure_transport(hostname, port)
+    else:
+        transport = get_unsecure_transport(hostname, port)
+    return transport
+
+def get_airavata_client(transport):
+
+    # Airavata currently uses Binary Protocol
+    protocol = TBinaryProtocol.TBinaryProtocol(transport)
+
+    # Create a Airavata client to use the protocol encoder
+    client=Airavata.Client(protocol)
+    return client
+
+def get_sharing_client(transport):
+
+    protocol = TBinaryProtocol.TBinaryProtocol(transport)
+
+    return SharingRegistryService.Client(protocol)
+
+def airavata_client(get_response):
+    "Open and close Airavata client for each request"
+
+    def middleware(request):
+
+        # If user is logged in create an airavata api client for the request
+        if request.user.is_authenticated:
+            transport = get_transport(settings.AIRAVATA_API_HOST, settings.AIRAVATA_API_PORT, settings.AIRAVATA_API_SECURE)
+            airavata_client = get_airavata_client(transport)
+
+            try:
+                transport.open()
+            except Exception as e:
+                logger.exception("Failed to open thrift connection to API server")
+
+            if transport.isOpen():
+                request.airavata_client = airavata_client
+            else:
+                # if request.airavata_client is None, this will indicate to view
+                # code that the API server is down
+                request.airavata_client = None
+
+            response = get_response(request)
+
+            if transport.isOpen():
+                transport.close()
+                logger.debug("transport closed in middleware")
+        else:
+            response = get_response(request)
+
+        return response
+
+    return middleware
+
+def sharing_client(get_response):
+    "Open and close Sharing registry client for each request"
+
+    def middleware(request):
+
+        # If user is logged in create an airavata api client for the request
+        if request.user.is_authenticated:
+            transport = get_transport(settings.SHARING_API_HOST, settings.SHARING_API_PORT, settings.SHARING_API_SECURE)
+            sharing_client = get_sharing_client(transport)
+
+            try:
+                transport.open()
+            except Exception as e:
+                logger.exception("Failed to open thrift connection to Sharing Registry server")
+
+            if transport.isOpen():
+                request.sharing_client = sharing_client
+            else:
+                # if request.sharing_client is None, this will indicate to view
+                # code that the Sharing server is down
+                request.sharing_client = None
+
+            response = get_response(request)
+
+            if transport.isOpen():
+                transport.close()
+                logger.debug("transport closed in middleware")
+        else:
+            response = get_response(request)
+
+        return response
+
+    return middleware

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/resources/incommon_rsa_server_ca.pem
----------------------------------------------------------------------
diff --git a/django_airavata/resources/incommon_rsa_server_ca.pem b/django_airavata/resources/incommon_rsa_server_ca.pem
new file mode 100644
index 0000000..63c6bae
--- /dev/null
+++ b/django_airavata/resources/incommon_rsa_server_ca.pem
@@ -0,0 +1,68 @@
+-----BEGIN CERTIFICATE-----
+MIIF+TCCA+GgAwIBAgIQRyDQ+oVGGn4XoWQCkYRjdDANBgkqhkiG9w0BAQwFADCB
+iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
+cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV
+BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTQx
+MDA2MDAwMDAwWhcNMjQxMDA1MjM1OTU5WjB2MQswCQYDVQQGEwJVUzELMAkGA1UE
+CBMCTUkxEjAQBgNVBAcTCUFubiBBcmJvcjESMBAGA1UEChMJSW50ZXJuZXQyMREw
+DwYDVQQLEwhJbkNvbW1vbjEfMB0GA1UEAxMWSW5Db21tb24gUlNBIFNlcnZlciBD
+QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJwb8bsvf2MYFVFRVA+e
+xU5NEFj6MJsXKZDmMwysE1N8VJG06thum4ltuzM+j9INpun5uukNDBqeso7JcC7v
+HgV9lestjaKpTbOc5/MZNrun8XzmCB5hJ0R6lvSoNNviQsil2zfVtefkQnI/tBPP
+iwckRR6MkYNGuQmm/BijBgLsNI0yZpUn6uGX6Ns1oytW61fo8BBZ321wDGZq0GTl
+qKOYMa0dYtX6kuOaQ80tNfvZnjNbRX3EhigsZhLI2w8ZMA0/6fDqSl5AB8f2IHpT
+eIFken5FahZv9JNYyWL7KSd9oX8hzudPR9aKVuDjZvjs3YncJowZaDuNi+L7RyML
+fzcCAwEAAaOCAW4wggFqMB8GA1UdIwQYMBaAFFN5v1qqK0rPVIDh2JvAnfKyA2bL
+MB0GA1UdDgQWBBQeBaN3j2yW4luHS6a0hqxxAAznODAOBgNVHQ8BAf8EBAMCAYYw
+EgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUH
+AwIwGwYDVR0gBBQwEjAGBgRVHSAAMAgGBmeBDAECAjBQBgNVHR8ESTBHMEWgQ6BB
+hj9odHRwOi8vY3JsLnVzZXJ0cnVzdC5jb20vVVNFUlRydXN0UlNBQ2VydGlmaWNh
+dGlvbkF1dGhvcml0eS5jcmwwdgYIKwYBBQUHAQEEajBoMD8GCCsGAQUFBzAChjNo
+dHRwOi8vY3J0LnVzZXJ0cnVzdC5jb20vVVNFUlRydXN0UlNBQWRkVHJ1c3RDQS5j
+cnQwJQYIKwYBBQUHMAGGGWh0dHA6Ly9vY3NwLnVzZXJ0cnVzdC5jb20wDQYJKoZI
+hvcNAQEMBQADggIBAC0RBjjW29dYaK+qOGcXjeIT16MUJNkGE+vrkS/fT2ctyNMU
+11ZlUp5uH5gIjppIG8GLWZqjV5vbhvhZQPwZsHURKsISNrqOcooGTie3jVgU0W+0
++Wj8mN2knCVANt69F2YrA394gbGAdJ5fOrQmL2pIhDY0jqco74fzYefbZ/VS29fR
+5jBxu4uj1P+5ZImem4Gbj1e4ZEzVBhmO55GFfBjRidj26h1oFBHZ7heDH1Bjzw72
+hipu47Gkyfr2NEx3KoCGMLCj3Btx7ASn5Ji8FoU+hCazwOU1VX55mKPU1I2250Lo
+RCASN18JyfsD5PVldJbtyrmz9gn/TKbRXTr80U2q5JhyvjhLf4lOJo/UzL5WCXED
+Smyj4jWG3R7Z8TED9xNNCxGBMXnMete+3PvzdhssvbORDwBZByogQ9xL2LUZFI/i
+eoQp0UM/L8zfP527vWjEzuDN5xwxMnhi+vCToh7J159o5ah29mP+aJnvujbXEnGa
+nrNxHzu+AGOePV8hwrGGG7hOIcPDQwkuYwzN/xT29iLp/cqf9ZhEtkGcQcIImH3b
+oJ8ifsCnSbu0GB9L06Yqh7lcyvKDTEADslIaeSEINxhO2Y1fmcYFX/Fqrrp1WnhH
+OjplXuXE0OPa0utaKC25Aplgom88L2Z8mEWcyfoB7zKOfD759AN7JKZWCYwk
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB
+iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
+cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV
+BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw
+MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV
+BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
+aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy
+dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
+AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B
+3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY
+tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/
+Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2
+VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT
+79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6
+c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT
+Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l
+c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee
+UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE
+Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd
+BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G
+A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF
+Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO
+VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3
+ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs
+8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR
+iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze
+Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ
+XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/
+qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB
+VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB
+L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG
+jjxDah2nGN59PRbxYvnKkKj9
+-----END CERTIFICATE-----

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/settings.py
----------------------------------------------------------------------
diff --git a/django_airavata/settings.py b/django_airavata/settings.py
new file mode 100644
index 0000000..cca6da1
--- /dev/null
+++ b/django_airavata/settings.py
@@ -0,0 +1,159 @@
+"""
+Django settings for django_airavata_gateway project.
+
+Generated by 'django-admin startproject' using Django 1.10.5.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/1.10/topics/settings/
+
+For the full list of settings and their values, see
+https://docs.djangoproject.com/en/1.10/ref/settings/
+"""
+
+import os
+
+# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+
+# Quick-start development settings - unsuitable for production
+# See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/
+
+# SECURITY WARNING: keep the secret key used in production secret!
+SECRET_KEY = 'bots0)m91u_i4gpw+103o%2jn#j57wjh7s@9$x*27_4^*jyku4'
+
+# SECURITY WARNING: don't run with debug turned on in production!
+DEBUG = True
+INTERNAL_IPS = ["127.0.0.1"]
+
+ALLOWED_HOSTS = []
+
+
+# Application definition
+
+INSTALLED_APPS = [
+    'django_airavata.apps.admin.apps.AdminConfig',
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    'django.contrib.messages',
+    'django.contrib.staticfiles',
+    'django_airavata.apps.auth.apps.AuthConfig',
+    'django_airavata.apps.workspace.apps.WorkspaceConfig',
+    'rest_framework',
+    'django_airavata.apps.api.apps.ApiConfig',
+    'django_airavata.apps.groups.apps.GroupsConfig',
+]
+
+MIDDLEWARE = [
+    'django.middleware.security.SecurityMiddleware',
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'django.middleware.common.CommonMiddleware',
+    'django.middleware.csrf.CsrfViewMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'django.contrib.messages.middleware.MessageMiddleware',
+    'django.middleware.clickjacking.XFrameOptionsMiddleware',
+    'django_airavata.apps.auth.middleware.authz_token_middleware',
+    'django_airavata.middleware.airavata_client',
+    'django_airavata.middleware.sharing_client',
+]
+
+ROOT_URLCONF = 'django_airavata.urls'
+
+TEMPLATES = [
+    {
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
+        'DIRS': [os.path.join(BASE_DIR, "django_airavata", "templates")],
+        'APP_DIRS': True,
+        'OPTIONS': {
+            'context_processors': [
+                'django.template.context_processors.debug',
+                'django.template.context_processors.request',
+                'django.contrib.auth.context_processors.auth',
+                'django.contrib.messages.context_processors.messages',
+            ],
+        },
+    },
+]
+
+WSGI_APPLICATION = 'django_airavata.wsgi.application'
+
+
+# Database
+# https://docs.djangoproject.com/en/1.10/ref/settings/#databases
+
+DATABASES = {
+    'default': {
+        'ENGINE': 'django.db.backends.sqlite3',
+        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
+    }
+}
+
+
+# Password validation
+# https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators
+
+AUTH_PASSWORD_VALIDATORS = [
+    {
+        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
+    },
+]
+
+
+# Internationalization
+# https://docs.djangoproject.com/en/1.10/topics/i18n/
+
+LANGUAGE_CODE = 'en-us'
+
+TIME_ZONE = 'UTC'
+
+USE_I18N = True
+
+USE_L10N = True
+
+USE_TZ = True
+
+
+# Static files (CSS, JavaScript, Images)
+# https://docs.djangoproject.com/en/1.10/howto/static-files/
+
+STATIC_URL = '/static/'
+STATICFILES_DIRS = [os.path.join(BASE_DIR, "django_airavata", "static")]
+
+AUTHENTICATION_BACKENDS = [
+    'django_airavata.apps.auth.backends.KeycloakBackend'
+]
+
+LOGIN_URL = 'django_airavata_auth:login'
+LOGIN_REDIRECT_URL = 'home'
+LOGOUT_REDIRECT_URL = 'home'
+
+LOGGING = {
+    'version': 1,
+    'handlers': {
+        'console': {
+            'class': 'logging.StreamHandler',
+        },
+    },
+    'loggers': {
+        'django_airavata': {
+            'handlers': ['console'],
+            'level': 'DEBUG' if DEBUG else 'INFO'
+        },
+    },
+}
+
+# Allow all settings to be overridden by settings_local.py file
+try:
+    from django_airavata.settings_local import *
+except ImportError:
+    pass

http://git-wip-us.apache.org/repos/asf/airavata-django-portal/blob/d8d7c37a/django_airavata/settings_local.py.sample
----------------------------------------------------------------------
diff --git a/django_airavata/settings_local.py.sample b/django_airavata/settings_local.py.sample
new file mode 100644
index 0000000..b86ef0b
--- /dev/null
+++ b/django_airavata/settings_local.py.sample
@@ -0,0 +1,34 @@
+"""
+Override default Django settings for a particular instance.
+
+Copy this file to settings_local.py and modify as appropriate. This file will
+be imported into settings.py last of all so settings in this file override any
+defaults specified in settings.py.
+"""
+
+import os
+
+# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+# Keycloak Configuration
+KEYCLOAK_CLIENT_ID = '...'
+KEYCLOAK_CLIENT_SECRET = '...'
+KEYCLOAK_AUTHORIZE_URL = '...'
+KEYCLOAK_TOKEN_URL = '...'
+KEYCLOAK_USERINFO_URL = '...'
+KEYCLOAK_LOGOUT_URL = '...'
+# Optional: specify if using self-signed certificate or certificate from unrecognized CA
+#KEYCLOAK_CA_CERTFILE = os.path.join(BASE_DIR, "django_airavata", "resources", "incommon_rsa_server_ca.pem")
+KEYCLOAK_VERIFY_SSL = True
+
+# Airavata API Configuration
+GATEWAY_ID = 'default'
+AIRAVATA_API_HOST = 'localhost'
+AIRAVATA_API_PORT = 8930
+AIRAVATA_API_SECURE = False
+
+# Sharing API Configuration
+SHARING_API_HOST = 'localhost'
+SHARING_API_PORT = 7878
+SHARING_API_SECURE = False