diff --git a/ipaserver/plugins/schema.py b/ipaserver/plugins/schema.py index 98b47cbbb..847d8c442 100644 --- a/ipaserver/plugins/schema.py +++ b/ipaserver/plugins/schema.py @@ -7,6 +7,7 @@ import itertools import sys import six +import hashlib from .baseldap import LDAPObject from ipalib import errors @@ -18,6 +19,10 @@ from ipalib.plugable import Registry from ipalib.text import _ from ipapython.version import API_VERSION +# Schema TTL sent to clients in response to schema call. +# Number of seconds before client should check for schema update. +SCHEMA_TTL = 7*24*3600 # default: 7 days + __doc__ = _(""" API Schema """) + _(""" @@ -690,6 +695,33 @@ class output_find(BaseParamSearch): class schema(Command): NO_CLI = True + @staticmethod + def _calculate_fingerprint(data): + """ + Returns fingerprint for schema + + Behavior of this function can be changed at any time + given that it always generates identical fingerprint for + identical data (change in order of items in dict is + irelevant) and the risk of generating identical fingerprint + for different inputs is low. + """ + to_process = [data] + fingerprint = hashlib.sha1() + + for entry in to_process: + if isinstance(entry, (list, tuple)): + for item in entry: + to_process.append(item) + elif isinstance(entry, dict): + for key in sorted(entry.keys()): + to_process.append(key) + to_process.append(entry[key]) + else: + fingerprint.update(unicode(entry).encode('utf-8')) + + return fingerprint.hexdigest()[:8] + def execute(self, *args, **kwargs): commands = list(self.api.Object.command.search(**kwargs)) for command in commands: @@ -712,4 +744,8 @@ class schema(Command): schema['classes'] = classes schema['topics'] = topics + schema_fp = self._calculate_fingerprint(schema) + schema['fingerprint'] = schema_fp + schema['ttl'] = SCHEMA_TTL + return dict(result=schema)