class OAuthApp(Principal): """ An intermediate abstract class for all OAuth applications """ Meta = BaseMeta(True) consumer_key = models.CharField(max_length=200) secret = models.CharField(max_length=60) name = models.CharField(max_length = 200) # short description of the app description = models.CharField(max_length=2000, null=True) # author of the app author = models.CharField(max_length=200, null=True) # version of the app version = models.CharField(max_length=40, null=True) # required Indivo version indivo_version = models.CharField(max_length=40, null=True) @classmethod def queryset_as_manifests(cls, queryset, as_string=False, **manifest_args): """ Return manifests for each app in the queryset, as a list or as a JSON string (if *as_string* is ``True``). """ manifest_args.update(as_string=False) manifests = [obj.to_manifest(**manifest_args) for obj in queryset.iterator()] if as_string: return simplejson.dumps(manifests) return manifests
class OAuthApp(Principal): """ An intermediate abstract class for all OAuth applications """ Meta = BaseMeta(True) consumer_key = models.CharField(max_length=200) secret = models.CharField(max_length=60) name = models.CharField(max_length=200)
class HelperApp(OAuthApp): """ Helper Applications provide a service to other apps. For now, they don't get access to patient record data... """ Meta = BaseMeta() # short description of the app description = models.CharField(max_length=2000, null=True) admin_p = models.BooleanField(default=False)
class Nonce(BaseModel): """ Nonces for oauth FIXME: clear out the old nonces regularly """ nonce = models.CharField(max_length=100, null=False) oauth_type = models.CharField(max_length=50, null=True) created_at = models.DateTimeField(auto_now_add=True) Meta = BaseMeta() Meta.unique_together = ("nonce", "oauth_type")
class PHA(OAuthApp): """ User applications """ Meta = BaseMeta() # URL templates look like http://host/url/{param1}?foo={param2} # start_url_template should contain a {record_id} parameter # start_url_template may contain a {document_id} parameter # start_url_template may contain a {next_url} parameter start_url_template = models.CharField(max_length=500) # callback_url callback_url = models.CharField(max_length=500) # does this app request a long-lived token? is_autonomous = models.BooleanField(default=False) autonomous_reason = models.TextField(null=True) # does the application have a user interface at all? (some are just background) # this really should only be falsifiable for autonomous apps. # non-autonomous apps must have a UI has_ui = models.BooleanField(default=True) # does the application fit in an iframe? # this should be true for now. Eventually # we'll have some apps that are not frameable frameable = models.BooleanField(default=True) # does the application have a document schema that it knows how to display well? schema = models.ForeignKey('DocumentSchema', null=True) # short description of the app description = models.CharField(max_length=2000, null=True) # privacy terms of use (XML) # FIXME: probably change this field type to XMLField() privacy_tou = models.TextField(null=True) # Accesscontrol: # roles that PHAs could implement. def isInCarenet(self, carenet): """ True if the PHA is in the specified carenet """ try: return indivo.models.CarenetPHA.objects.filter(carenet=carenet, pha=self) except: return False
class PHA(OAuthApp): """ User applications """ Meta = BaseMeta() # URL templates look like http://host/url/{param1}?foo={param2} icon_url = models.CharField(max_length=500) # does the application fit in an iframe? frameable = models.BooleanField(default=True) # short description of the app description = models.CharField(max_length=2000, null=True) # is app enabled by default on new accounts? enabled_by_default = models.BooleanField(default=False) supported_environments = models.CharField(max_length=30, default="desktop,mobile,tablet") optimal_environments = models.CharField(max_length=30, default="desktop") mode = models.CharField(max_length=10)
class PHA(OAuthApp): """ User applications """ Meta = BaseMeta() # URL templates look like http://host/url/{param1}?foo={param2} # start_url_template should contain a {record_id} parameter # start_url_template may contain a {document_id} parameter # start_url_template may contain a {next_url} parameter start_url_template = models.CharField(max_length=500) # callback_url callback_url = models.CharField(max_length=500) # does this app request a long-lived token? is_autonomous = models.BooleanField(default=False) autonomous_reason = models.TextField(null=True) # does the application have a user interface at all? (some are just background) # this really should only be falsifiable for autonomous apps. # non-autonomous apps must have a UI has_ui = models.BooleanField(default=True) # does the application fit in an iframe? # this should be true for now. Eventually # we'll have some apps that are not frameable frameable = models.BooleanField(default=True) # location of the app's icon icon_url = models.CharField(max_length=500, null=True) # other requirements: datatypes, REST methods, codes, etc. # represented as a JSON string suitable for dropping into # a SMART manifest requirements = models.TextField(null=True) @classmethod def from_manifest(cls, manifest, credentials, save=True): """ Produce a PHA object from an app manifest. Manifests should correspond to SMART manifest format (http://wiki.chip.org/smart-project/index.php/Developers_Documentation:_Packaging_Applications_via_SMART_Manifest), with some optional Indivo specific extensions, namely: * *oauth_callback_url*: A callback URL for Indivo-style oAuth access * *autonomous_reason*: An explanation for why the app requires offline access to patient records * *has_ui*: ``true`` or ``false``, whether the app can be displayed in a browser. * *frameable*: ``true`` or ``false``, whether the app should be loaded in an iframe in the Indivo UI. * *indivo_version*: Required version of Indivo for compatibility Credentials should be JSON objects with two keys: * *consumer_key*: The oAuth consumer key to use for the app * *consumer_secret*: The oAuth consumer secret to use for the app See :doc:`app-registration` for more details. """ from indivo.views import _get_indivo_version parsed_manifest = simplejson.loads(manifest) parsed_credentials = simplejson.loads(credentials) # expand relative urls to be relative to the UI start_url = parsed_manifest.get('index', '') if start_url and start_url.find('://') < 0: start_url = "%s%s"%(settings.UI_SERVER_URL, start_url) callback_url = parsed_manifest.get('oauth_callback_url', '') if callback_url and callback_url.find('://') < 0: callback_url = "%s%s"%(settings.UI_SERVER_URL, callback_url) icon_url = parsed_manifest.get('icon', '') if icon_url and icon_url.find('://') < 0: icon_url = "%s%s"%(settings.UI_SERVER_URL, icon_url) kwargs = { 'consumer_key': parsed_credentials['consumer_key'], 'secret': parsed_credentials['consumer_secret'], 'name': parsed_manifest['name'], 'email': parsed_manifest['id'], 'start_url_template': start_url, 'callback_url': callback_url, 'is_autonomous': parsed_manifest.get('mode', '') == 'background', 'autonomous_reason': parsed_manifest.get('autonomous_reason', ''), 'has_ui': parsed_manifest['has_ui'] if parsed_manifest.has_key('has_ui') \ else parsed_manifest.has_key('index'), # This may not be perfect 'frameable': parsed_manifest['frameable'] if parsed_manifest.has_key('frameable') \ else parsed_manifest.has_key('index'), 'description': parsed_manifest.get('description', ''), 'author': parsed_manifest.get('author', ''), 'version': parsed_manifest.get('version', ''), 'icon_url': icon_url, 'indivo_version': parsed_manifest['indivo_version'] if parsed_manifest.has_key('indivo_version') \ else _get_indivo_version(parsed_manifest.get('smart_version', '')), 'requirements': simplejson.dumps(parsed_manifest.get('requires', {})), } app = cls(**kwargs) if save: app.save() return app def to_manifest(self, smart_only=False, as_string=True): """ Produce a SMART-style manifest for the app. see :doc:`app-registration` for details on the manifest format. If *smart_only* is True, only SMART-manifest compatible fields will be included in the output. """ from indivo.views import _get_smart_version smart_version = _get_smart_version(self.indivo_version) output = { "name": self.name, "description": self.description, "author": self.author, "id": self.email, "version": self.version, "mode": "background" if self.is_autonomous else "ui", "scope": "record", "icon": self.icon_url, "index": self.start_url_template, "requires": simplejson.loads(self.requirements), } if smart_version: output["smart_version"] = smart_version if not smart_only: output.update({ "has_ui": self.has_ui, "frameable": self.frameable, "oauth_callback_url": self.callback_url, "indivo_version": self.indivo_version, }) if self.is_autonomous: output['autonomous_reason'] = self.autonomous_reason if as_string: return simplejson.dumps(output) else: return output # Accesscontrol: # roles that PHAs could implement. def isInCarenet(self, carenet): """ True if the PHA is in the specified carenet """ try: return indivo.models.CarenetPHA.objects.filter(carenet=carenet, pha=self) except: return False def scopedToRecord(self, record): """ True if the PHA is enabled on the record """ return self.pha_shares_to.filter(record=record).exists()