airavata-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From machris...@apache.org
Subject [airavata-django-portal] 04/04: AIRAVATA-2727 Credential token editor
Date Thu, 16 Aug 2018 13:19:42 GMT
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 6d6d0ac0a98e5dce7f161a599ef01a7380158d4f
Author: Marcus Christie <machrist@iu.edu>
AuthorDate: Wed Aug 15 15:31:19 2018 -0400

    AIRAVATA-2727 Credential token editor
---
 .../ComputePreference.vue                          | 31 +++++++++++++++++++++-
 django_airavata/apps/api/serializers.py            | 16 +++++++----
 .../api/static/django_airavata_api/js/index.js     |  3 +++
 .../django_airavata_api/js/models/BaseEnum.js      | 10 ++++---
 .../django_airavata_api/js/models/BaseModel.js     |  9 +++++--
 .../js/models/CredentialSummary.js                 | 25 +++++++++++++++++
 .../django_airavata_api/js/models/SummaryType.js   |  9 +++++++
 .../django_airavata_api/js/service_config.js       | 10 ++++++-
 django_airavata/apps/api/thrift_utils.py           | 27 +++++++++++++++++--
 django_airavata/apps/api/urls.py                   |  2 ++
 django_airavata/apps/api/views.py                  | 10 +++++++
 11 files changed, 137 insertions(+), 15 deletions(-)

diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue
b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue
index a92ec70..a43a971 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue
@@ -17,6 +17,15 @@
                 v-model="data.loginUserName">
               </b-form-input>
             </b-form-group>
+            <b-form-group label="SSH Credential" label-for="credential-store-token">
+              <b-form-select id="credential-store-token"
+                v-model="data.resourceSpecificCredentialStoreToken"
+                :options="credentialStoreTokenOptions">
+                <template slot="first">
+                  <option :value="null"><em>Use this profile's default SSH credential</em></option>
+                </template>
+              </b-form-select>
+            </b-form-group>
             <b-form-group label="Allocation Project Number" label-for="allocation-number">
               <b-form-input id="allocation-number" type="text"
                 v-model="data.allocationProjectNumber">
@@ -116,6 +125,7 @@
       } else if (!this.computeResourcePolicy) {
         this.createDefaultComputeResourcePolicy(computeResourcePromise);
       }
+      this.fetchCredentialSummaries();
     },
     data: function () {
       return {
@@ -126,7 +136,22 @@
         computeResource: {
           batchQueues: [],
           jobSubmissionInterfaces: []
-        }
+        },
+        credentialSummaries: [],
+      }
+    },
+    computed: {
+      credentialStoreTokenOptions: function() {
+        const options = this.credentialSummaries
+          .filter(summary => summary.type === models.SummaryType.SSH)
+          .map(summary => {
+            return {
+              value: summary.token,
+              text: summary.description
+            }
+          });
+        options.sort((a, b) => a.text.toLowerCase().localeCompare(b.text.toLowerCase()));
+        return options;
       }
     },
     mixins: [VModelMixin],
@@ -168,6 +193,10 @@
           return this.computeResource = value;
         });
       },
+      fetchCredentialSummaries: function() {
+        return services.CredentialSummaryService.list()
+          .then(summaries => this.credentialSummaries = summaries);
+      },
       save: function() {
         let groupResourceProfile = this.localGroupResourceProfile.clone();
         groupResourceProfile.mergeComputeResourcePreference(this.data, this.localComputeResourcePolicy,
this.localBatchQueueResourcePolicies);
diff --git a/django_airavata/apps/api/serializers.py b/django_airavata/apps/api/serializers.py
index 3ccd9d8..a174565 100644
--- a/django_airavata/apps/api/serializers.py
+++ b/django_airavata/apps/api/serializers.py
@@ -15,13 +15,14 @@ from airavata.model.appcatalog.appdeployment.ttypes import (ApplicationDeploymen
                                                             SetEnvPaths)
 from airavata.model.appcatalog.appinterface.ttypes import \
     ApplicationInterfaceDescription
-from airavata.model.appcatalog.computeresource.ttypes \
-    import (BatchQueue,
-            ComputeResourceDescription)
+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,
                                                   OutputDataObjectType)
+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,
@@ -32,8 +33,7 @@ from airavata.model.status.ttypes import ExperimentStatus
 from airavata.model.user.ttypes import UserProfile
 from airavata.model.workspace.ttypes import Project
 
-from . import datastore
-from . import thrift_utils
+from . import datastore, thrift_utils
 
 log = logging.getLogger(__name__)
 
@@ -615,3 +615,9 @@ class SharedEntitySerializer(serializers.Serializer):
     def get_isOwner(self, shared_entity):
         request = self.context['request']
         return shared_entity['owner'].userId == request.user.username
