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 2018/10/11 21:11:17 UTC

[airavata-django-portal] branch master updated (62493fa -> dfa08cd)

This is an automated email from the ASF dual-hosted git repository.

machristie pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git.


    from 62493fa  Bug fix: check if there is a resolver_match first
     new 3d63b72  AIRAVATA-2890 Regenerated Thrift stubs
     new d1e7781  AIRAVATA-2888 Get service account token
     new 5750d9b  AIRAVATA-2888 Create account form validation
     new bdb9e4a  Add metadata field editor to app input editor
     new dfa08cd  Improving formatting of long imports

The 5 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 airavata/api/Airavata-remote                       |   7 +
 airavata/api/Airavata.py                           | 290 +++++++++++++++++++++
 .../iam/admin/services/cpi/IamAdminServices-remote |   7 +
 .../iam/admin/services/cpi/IamAdminServices.py     | 233 +++++++++++++++++
 .../applications/ApplicationInputFieldEditor.vue   |  14 +-
 .../src/components/applications/JSONEditor.vue     |  47 ++++
 django_airavata/apps/api/serializers.py            |  48 +++-
 django_airavata/apps/auth/forms.py                 |  71 +++++
 django_airavata/apps/auth/iam_admin_client.py      |  13 +
 .../django_airavata_auth/create_account.html       |  48 ++--
 django_airavata/apps/auth/urls.py                  |   2 -
 django_airavata/apps/auth/utils.py                 |  25 ++
 django_airavata/apps/auth/views.py                 |  18 +-
 setup.cfg                                          |  11 +
 14 files changed, 778 insertions(+), 56 deletions(-)
 create mode 100644 django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/JSONEditor.vue
 create mode 100644 django_airavata/apps/auth/forms.py
 create mode 100644 django_airavata/apps/auth/iam_admin_client.py


[airavata-django-portal] 04/05: Add metadata field editor to app input editor

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git

commit bdb9e4a5827e7c0f15a98deeb493352ef441874b
Author: Marcus Christie <ma...@iu.edu>
AuthorDate: Thu Oct 11 16:52:24 2018 -0400

    Add metadata field editor to app input editor
---
 .../applications/ApplicationInputFieldEditor.vue   | 14 +++++--
 .../src/components/applications/JSONEditor.vue     | 47 ++++++++++++++++++++++
 django_airavata/apps/api/serializers.py            |  8 ++++
 3 files changed, 66 insertions(+), 3 deletions(-)

diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationInputFieldEditor.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationInputFieldEditor.vue
index 99dbadf..b3efbab 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationInputFieldEditor.vue
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationInputFieldEditor.vue
@@ -26,7 +26,8 @@
       </b-form-group>
       <div class="d-flex">
         <b-form-group class="flex-fill" label="Standard Input" :label-for="id+'-standard-input'">
-          <b-form-radio-group :id="id+'-standard-input'" v-model="data.standardInput" :options="trueFalseOptions" :disabled="readonly">
+          <b-form-radio-group :id="id+'-standard-input'" v-model="data.standardInput" :options="trueFalseOptions"
+            :disabled="readonly">
           </b-form-radio-group>
         </b-form-group>
         <b-form-group class="flex-fill" label="Read Only" :label-for="id+'-read-only'">
@@ -35,8 +36,8 @@
         </b-form-group>
       </div>
       <b-form-group label="User Friendly Description" :label-for="id+'-user-friendly-description'">
-        <b-form-textarea :id="id+'-user-friendly-description'" v-model="data.userFriendlyDescription" :rows="3" :disabled="readonly"
-        />
+        <b-form-textarea :id="id+'-user-friendly-description'" v-model="data.userFriendlyDescription" :rows="3"
+          :disabled="readonly" />
       </b-form-group>
       <div class="d-flex">
         <b-form-group class="flex-fill" label="Data is staged" :label-for="id+'-data-staged'">
@@ -53,6 +54,9 @@
           :disabled="readonly">
         </b-form-radio-group>
       </b-form-group>
+      <b-form-group label="Metadata" :label-for="id+'-metadata'" description="Metadata for this input, in the JSON format">
+        <json-editor :id="id+'-metadata'" v-model="data.metaData" :rows="5" :disabled="readonly" />
+      </b-form-group>
     </b-collapse>
   </b-card>
 </template>
@@ -60,6 +64,7 @@
 <script>
 import { models } from "django-airavata-api";
 import vmodel_mixin from "../commons/vmodel_mixin";
+import JSONEditor from "./JSONEditor.vue";
 
 export default {
   name: "application-input-field-editor",
@@ -80,6 +85,9 @@ export default {
       default: false
     }
   },
