OwlCyberSecurity - MANAGER
Edit File: principals.py
# -*- test-case-name: calendarserver.webadmin.test.test_principals -*- ## # Copyright (c) 2014-2017 Apple Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ## from __future__ import print_function """ Calendar Server principal management web UI. """ __all__ = [ "PrincipalsResource", ] from cStringIO import StringIO from zipfile import ZipFile from twistedcaldav.simpleresource import SimpleResource from twisted.internet.defer import inlineCallbacks, returnValue from twisted.web.template import tags as html, renderer from txweb2.stream import MemoryStream from txweb2.http import Response from txweb2.http_headers import MimeType from .resource import PageElement, TemplateResource class PrincipalsPageElement(PageElement): """ Principal management page element. """ def __init__(self, directory): super(PrincipalsPageElement, self).__init__(u"principals") self._directory = directory def pageSlots(self): return { u"title": u"Principal Management", } @renderer def search_terms(self, request, tag): """ Inserts search terms as a text child of C{tag}. """ terms = searchTerms(request) if terms: return tag(value=u" ".join(terms)) else: return tag @renderer def if_search_results(self, request, tag): """ Renders C{tag} if there are search results, otherwise removes it. """ if searchTerms(request): return tag else: return u"" @renderer def search_results_row(self, request, tag): def rowsForRecords(records): for record in records: yield tag.clone().fillSlots( **slotsForRecord(record) ) d = self.recordsForSearchTerms(request) d.addCallback(rowsForRecords) return d @inlineCallbacks def recordsForSearchTerms(self, request): if not hasattr(request, "_search_result_records"): terms = searchTerms(request) records = yield self._directory.recordsMatchingTokens(terms) request._search_result_records = tuple(records) returnValue(request._search_result_records) class PrincipalsResource(TemplateResource): """ Principal management page resource. """ addSlash = True def __init__(self, directory, store, principalCollections): super(PrincipalsResource, self).__init__( lambda: PrincipalsPageElement(directory), principalCollections, isdir=False ) self._directory = directory self._store = store @inlineCallbacks def getChild(self, name): if name == "": returnValue(self) record = yield self._directory.recordWithUID(name) if record: returnValue(PrincipalResource(record, self._store, self._principalCollections)) else: returnValue(None) class PrincipalPageElement(PageElement): """ Principal editing page element. """ def __init__(self, record): super(PrincipalPageElement, self).__init__(u"principals_edit") self._record = record def pageSlots(self): slots = slotsForRecord(self._record) slots[u"title"] = u"Calendar & Contacts Server Principal Information" slots[u"service"] = ( u"{self._record.service.__class__.__name__}: " "{self._record.service.realmName}" .format(self=self) ) return slots class PrincipalResource(TemplateResource): """ Principal editing resource. """ addSlash = True def __init__(self, record, store, principalCollections): super(PrincipalResource, self).__init__( lambda: PrincipalPageElement(record), principalCollections, isdir=False ) self._record = record self._store = store def getChild(self, name): if name == "": return self if name == "calendars_combined": return PrincipalCalendarsExportResource(self._record, self._store, self._principalCollections) class PrincipalCalendarsExportResource(SimpleResource): """ Resource that vends a principal's calendars as iCalendar text. """ addSlash = False def __init__(self, record, store, principalCollections): super(PrincipalCalendarsExportResource, self).__init__(principalCollections, isdir=False) self._record = record self._store = store @inlineCallbacks def calendarComponents(self): uid = self._record.uid calendarComponents = [] txn = self._store.newTransaction(label="PrincipalCalendarsExportResource") try: calendarHome = yield txn.calendarHomeWithUID(uid) if calendarHome is None: raise RuntimeError("No calendar home for UID: {}".format(uid)) for calendar in (yield calendarHome.calendars()): name = calendar.displayName() for calendarObject in (yield calendar.calendarObjects()): perUser = yield calendarObject.filteredComponent(uid, True) calendarComponents.add((name, perUser)) finally: txn.abort() returnValue(calendarComponents) @inlineCallbacks def iCalendarZipArchiveData(self): calendarComponents = yield self.calendarComponents() fileHandle = StringIO() try: zipFile = ZipFile(fileHandle, "w", allowZip64=True) try: zipFile.comment = ( "Calendars for UID: {}".format(self._record.uid) ) names = set() for name, component in calendarComponents: if name in names: i = 0 while True: i += 1 nextName = "{} {:d}".format(name, i) if nextName not in names: name = nextName break assert i < len(calendarComponents) text = component.getText() zipFile.writestr(name, text) finally: zipFile.close() data = fileHandle.getvalue() finally: fileHandle.close() returnValue(data) @inlineCallbacks def render(self, request): response = Response() response.stream = MemoryStream((yield self.iCalendarZipArchiveData())) # FIXME: Use content-encoding instead? response.headers.setHeader( b"content-type", MimeType.fromString(b"application/zip") ) returnValue(response) def searchTerms(request): if not hasattr(request, "_search_terms"): terms = set() if request.args: for query in request.args.get(u"search", []): for term in query.split(u" "): if term: terms.add(term) for term in request.args.get(u"term", []): if term: terms.add(term) request._search_terms = terms return request._search_terms # # This should work when we switch to twext.who # def slotsForRecord(record): def asText(obj): if obj is None: return u"(no value)" else: try: return unicode(obj) except UnicodeDecodeError: try: return unicode(repr(obj)) except UnicodeDecodeError: return u"(error rendering value)" def joinWithBR(elements): noValues = True for element in elements: if not noValues: yield html.br() yield asText(element) noValues = False if noValues: yield u"(no values)" # slots = {} # for field, values in record.fields.iteritems(): # if not record.service.fieldName.isMultiValue(field): # values = (values,) # slots[field.name] = joinWithBR(asText(value) for value in values) # return slots return { u"service": ( u"{record.service.__class__.__name__}: {record.service.realmName}" .format(record=record) ), u"uid": joinWithBR((record.uid,)), u"guid": joinWithBR((record.guid,)), u"recordType": joinWithBR((record.recordType,)), u"shortNames": joinWithBR(record.shortNames) if hasattr(record, "shortNames") else "", u"fullNames": joinWithBR(record.fullNames), u"emailAddresses": joinWithBR(record.emailAddresses) if hasattr(record, "emailAddresses") else "", u"calendarUserAddresses": joinWithBR(record.calendarUserAddresses), u"serverURI": joinWithBR((record.serverURI(),)), }