+
+
+class CredentialSummarySerializer(
+        thrift_utils.create_serializer_class(CredentialSummary)):
+    type = thrift_utils.ThriftEnumField(SummaryType)
+    persistedTime = UTCPosixTimestampDateTimeField()
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/index.js b/django_airavata/apps/api/static/django_airavata_api/js/index.js
index 49f08c1..0e3502b 100644
--- a/django_airavata/apps/api/static/django_airavata_api/js/index.js
+++ b/django_airavata/apps/api/static/django_airavata_api/js/index.js
@@ -17,6 +17,7 @@ import OutputDataObjectType from './models/OutputDataObjectType'
 import Project from './models/Project'
 import ResourcePermissionType from './models/ResourcePermissionType'
 import SharedEntity from './models/SharedEntity'
+import SummaryType from './models/SummaryType'
 import UserPermission from './models/UserPermission'
 
 import ApplicationDeploymentService from './services/ApplicationDeploymentService'
@@ -60,6 +61,7 @@ exports.models = {
     Project,
     ResourcePermissionType,
     SharedEntity,
+    SummaryType,
     UserPermission,
 }
 
@@ -67,6 +69,7 @@ exports.services = {
     ApplicationDeploymentService,
     ApplicationInterfaceService,
     ApplicationModuleService,
+    CredentialSummaryService: ServiceFactory.service("CredentialSummaries"),
     ExperimentService,
     ExperimentSearchService,
     FullExperimentService,
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/models/BaseEnum.js b/django_airavata/apps/api/static/django_airavata_api/js/models/BaseEnum.js
index bb11276..d6d9f07 100644
--- a/django_airavata/apps/api/static/django_airavata_api/js/models/BaseEnum.js
+++ b/django_airavata/apps/api/static/django_airavata_api/js/models/BaseEnum.js
@@ -1,13 +1,15 @@
 
 export default class BaseEnum {
-    constructor(name, value) {
+    // TODO: add parameter
+    constructor(name, value, writeName=false) {
         this.name = name;
         this.value = value;
+        this.writeName = writeName;
         // immutable
         Object.freeze(this);
     }
     toJSON() {
-        return this.value;
+        return this.writeName ? this.name : this.value;
     }
     static byName(name) {
         return this.values.find(x => x.name === name);
@@ -16,8 +18,8 @@ export default class BaseEnum {
         return this.values.find(x => x.value === value);
     }
     // This must be called to initialize static methods on the Enum subclass
-    static init(names) {
-        const enums = names.map((name, index) => new this(name, index));
+    static init(names, writeName=false) {
+        const enums = names.map((name, index) => new this(name, index, writeName));
         Object.freeze(enums);
         Object.defineProperty(this, 'values', {get: function() { return enums;}});
         this.values.forEach(v => this[v.name] = v);
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/models/BaseModel.js b/django_airavata/apps/api/static/django_airavata_api/js/models/BaseModel.js
index 56cf29f..43cc5a6 100644
--- a/django_airavata/apps/api/static/django_airavata_api/js/models/BaseModel.js
+++ b/django_airavata/apps/api/static/django_airavata_api/js/models/BaseModel.js
@@ -60,8 +60,13 @@ export default class BaseModel {
                 if (fieldValue instanceof BaseEnum){
                     return fieldValue;
                 }
-                // Otherwise it is an integer that we need to convert to enum
-                return modelClass.byValue(fieldValue);
+                if (typeof fieldValue === 'string') {
+                    // convert by name if type is string
+                    return modelClass.byName(fieldValue);
+                } else {
+                    // Otherwise it is an integer that we need to convert to enum
+                    return modelClass.byValue(fieldValue);
+                }
             } else {
                 return new modelClass(fieldValue);
             }
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/models/CredentialSummary.js
b/django_airavata/apps/api/static/django_airavata_api/js/models/CredentialSummary.js
new file mode 100644
index 0000000..a85d89e
--- /dev/null
+++ b/django_airavata/apps/api/static/django_airavata_api/js/models/CredentialSummary.js
@@ -0,0 +1,25 @@
+import BaseModel from './BaseModel'
+import SummaryType from './SummaryType'
+
+const FIELDS = [
+    {
+        name: 'type',
+        type: SummaryType,
+    },
+    'gatewayId',
+    'username',
+    'publicKey',
+    {
+        name: 'persistedTime',
+        type: Date,
+    },
+    'token',
+    'description',
+];
+
+export default class CredentialSummary extends BaseModel {
+
+    constructor(data = {}) {
+        super(FIELDS, data);
+    }
+}
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/models/SummaryType.js
b/django_airavata/apps/api/static/django_airavata_api/js/models/SummaryType.js
new file mode 100644
index 0000000..7e01256
--- /dev/null
+++ b/django_airavata/apps/api/static/django_airavata_api/js/models/SummaryType.js
@@ -0,0 +1,9 @@
+import BaseEnum from './BaseEnum'
+
+export default class SummaryType extends BaseEnum {
+}
+SummaryType.init([
+    'SSH',
+    'PASSWD',
+    'CERT',
+], true);
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/service_config.js b/django_airavata/apps/api/static/django_airavata_api/js/service_config.js
index 86074ed..6b62431 100644
--- a/django_airavata/apps/api/static/django_airavata_api/js/service_config.js
+++ b/django_airavata/apps/api/static/django_airavata_api/js/service_config.js
@@ -1,4 +1,5 @@
 import ApplicationDeploymentDescription from './models/ApplicationDeploymentDescription'
+import CredentialSummary from './models/CredentialSummary'
 import Group from './models/Group'
 import GroupResourceProfile from './models/GroupResourceProfile'
 import SharedEntity from './models/SharedEntity'
@@ -58,6 +59,13 @@ export default {
         }],
         modelClass: ApplicationDeploymentDescription,
     },
+    "CredentialSummaries": {
+        url: "/api/credential-summaries/",
+        viewSet: [{
+            name: "list"
+        }],
+        modelClass: CredentialSummary,
+    },
     "GroupResourceProfiles": {
         url: "/api/group-resource-profiles/",
         viewSet: true,
@@ -82,4 +90,4 @@ export default {
         }],
         modelClass: UserProfile,
     },
-}
\ No newline at end of file
+}
diff --git a/django_airavata/apps/api/thrift_utils.py b/django_airavata/apps/api/thrift_utils.py
index dd4442a..bbff521 100644
--- a/django_airavata/apps/api/thrift_utils.py
+++ b/django_airavata/apps/api/thrift_utils.py
@@ -5,8 +5,10 @@ import copy
 import datetime
 
 from django.utils import six
-from rest_framework.serializers import CharField, BooleanField, DecimalField, IntegerField,
Serializer, \
-    DictField, SerializerMetaclass, ListField, DateTimeField
+from rest_framework.serializers import (BooleanField, CharField, DateTimeField,
+                                        DecimalField, DictField, Field,
+                                        IntegerField, ListField, Serializer,
+                                        SerializerMetaclass, ValidationError)
 from thrift.Thrift import TType
 
 # used to map apache thrift data types to django serializer fields
@@ -46,6 +48,27 @@ class UTCPosixTimestampDateTimeField(DateTimeField):
         return int(datetime.datetime.utcnow().timestamp() * 1000)
 
 
+class ThriftEnumField(Field):
+
+    def __init__(self, enumClass, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.enumClass = enumClass
+
+    def to_representation(self, obj):
+        if obj is None:
+            return None
+        return self.enumClass._VALUES_TO_NAMES[obj]
+
+    def to_internal_value(self, data):
+        if self.allow_null and data is None:
+            return None
+        if data not in self.enumClass._NAMES_TO_VALUES:
+            raise ValidationError(
+                "Not an allowed name of enum {}".format(
+                    self.enumClass.__name__))
+        return self.enumClass._NAMES_TO_VALUES.get(data, None)
+
+
 def create_serializer(thrift_data_type, enable_date_time_conversion=False, **kwargs):
     """
     Create django rest framework serializer based on the thrift data type
diff --git a/django_airavata/apps/api/urls.py b/django_airavata/apps/api/urls.py
index c6e9c56..5e4f0bf 100644
--- a/django_airavata/apps/api/urls.py
+++ b/django_airavata/apps/api/urls.py
@@ -26,6 +26,8 @@ router.register(r'shared-entities', views.SharedEntityViewSet,
                 base_name='shared-entity')
 router.register(r'compute-resources', views.ComputeResourceViewSet,
                 base_name='compute-resource')
+router.register(r'credential-summaries', views.CredentialSummaryViewSet,
+                base_name='credential-summary')
 
 app_name = 'django_airavata_api'
 urlpatterns = [
diff --git a/django_airavata/apps/api/views.py b/django_airavata/apps/api/views.py
index a2f473c..f3b4359 100644
--- a/django_airavata/apps/api/views.py
+++ b/django_airavata/apps/api/views.py
@@ -869,3 +869,13 @@ class SharedEntityViewSet(mixins.RetrieveModelMixin,
         self.request.airavata_client.revokeSharingOfResourceFromGroups(
             self.authz_token, entity_id,
             {group_id: permission_type for group_id in group_ids})
+
+
+class CredentialSummaryViewSet(mixins.ListModelMixin,
+                               GenericAPIBackedViewSet):
+    serializer_class = serializers.CredentialSummarySerializer
+
+    def get_list(self):
+        # NOTE: only supported SummaryType is SSH for now
+        return self.request.airavata_client.getAllCredentialSummaryForGateway(
+            self.authz_token, SummaryType.SSH, settings.GATEWAY_ID)


Mime
View raw message