+  components: {
+    'json-editor': JSONEditor
+  },
   computed: {
     inputTypeOptions() {
       return models.DataType.values.map(dataType => {
diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/JSONEditor.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/JSONEditor.vue
new file mode 100644
index 0000000..54f8941
--- /dev/null
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/JSONEditor.vue
@@ -0,0 +1,47 @@
+<template>
+  <b-form-textarea :id="id" v-model="jsonString" @input="valueChanged" :rows="rows" :disabled="disabled" :state="state" />
+</template>
+
+<script>
+export default {
+  name: "json-editor",
+  props: {
+    value: {
+      type: Object,
+      required: true
+    },
+    id: String,
+    rows: Number,
+    disabled: Boolean
+  },
+  data() {
+    return {
+      jsonString: this.value ? this.formatJSON(this.value) : null,
+      state: null
+    };
+  },
+  methods: {
+    formatJSON(value) {
+      return JSON.stringify(value, null, 4);
+    },
+    valueChanged(newValue) {
+      try {
+        if (newValue) {
+          const parsedValue = JSON.parse(newValue);
+          this.$emit("input", parsedValue);
+        } else {
+          this.$emit("input", null);
+        }
+        this.state = true;
+      } catch (e) {
+        this.state = false;
+      }
+    }
+  },
+  watch: {
+    value(newValue) {
+      this.jsonString = newValue ? this.formatJSON(newValue) : null;
+    }
+  }
+};
+</script>
diff --git a/django_airavata/apps/api/serializers.py b/django_airavata/apps/api/serializers.py
index d29f726..5be55d4 100644
--- a/django_airavata/apps/api/serializers.py
+++ b/django_airavata/apps/api/serializers.py
@@ -98,6 +98,12 @@ class StoredJSONField(serializers.JSONField):
         except Exception:
             return value
 
+    def to_internal_value(self, data):
+        try:
+            return json.dumps(data)
+        except (TypeError, ValueError):
+            self.fail('invalid')
+
 
 class OrderedListField(serializers.ListField):
 
@@ -245,6 +251,8 @@ class ApplicationModuleSerializer(
 class InputDataObjectTypeSerializer(
         thrift_utils.create_serializer_class(InputDataObjectType)):
 
+    metaData = StoredJSONField(required=False, allow_null=True)
+
     class Meta:
         required = ('name',)
 


[airavata-django-portal] 02/05: AIRAVATA-2888 Get service account token

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git

commit d1e77811c2aacae9c3ffdf0cf8595d37c41052ca
Author: Marcus Christie <ma...@iu.edu>
AuthorDate: Thu Oct 11 12:04:26 2018 -0400

    AIRAVATA-2888 Get service account token
---
 django_airavata/apps/auth/utils.py | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/django_airavata/apps/auth/utils.py b/django_airavata/apps/auth/utils.py
index a4691d7..e5d39ea 100644
--- a/django_airavata/apps/auth/utils.py
+++ b/django_airavata/apps/auth/utils.py
@@ -4,6 +4,8 @@ import time
 
 from django.conf import settings
 from django.contrib.auth import authenticate
+from oauthlib.oauth2 import BackendApplicationClient
+from requests_oauthlib import OAuth2Session
 
 from airavata.model.security.ttypes import AuthzToken
 
@@ -20,6 +22,31 @@ def get_authz_token(request):
     return None
 
 
+def get_service_account_authz_token():
+    client_id = settings.KEYCLOAK_CLIENT_ID
+    client_secret = settings.KEYCLOAK_CLIENT_SECRET
+    token_url = settings.KEYCLOAK_TOKEN_URL
+    verify_ssl = settings.KEYCLOAK_VERIFY_SSL
+
+    client = BackendApplicationClient(client_id=client_id)
+    oauth = OAuth2Session(client=client)
+    if hasattr(settings, 'KEYCLOAK_CA_CERTFILE'):
+        oauth.verify = settings.KEYCLOAK_CA_CERTFILE
+    token = oauth.fetch_token(
+        token_url=token_url,
+        client_id=client_id,
+        client_secret=client_secret,
+        verify=verify_ssl)
+
+    access_token = token.get('access_token')
+    return AuthzToken(
+        accessToken=access_token,
+        claimsMap={
+            'gatewayID': settings.GATEWAY_ID,
+            # This is a service account, so leaving userName blank for now
+            'userName': None})
+
+
 def _create_authz_token(request):
     access_token = request.session['ACCESS_TOKEN']
     username = request.user.username


[airavata-django-portal] 05/05: Improving formatting of long imports

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git

commit dfa08cde6eede35a98bc050c592f91a260c0bcae
Author: Marcus Christie <ma...@iu.edu>
AuthorDate: Thu Oct 11 17:10:33 2018 -0400

    Improving formatting of long imports
---
 django_airavata/apps/api/serializers.py | 40 +++++++++++++++++++++------------
 setup.cfg                               | 11 +++++++++
 2 files changed, 37 insertions(+), 14 deletions(-)

diff --git a/django_airavata/apps/api/serializers.py b/django_airavata/apps/api/serializers.py
index 5be55d4..73d5e25 100644
--- a/django_airavata/apps/api/serializers.py
+++ b/django_airavata/apps/api/serializers.py
@@ -9,23 +9,35 @@ from django.conf import settings
 from django.urls import reverse
 from rest_framework import serializers
 
-from airavata.model.appcatalog.appdeployment.ttypes import (ApplicationDeploymentDescription,
-                                                            ApplicationModule,
-                                                            CommandObject,
-                                                            SetEnvPaths)
-from airavata.model.appcatalog.appinterface.ttypes import \
+from airavata.model.appcatalog.appdeployment.ttypes import (
+    ApplicationDeploymentDescription,
+    ApplicationModule,
+    CommandObject,
+    SetEnvPaths
+)
+from airavata.model.appcatalog.appinterface.ttypes import (
     ApplicationInterfaceDescription
-from airavata.model.appcatalog.computeresource.ttypes import (BatchQueue,
-                                                              ComputeResourceDescription)
-from airavata.model.appcatalog.groupresourceprofile.ttypes import \
+)
+from airavata.model.appcatalog.computeresource.ttypes import (
+    BatchQueue,
+    ComputeResourceDescription
+)
+from airavata.model.appcatalog.groupresourceprofile.ttypes import (
     GroupResourceProfile
+)
 from airavata.model.application.io.ttypes import InputDataObjectType
-from airavata.model.credential.store.ttypes import (CredentialSummary,
-                                                    SummaryType)
-from airavata.model.data.replica.ttypes import (DataProductModel,
-                                                DataReplicaLocationModel)
-from airavata.model.experiment.ttypes import (ExperimentModel,
-                                              ExperimentSummaryModel)
+from airavata.model.credential.store.ttypes import (
+    CredentialSummary,
+    SummaryType
+)
+from airavata.model.data.replica.ttypes import (
+    DataProductModel,
+    DataReplicaLocationModel
+)
+from airavata.model.experiment.ttypes import (
+    ExperimentModel,
+    ExperimentSummaryModel
+)
 from airavata.model.group.ttypes import GroupModel, ResourcePermissionType
 from airavata.model.job.ttypes import JobModel
 from airavata.model.status.ttypes import ExperimentStatus
diff --git a/setup.cfg b/setup.cfg
index b261700..82f32c9 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -5,3 +5,14 @@ exclude =
     node_modules,
     settings_local.py,
     */migrations
+
+[isort]
+# Set multi_line_output to option 3 - Vertical Hanging Indent
+
+# from third_party import (
+#     lib1,
+#     lib2,
+#     lib3,
+#     lib4,
+# )
+multi_line_output = 3


[airavata-django-portal] 01/05: AIRAVATA-2890 Regenerated Thrift stubs

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git

commit 3d63b72b5dc048149e239c49a5862e620a869c42
Author: Marcus Christie <ma...@iu.edu>
AuthorDate: Thu Oct 11 11:37:52 2018 -0400

    AIRAVATA-2890 Regenerated Thrift stubs
---
 airavata/api/Airavata-remote                       |   7 +
 airavata/api/Airavata.py                           | 290 +++++++++++++++++++++
 .../iam/admin/services/cpi/IamAdminServices-remote |   7 +
 .../iam/admin/services/cpi/IamAdminServices.py     | 233 +++++++++++++++++
 4 files changed, 537 insertions(+)

diff --git a/airavata/api/Airavata-remote b/airavata/api/Airavata-remote
index 895e181..006305b 100755
--- a/airavata/api/Airavata-remote
+++ b/airavata/api/Airavata-remote
@@ -159,6 +159,7 @@ if len(sys.argv) <= 1 or sys.argv[1] == '--help':
     print('  bool deleteGatewayStoragePreference(AuthzToken authzToken, string gatewayID, string storageId)')
     print('   getSSHAccountProvisioners(AuthzToken authzToken)')
     print('  bool doesUserHaveSSHAccount(AuthzToken authzToken, string computeResourceId, string userId)')
+    print('  bool isSSHSetupCompleteForUserComputeResourcePreference(AuthzToken authzToken, string computeResourceId, string airavataCredStoreToken)')
     print('  UserComputeResourcePreference setupUserComputeResourcePreferencesForSSH(AuthzToken authzToken, string computeResourceId, string userId, string airavataCredStoreToken)')
     print('  string registerUserResourceProfile(AuthzToken authzToken, UserResourceProfile userResourceProfile)')
     print('  bool isUserResourceProfileExists(AuthzToken authzToken, string userId, string gatewayID)')
@@ -1100,6 +1101,12 @@ elif cmd == 'doesUserHaveSSHAccount':
         sys.exit(1)
     pp.pprint(client.doesUserHaveSSHAccount(eval(args[0]), args[1], args[2],))
 
+elif cmd == 'isSSHSetupCompleteForUserComputeResourcePreference':
+    if len(args) != 3:
+        print('isSSHSetupCompleteForUserComputeResourcePreference requires 3 args')
+        sys.exit(1)
+    pp.pprint(client.isSSHSetupCompleteForUserComputeResourcePreference(eval(args[0]), args[1], args[2],))
+
 elif cmd == 'setupUserComputeResourcePreferencesForSSH':
     if len(args) != 4:
         print('setupUserComputeResourcePreferencesForSSH requires 4 args')
diff --git a/airavata/api/Airavata.py b/airavata/api/Airavata.py
index 9f7a0ba..642f8f1 100644
--- a/airavata/api/Airavata.py
+++ b/airavata/api/Airavata.py
@@ -2857,6 +2857,17 @@ class Iface(object):
         """
         pass
 
+    def isSSHSetupCompleteForUserComputeResourcePreference(self, authzToken, computeResourceId, airavataCredStoreToken):
+        """
+        Check if SSH account setup is complete for this user on the given compute resource.
+
+        Parameters:
+         - authzToken
+         - computeResourceId
+         - airavataCredStoreToken
+        """
+        pass
+
     def setupUserComputeResourcePreferencesForSSH(self, authzToken, computeResourceId, userId, airavataCredStoreToken):
         """
         Setup and return a UserComputeResourcePreference object for this user to SSH into the given compute resource with
@@ -10967,6 +10978,51 @@ class Client(Iface):
             raise result.ae
         raise TApplicationException(TApplicationException.MISSING_RESULT, "doesUserHaveSSHAccount failed: unknown result")
 
+    def isSSHSetupCompleteForUserComputeResourcePreference(self, authzToken, computeResourceId, airavataCredStoreToken):
+        """
+        Check if SSH account setup is complete for this user on the given compute resource.
+
+        Parameters:
+         - authzToken
+         - computeResourceId
+         - airavataCredStoreToken
+        """
+        self.send_isSSHSetupCompleteForUserComputeResourcePreference(authzToken, computeResourceId, airavataCredStoreToken)
+        return self.recv_isSSHSetupCompleteForUserComputeResourcePreference()
+
+    def send_isSSHSetupCompleteForUserComputeResourcePreference(self, authzToken, computeResourceId, airavataCredStoreToken):
+        self._oprot.writeMessageBegin('isSSHSetupCompleteForUserComputeResourcePreference', TMessageType.CALL, self._seqid)
+        args = isSSHSetupCompleteForUserComputeResourcePreference_args()
+        args.authzToken = authzToken
+        args.computeResourceId = computeResourceId
+        args.airavataCredStoreToken = airavataCredStoreToken
+        args.write(self._oprot)
+        self._oprot.writeMessageEnd()
+        self._oprot.trans.flush()
+
+    def recv_isSSHSetupCompleteForUserComputeResourcePreference(self):
+        iprot = self._iprot
+        (fname, mtype, rseqid) = iprot.readMessageBegin()
+        if mtype == TMessageType.EXCEPTION:
+            x = TApplicationException()
+            x.read(iprot)
+            iprot.readMessageEnd()
+            raise x
+        result = isSSHSetupCompleteForUserComputeResourcePreference_result()
+        result.read(iprot)
+        iprot.readMessageEnd()
+        if result.success is not None:
+            return result.success
+        if result.ire is not None:
+            raise result.ire
+        if result.ace is not None:
+            raise result.ace
+        if result.ase is not None:
+            raise result.ase
+        if result.ae is not None:
+            raise result.ae
+        raise TApplicationException(TApplicationException.MISSING_RESULT, "isSSHSetupCompleteForUserComputeResourcePreference failed: unknown result")
+
     def setupUserComputeResourcePreferencesForSSH(self, authzToken, computeResourceId, userId, airavataCredStoreToken):
         """
         Setup and return a UserComputeResourcePreference object for this user to SSH into the given compute resource with
@@ -13554,6 +13610,7 @@ class Processor(Iface, TProcessor):
         self._processMap["deleteGatewayStoragePreference"] = Processor.process_deleteGatewayStoragePreference
         self._processMap["getSSHAccountProvisioners"] = Processor.process_getSSHAccountProvisioners
         self._processMap["doesUserHaveSSHAccount"] = Processor.process_doesUserHaveSSHAccount
+        self._processMap["isSSHSetupCompleteForUserComputeResourcePreference"] = Processor.process_isSSHSetupCompleteForUserComputeResourcePreference
         self._processMap["setupUserComputeResourcePreferencesForSSH"] = Processor.process_setupUserComputeResourcePreferencesForSSH
         self._processMap["registerUserResourceProfile"] = Processor.process_registerUserResourceProfile
         self._processMap["isUserResourceProfileExists"] = Processor.process_isUserResourceProfileExists
@@ -17840,6 +17897,37 @@ class Processor(Iface, TProcessor):
         oprot.writeMessageEnd()
         oprot.trans.flush()
 
+    def process_isSSHSetupCompleteForUserComputeResourcePreference(self, seqid, iprot, oprot):
+        args = isSSHSetupCompleteForUserComputeResourcePreference_args()
+        args.read(iprot)
+        iprot.readMessageEnd()
+        result = isSSHSetupCompleteForUserComputeResourcePreference_result()
+        try:
+            result.success = self._handler.isSSHSetupCompleteForUserComputeResourcePreference(args.authzToken, args.computeResourceId, args.airavataCredStoreToken)
+            msg_type = TMessageType.REPLY
+        except (TTransport.TTransportException, KeyboardInterrupt, SystemExit):
+            raise
+        except airavata.api.error.ttypes.InvalidRequestException as ire:
+            msg_type = TMessageType.REPLY
+            result.ire = ire
+        except airavata.api.error.ttypes.AiravataClientException as ace:
+            msg_type = TMessageType.REPLY
+            result.ace = ace
+        except airavata.api.error.ttypes.AiravataSystemException as ase:
+            msg_type = TMessageType.REPLY
+            result.ase = ase
+        except airavata.api.error.ttypes.AuthorizationException as ae:
+            msg_type = TMessageType.REPLY
+            result.ae = ae
+        except Exception as ex:
+            msg_type = TMessageType.EXCEPTION
+            logging.exception(ex)
+            result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error')
+        oprot.writeMessageBegin("isSSHSetupCompleteForUserComputeResourcePreference", msg_type, seqid)
+        result.write(oprot)
+        oprot.writeMessageEnd()
+        oprot.trans.flush()
+
     def process_setupUserComputeResourcePreferencesForSSH(self, seqid, iprot, oprot):
         args = setupUserComputeResourcePreferencesForSSH_args()
         args.read(iprot)
@@ -46611,6 +46699,208 @@ class doesUserHaveSSHAccount_result(object):
         return not (self == other)
 
 
+class isSSHSetupCompleteForUserComputeResourcePreference_args(object):
+    """
+    Attributes:
+     - authzToken
+     - computeResourceId
+     - airavataCredStoreToken
+    """
+
+    thrift_spec = (
+        None,  # 0
+        (1, TType.STRUCT, 'authzToken', (airavata.model.security.ttypes.AuthzToken, airavata.model.security.ttypes.AuthzToken.thrift_spec), None, ),  # 1
+        (2, TType.STRING, 'computeResourceId', 'UTF8', None, ),  # 2
+        (3, TType.STRING, 'airavataCredStoreToken', 'UTF8', None, ),  # 3
+    )
+
+    def __init__(self, authzToken=None, computeResourceId=None, airavataCredStoreToken=None,):
+        self.authzToken = authzToken
+        self.computeResourceId = computeResourceId
+        self.airavataCredStoreToken = airavataCredStoreToken
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec))
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 1:
+                if ftype == TType.STRUCT:
+                    self.authzToken = airavata.model.security.ttypes.AuthzToken()
+                    self.authzToken.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            elif fid == 2:
+                if ftype == TType.STRING:
+                    self.computeResourceId = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 3:
+                if ftype == TType.STRING:
+                    self.airavataCredStoreToken = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, (self.__class__, self.thrift_spec)))
+            return
+        oprot.writeStructBegin('isSSHSetupCompleteForUserComputeResourcePreference_args')
+        if self.authzToken is not None:
+            oprot.writeFieldBegin('authzToken', TType.STRUCT, 1)
+            self.authzToken.write(oprot)
+            oprot.writeFieldEnd()
+        if self.computeResourceId is not None:
+            oprot.writeFieldBegin('computeResourceId', TType.STRING, 2)
+            oprot.writeString(self.computeResourceId.encode('utf-8') if sys.version_info[0] == 2 else self.computeResourceId)
+            oprot.writeFieldEnd()
+        if self.airavataCredStoreToken is not None:
+            oprot.writeFieldBegin('airavataCredStoreToken', TType.STRING, 3)
+            oprot.writeString(self.airavataCredStoreToken.encode('utf-8') if sys.version_info[0] == 2 else self.airavataCredStoreToken)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        if self.authzToken is None:
+            raise TProtocolException(message='Required field authzToken is unset!')
+        if self.computeResourceId is None:
+            raise TProtocolException(message='Required field computeResourceId is unset!')
+        if self.airavataCredStoreToken is None:
+            raise TProtocolException(message='Required field airavataCredStoreToken is unset!')
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+
+
+class isSSHSetupCompleteForUserComputeResourcePreference_result(object):
+    """
+    Attributes:
+     - success
+     - ire
+     - ace
+     - ase
+     - ae
+    """
+
+    thrift_spec = (
+        (0, TType.BOOL, 'success', None, None, ),  # 0
+        (1, TType.STRUCT, 'ire', (airavata.api.error.ttypes.InvalidRequestException, airavata.api.error.ttypes.InvalidRequestException.thrift_spec), None, ),  # 1
+        (2, TType.STRUCT, 'ace', (airavata.api.error.ttypes.AiravataClientException, airavata.api.error.ttypes.AiravataClientException.thrift_spec), None, ),  # 2
+        (3, TType.STRUCT, 'ase', (airavata.api.error.ttypes.AiravataSystemException, airavata.api.error.ttypes.AiravataSystemException.thrift_spec), None, ),  # 3
+        (4, TType.STRUCT, 'ae', (airavata.api.error.ttypes.AuthorizationException, airavata.api.error.ttypes.AuthorizationException.thrift_spec), None, ),  # 4
+    )
+
+    def __init__(self, success=None, ire=None, ace=None, ase=None, ae=None,):
+        self.success = success
+        self.ire = ire
+        self.ace = ace
+        self.ase = ase
+        self.ae = ae
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec))
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 0:
+                if ftype == TType.BOOL:
+                    self.success = iprot.readBool()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 1:
+                if ftype == TType.STRUCT:
+                    self.ire = airavata.api.error.ttypes.InvalidRequestException()
+                    self.ire.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            elif fid == 2:
+                if ftype == TType.STRUCT:
+                    self.ace = airavata.api.error.ttypes.AiravataClientException()
+                    self.ace.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            elif fid == 3:
+                if ftype == TType.STRUCT:
+                    self.ase = airavata.api.error.ttypes.AiravataSystemException()
+                    self.ase.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            elif fid == 4:
+                if ftype == TType.STRUCT:
+                    self.ae = airavata.api.error.ttypes.AuthorizationException()
+                    self.ae.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, (self.__class__, self.thrift_spec)))
+            return
+        oprot.writeStructBegin('isSSHSetupCompleteForUserComputeResourcePreference_result')
+        if self.success is not None:
+            oprot.writeFieldBegin('success', TType.BOOL, 0)
+            oprot.writeBool(self.success)
+            oprot.writeFieldEnd()
+        if self.ire is not None:
+            oprot.writeFieldBegin('ire', TType.STRUCT, 1)
+            self.ire.write(oprot)
+            oprot.writeFieldEnd()
+        if self.ace is not None:
+            oprot.writeFieldBegin('ace', TType.STRUCT, 2)
+            self.ace.write(oprot)
+            oprot.writeFieldEnd()
+        if self.ase is not None:
+            oprot.writeFieldBegin('ase', TType.STRUCT, 3)
+            self.ase.write(oprot)
+            oprot.writeFieldEnd()
+        if self.ae is not None:
+            oprot.writeFieldBegin('ae', TType.STRUCT, 4)
+            self.ae.write(oprot)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+
+
 class setupUserComputeResourcePreferencesForSSH_args(object):
     """
     Attributes:
diff --git a/airavata/service/profile/iam/admin/services/cpi/IamAdminServices-remote b/airavata/service/profile/iam/admin/services/cpi/IamAdminServices-remote
index a54cafd..683c411 100755
--- a/airavata/service/profile/iam/admin/services/cpi/IamAdminServices-remote
+++ b/airavata/service/profile/iam/admin/services/cpi/IamAdminServices-remote
@@ -26,6 +26,7 @@ if len(sys.argv) <= 1 or sys.argv[1] == '--help':
     print('Functions:')
     print('  string getAPIVersion(AuthzToken authzToken)')
     print('  Gateway setUpGateway(AuthzToken authzToken, Gateway gateway)')
+    print('  bool isUsernameAvailable(AuthzToken authzToken, string username)')
     print('  bool registerUser(AuthzToken authzToken, string username, string emailAddress, string firstName, string lastName, string newPassword)')
     print('  bool enableUser(AuthzToken authzToken, string username)')
     print('  bool isUserEnabled(AuthzToken authzToken, string username)')
@@ -126,6 +127,12 @@ elif cmd == 'setUpGateway':
         sys.exit(1)
     pp.pprint(client.setUpGateway(eval(args[0]), eval(args[1]),))
 
+elif cmd == 'isUsernameAvailable':
+    if len(args) != 2:
+        print('isUsernameAvailable requires 2 args')
+        sys.exit(1)
+    pp.pprint(client.isUsernameAvailable(eval(args[0]), args[1],))
+
 elif cmd == 'registerUser':
     if len(args) != 6:
         print('registerUser requires 6 args')
diff --git a/airavata/service/profile/iam/admin/services/cpi/IamAdminServices.py b/airavata/service/profile/iam/admin/services/cpi/IamAdminServices.py
index 1e2cb09..58b46fe 100644
--- a/airavata/service/profile/iam/admin/services/cpi/IamAdminServices.py
+++ b/airavata/service/profile/iam/admin/services/cpi/IamAdminServices.py
@@ -31,6 +31,14 @@ class Iface(object):
         """
         pass
 
+    def isUsernameAvailable(self, authzToken, username):
+        """
+        Parameters:
+         - authzToken
+         - username
+        """
+        pass
+
     def registerUser(self, authzToken, username, emailAddress, firstName, lastName, newPassword):
         """
         Parameters:
@@ -191,6 +199,43 @@ class Client(Iface):
             raise result.ae
         raise TApplicationException(TApplicationException.MISSING_RESULT, "setUpGateway failed: unknown result")
 
+    def isUsernameAvailable(self, authzToken, username):
+        """
+        Parameters:
+         - authzToken
+         - username
+        """
+        self.send_isUsernameAvailable(authzToken, username)
+        return self.recv_isUsernameAvailable()
+
+    def send_isUsernameAvailable(self, authzToken, username):
+        self._oprot.writeMessageBegin('isUsernameAvailable', TMessageType.CALL, self._seqid)
+        args = isUsernameAvailable_args()
+        args.authzToken = authzToken
+        args.username = username
+        args.write(self._oprot)
+        self._oprot.writeMessageEnd()
+        self._oprot.trans.flush()
+
+    def recv_isUsernameAvailable(self):
+        iprot = self._iprot
+        (fname, mtype, rseqid) = iprot.readMessageBegin()
+        if mtype == TMessageType.EXCEPTION:
+            x = TApplicationException()
+            x.read(iprot)
+            iprot.readMessageEnd()
+            raise x
+        result = isUsernameAvailable_result()
+        result.read(iprot)
+        iprot.readMessageEnd()
+        if result.success is not None:
+            return result.success
+        if result.Idse is not None:
+            raise result.Idse
+        if result.ae is not None:
+            raise result.ae
+        raise TApplicationException(TApplicationException.MISSING_RESULT, "isUsernameAvailable failed: unknown result")
+
     def registerUser(self, authzToken, username, emailAddress, firstName, lastName, newPassword):
         """
         Parameters:
@@ -545,6 +590,7 @@ class Processor(Iface, TProcessor):
         self._processMap = {}
         self._processMap["getAPIVersion"] = Processor.process_getAPIVersion
         self._processMap["setUpGateway"] = Processor.process_setUpGateway
+        self._processMap["isUsernameAvailable"] = Processor.process_isUsernameAvailable
         self._processMap["registerUser"] = Processor.process_registerUser
         self._processMap["enableUser"] = Processor.process_enableUser
         self._processMap["isUserEnabled"] = Processor.process_isUserEnabled
@@ -620,6 +666,31 @@ class Processor(Iface, TProcessor):
         oprot.writeMessageEnd()
         oprot.trans.flush()
 
+    def process_isUsernameAvailable(self, seqid, iprot, oprot):
+        args = isUsernameAvailable_args()
+        args.read(iprot)
+        iprot.readMessageEnd()
+        result = isUsernameAvailable_result()
+        try:
+            result.success = self._handler.isUsernameAvailable(args.authzToken, args.username)
+            msg_type = TMessageType.REPLY
+        except (TTransport.TTransportException, KeyboardInterrupt, SystemExit):
+            raise
+        except airavata.service.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException as Idse:
+            msg_type = TMessageType.REPLY
+            result.Idse = Idse
+        except airavata.api.error.ttypes.AuthorizationException as ae:
+            msg_type = TMessageType.REPLY
+            result.ae = ae
+        except Exception as ex:
+            msg_type = TMessageType.EXCEPTION
+            logging.exception(ex)
+            result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error')
+        oprot.writeMessageBegin("isUsernameAvailable", msg_type, seqid)
+        result.write(oprot)
+        oprot.writeMessageEnd()
+        oprot.trans.flush()
+
     def process_registerUser(self, seqid, iprot, oprot):
         args = registerUser_args()
         args.read(iprot)
@@ -1160,6 +1231,168 @@ class setUpGateway_result(object):
         return not (self == other)
 
 
+class isUsernameAvailable_args(object):
+    """
+    Attributes:
+     - authzToken
+     - username
+    """
+
+    thrift_spec = (
+        None,  # 0
+        (1, TType.STRUCT, 'authzToken', (airavata.model.security.ttypes.AuthzToken, airavata.model.security.ttypes.AuthzToken.thrift_spec), None, ),  # 1
+        (2, TType.STRING, 'username', 'UTF8', None, ),  # 2
+    )
+
+    def __init__(self, authzToken=None, username=None,):
+        self.authzToken = authzToken
+        self.username = username
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec))
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 1:
+                if ftype == TType.STRUCT:
+                    self.authzToken = airavata.model.security.ttypes.AuthzToken()
+                    self.authzToken.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            elif fid == 2:
+                if ftype == TType.STRING:
+                    self.username = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, (self.__class__, self.thrift_spec)))
+            return
+        oprot.writeStructBegin('isUsernameAvailable_args')
+        if self.authzToken is not None:
+            oprot.writeFieldBegin('authzToken', TType.STRUCT, 1)
+            self.authzToken.write(oprot)
+            oprot.writeFieldEnd()
+        if self.username is not None:
+            oprot.writeFieldBegin('username', TType.STRING, 2)
+            oprot.writeString(self.username.encode('utf-8') if sys.version_info[0] == 2 else self.username)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        if self.authzToken is None:
+            raise TProtocolException(message='Required field authzToken is unset!')
+        if self.username is None:
+            raise TProtocolException(message='Required field username is unset!')
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+
+
+class isUsernameAvailable_result(object):
+    """
+    Attributes:
+     - success
+     - Idse
+     - ae
+    """
+
+    thrift_spec = (
+        (0, TType.BOOL, 'success', None, None, ),  # 0
+        (1, TType.STRUCT, 'Idse', (airavata.service.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException, airavata.service.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException.thrift_spec), None, ),  # 1
+        (2, TType.STRUCT, 'ae', (airavata.api.error.ttypes.AuthorizationException, airavata.api.error.ttypes.AuthorizationException.thrift_spec), None, ),  # 2
+    )
+
+    def __init__(self, success=None, Idse=None, ae=None,):
+        self.success = success
+        self.Idse = Idse
+        self.ae = ae
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec))
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 0:
+                if ftype == TType.BOOL:
+                    self.success = iprot.readBool()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 1:
+                if ftype == TType.STRUCT:
+                    self.Idse = airavata.service.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException()
+                    self.Idse.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            elif fid == 2:
+                if ftype == TType.STRUCT:
+                    self.ae = airavata.api.error.ttypes.AuthorizationException()
+                    self.ae.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, (self.__class__, self.thrift_spec)))
+            return
+        oprot.writeStructBegin('isUsernameAvailable_result')
+        if self.success is not None:
+            oprot.writeFieldBegin('success', TType.BOOL, 0)
+            oprot.writeBool(self.success)
+            oprot.writeFieldEnd()
+        if self.Idse is not None:
+            oprot.writeFieldBegin('Idse', TType.STRUCT, 1)
+            self.Idse.write(oprot)
+            oprot.writeFieldEnd()
+        if self.ae is not None:
+            oprot.writeFieldBegin('ae', TType.STRUCT, 2)
+            self.ae.write(oprot)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+
+
 class registerUser_args(object):
     """
     Attributes:


[airavata-django-portal] 03/05: AIRAVATA-2888 Create account form validation

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git

commit 5750d9be3b6a206383f85e2edafdc7db1d787e1c
Author: Marcus Christie <ma...@iu.edu>
AuthorDate: Thu Oct 11 12:57:34 2018 -0400

    AIRAVATA-2888 Create account form validation
---
 django_airavata/apps/auth/forms.py                 | 71 ++++++++++++++++++++++
 django_airavata/apps/auth/iam_admin_client.py      | 13 ++++
 .../django_airavata_auth/create_account.html       | 48 ++++++---------
 django_airavata/apps/auth/urls.py                  |  2 -
 django_airavata/apps/auth/utils.py                 |  6 +-
 django_airavata/apps/auth/views.py                 | 18 ++++--
 6 files changed, 115 insertions(+), 43 deletions(-)

diff --git a/django_airavata/apps/auth/forms.py b/django_airavata/apps/auth/forms.py
new file mode 100644
index 0000000..007d05f
--- /dev/null
+++ b/django_airavata/apps/auth/forms.py
@@ -0,0 +1,71 @@
+from django import forms
+
+from . import iam_admin_client
+
+
+class CreateAccountForm(forms.Form):
+    error_css_class = "is-invalid"
+    username = forms.CharField(
+        label='Username',
+        widget=forms.TextInput(attrs={'class': 'form-control',
+                                      'placeholder': 'Username'}))
+    password = forms.CharField(
+        label='Password',
+        widget=forms.PasswordInput(attrs={'class': 'form-control',
+                                          'placeholder': 'Password'}))
+    password_again = forms.CharField(
+        label='Password (again)',
+        widget=forms.PasswordInput(attrs={'class': 'form-control',
+                                          'placeholder': 'Password (again)'}))
+    email = forms.EmailField(
+        label='E-mail',
+        widget=forms.EmailInput(attrs={'class': 'form-control',
+                                       'placeholder': 'email@example.com'}))
+    email_again = forms.EmailField(
+        label='E-mail (again)',
+        widget=forms.EmailInput(
+            attrs={
+                'class': 'form-control',
+                'placeholder': 'email@example.com (again)'}))
+    first_name = forms.CharField(
+        label='First Name',
+        widget=forms.TextInput(attrs={'class': 'form-control',
+                                      'placeholder': 'First Name'}))
+    last_name = forms.CharField(
+        label='Last Name',
+        widget=forms.TextInput(attrs={'class': 'form-control',
+                                      'placeholder': 'Last Name'}))
+
+    def clean(self):
+        cleaned_data = super().clean()
+        password = cleaned_data.get('password')
+        password_again = cleaned_data.get('password_again')
+
+        if password != password_again:
+            self.add_error(
+                'password',
+                forms.ValidationError("Passwords do not match"))
+            self.add_error(
+                'password_again',
+                forms.ValidationError("Passwords do not match"))
+
+        email = cleaned_data.get('email')
+        email_again = cleaned_data.get('email_again')
+        if email != email_again:
+            self.add_error(
+                'email',
+                forms.ValidationError("E-mail addresses do not match")
+            )
+            self.add_error(
+                'email_again',
+                forms.ValidationError("E-mail addresses do not match")
+            )
+
+        username = cleaned_data.get('username')
+        if not iam_admin_client.is_username_available(username):
+            self.add_error(
+                'username',
+                forms.ValidationError("That username is not available")
+            )
+
+        return cleaned_data
diff --git a/django_airavata/apps/auth/iam_admin_client.py b/django_airavata/apps/auth/iam_admin_client.py
new file mode 100644
index 0000000..5e619fe
--- /dev/null
+++ b/django_airavata/apps/auth/iam_admin_client.py
@@ -0,0 +1,13 @@
+"""
+Wrapper around the IAM Admin Services client.
+"""
+
+from django_airavata.utils import get_iam_admin_client
+
+from . import utils
+
+
+def is_username_available(username):
+    with get_iam_admin_client() as iam_admin_client:
+        authz_token = utils.get_service_account_authz_token()
+        return iam_admin_client.isUsernameAvailable(authz_token, username)
diff --git a/django_airavata/apps/auth/templates/django_airavata_auth/create_account.html b/django_airavata/apps/auth/templates/django_airavata_auth/create_account.html
index 8c063f2..1da2a23 100644
--- a/django_airavata/apps/auth/templates/django_airavata_auth/create_account.html
+++ b/django_airavata/apps/auth/templates/django_airavata_auth/create_account.html
@@ -30,40 +30,26 @@
           {% else %}
           <h5 class="card-title">Create a {{ options.password.name }} account</h5>
           {% endif %}
-          <form action="{% url 'django_airavata_auth:handle_create_account' %}" method="post">
+          <form action="{% url 'django_airavata_auth:create_account' %}" method="post">
+            {{ form.non_field_errors }}
             {% csrf_token %}
+
+            {% for field in form %}
             <div class="form-group">
-              <label for="username">Username</label>
-              <input type="text" class="form-control" id="username" name="username" placeholder="Username" value="{{ username }}"
-                required>
-            </div>
-            <div class="form-group">
-              <label for="password">Password</label>
-              <input type="password" class="form-control" id="password" name="password" placeholder="Password" required>
-            </div>
-            <div class="form-group">
-              <label for="password_again">Password (again)</label>
-              <input type="password" class="form-control" id="password_again" name="password_again" placeholder="Password (again)"
-                required>
-            </div>
-            <div class="form-group">
-              <label for="email">E-mail</label>
-              <input type="email" class="form-control" id="email" name="email" placeholder="email@example.com" required>
-            </div>
-            <div class="form-group">
-              <label for="email_again">E-mail (again)</label>
-              <input type="email" class="form-control" id="email_again" name="email_again" placeholder="email@example.com (again)"
-                required>
-            </div>
-            <div class="form-group">
-              <label for="first_name">First Name</label>
-              <input type="text" class="form-control" id="first_name" name="first_name" placeholder="First Name"
-                required>
-            </div>
-            <div class="form-group">
-              <label for="last_name">Last Name</label>
-              <input type="text" class="form-control" id="last_name" name="last_name" placeholder="Last Name" required>
+              <label for="{{ field.id_for_label }}">{{ field.label }}</label>
+              <input id="{{ field.id_for_label }}" type="{{ field.field.widget.input_type }}"
+                class="form-control{% if field.errors %} is-invalid{% endif %}" name="{{ field.name }}"
+                placeholder="{{ field.field.widget.attrs.placeholder }}"
+                {% if field.value %} value="{{ field.value }}" {% endif %}
+                {% if field.field.required %} required {% endif %} />
+              <div class="invalid-feedback">
+                {% for error in field.errors %}
+                {{ error | escape }}
+                {% endfor %}
+              </div>
             </div>
+            {% endfor %}
+
             <button type="submit" class="btn btn-primary btn-block">
               Create
             </button>
diff --git a/django_airavata/apps/auth/urls.py b/django_airavata/apps/auth/urls.py
index 33e341e..b605d03 100644
--- a/django_airavata/apps/auth/urls.py
+++ b/django_airavata/apps/auth/urls.py
@@ -13,6 +13,4 @@ urlpatterns = [
     url(r'^callback', views.callback, name='callback'),
     url(r'^error', views.auth_error, name='error'),
     url(r'^create-account$', views.create_account, name='create_account'),
-    url(r'^handle-create-account$', views.handle_create_account,
-        name='handle_create_account'),
 ]
diff --git a/django_airavata/apps/auth/utils.py b/django_airavata/apps/auth/utils.py
index e5d39ea..4e949f1 100644
--- a/django_airavata/apps/auth/utils.py
+++ b/django_airavata/apps/auth/utils.py
@@ -41,10 +41,8 @@ def get_service_account_authz_token():
     access_token = token.get('access_token')
     return AuthzToken(
         accessToken=access_token,
-        claimsMap={
-            'gatewayID': settings.GATEWAY_ID,
-            # This is a service account, so leaving userName blank for now
-            'userName': None})
+        # This is a service account, so leaving out userName for now
+        claimsMap={'gatewayID': settings.GATEWAY_ID})
 
 
 def _create_authz_token(request):
diff --git a/django_airavata/apps/auth/views.py b/django_airavata/apps/auth/views.py
index 524586f..612022c 100644
--- a/django_airavata/apps/auth/views.py
+++ b/django_airavata/apps/auth/views.py
@@ -7,6 +7,8 @@ from django.shortcuts import redirect, render, resolve_url
 from django.urls import reverse
 from requests_oauthlib import OAuth2Session
 
+from . import forms
+
 logger = logging.getLogger(__name__)
 
 
@@ -91,12 +93,16 @@ def auth_error(request):
 
 
 def create_account(request):
+    if request.method == 'POST':
+        form = forms.CreateAccountForm(request.POST)
+        if form.is_valid():
+            # TODO: IAM registerUser
+            # TODO: send email account verification email
+            # TODO: success message
+            return redirect(reverse('django_airavata_auth:login'))
+    else:
+        form = forms.CreateAccountForm()
     return render(request, 'django_airavata_auth/create_account.html', {
         'options': settings.AUTHENTICATION_OPTIONS,
-    })
-
-
-def handle_create_account(request):
-    return render(request, 'django_airavata_auth/create_account.html', {
-        'options': settings.AUTHENTICATION_OPTIONS,
+        'form': form
     })