def testIsAllowedToViewPacakgesProposalsOff(self): """Test PermissionResolver._IsAllowedToViewPackages() without proposals.""" test_resolver = auth.PermissionResolver('task') email_one = '*****@*****.**' email_two = '*****@*****.**' email_three = '*****@*****.**' setattr(auth.settings, 'ENABLE_PROPOSALS_GROUP', False) setattr(auth.settings, 'PROPOSALS_GROUP', '') self.mox.StubOutWithMock(auth, 'DoUserAuth') self.mox.StubOutWithMock(auth, 'IsAdminUser') self.mox.StubOutWithMock(auth, 'IsSupportUser') auth.IsAdminUser(email_one).AndReturn(True) auth.IsAdminUser(email_two).AndReturn(False) auth.IsSupportUser(email_two).AndReturn(True) auth.IsAdminUser(email_three).AndReturn(False) auth.IsSupportUser(email_three).AndReturn(False) self.mox.ReplayAll() test_resolver.email = email_one self.assertTrue(test_resolver._IsAllowedToViewPackages()) test_resolver.email = email_two self.assertTrue(test_resolver._IsAllowedToViewPackages()) test_resolver.email = email_three self.assertFalse(test_resolver._IsAllowedToViewPackages()) self.mox.VerifyAll()
def get(self): """Handle GET.""" try: # already munki authenticated? return, nothing to do. gaeserver.DoMunkiAuth() #logging.info('Uauth: session is already authenticated') return except gaeserver.NotAuthenticated: pass user = users.get_current_user() if not user: #logging.error('Uauth: user is not logged in') raise NotAuthenticated email = user.email() if auth.IsAdminUser(email): a = gaeserver.AuthSimianServer() output = a.SessionCreateUserAuthToken(email, level=gaeserver.LEVEL_ADMIN) elif auth.IsSupportUser(email): a = gaeserver.AuthSimianServer() output = a.SessionCreateUserAuthToken(email, level=gaeserver.LEVEL_BASE) else: logging.error('Uauth: user %s is not an admin', email) raise NotAuthenticated if output: #logging.info('Uauth: success, token = %s', output) self.response.headers['Set-Cookie'] = '%s=%s; secure; httponly;' % ( auth_init.AUTH_TOKEN_COOKIE, output) self.response.out.write(auth_init.AUTH_TOKEN_COOKIE) else: #logging.info('Uauth: unknown token') raise NotAuthenticated
def post(self, uuid=None): """POST handler.""" if not self.IsAdminUser() and not auth.IsSupportUser(): self.response.set_status(403) return action = self.request.get('action') if action == 'set_inactive': c = models.Computer.get_by_key_name(uuid) if not c: self.response.out.write('UUID not found') return c.active = False c.put(update_active=False) msg = 'Host set as inactive.' elif action == 'upload_logs': c = models.Computer.get_by_key_name(uuid) if not c: self.response.set_status(404) return c.upload_logs_and_notify = users.get_current_user().email() c.put() self.response.set_status(200) self.response.headers['Content-Type'] = 'application/json' self.response.out.write( json.dumps({'email': c.upload_logs_and_notify})) return else: self.response.set_status(400) return self.redirect('/admin/host/%s?msg=%s' % (uuid, msg))
def _DisplayHost(self, uuid=None, computer=None): """Displays the report for a single host. Args: uuid: str uuid for host to display. computer: models.Computer object to display. """ if not computer: computer = models.Computer.get_by_key_name(uuid) else: uuid = computer.uuid client_log_files = models.ClientLogFile.all().filter( 'uuid =', uuid).order('-mtime').fetch(100) msu_log = models.ComputerMSULog.all().filter( 'uuid =', uuid).order('-mtime').fetch(100) applesus_installs = models.InstallLog.all().filter( 'uuid =', uuid).filter( 'applesus =', True).order('-mtime').fetch(SINGLE_HOST_DATA_FETCH_LIMIT) installs = models.InstallLog.all().filter('uuid =', uuid).filter( 'applesus =', False).order('-mtime').fetch(SINGLE_HOST_DATA_FETCH_LIMIT) exits = models.PreflightExitLog.all().filter( 'uuid =', uuid).order('-mtime').fetch(SINGLE_HOST_DATA_FETCH_LIMIT) install_problems = models.ClientLog.all().filter( 'action =', 'install_problem').filter( 'uuid =', uuid).order('-mtime').fetch(SINGLE_HOST_DATA_FETCH_LIMIT) uptime = None if computer: AddTimezoneToComputerDatetimes(computer) computer.connection_dates.reverse() computer.connection_datetimes.reverse() if computer.uptime: uptime_days = datetime.timedelta(seconds=computer.uptime).days uptime_hms = time.strftime('%H:%M:%S', time.gmtime(computer.uptime)) uptime = '%d days, %s' % (uptime_days, uptime_hms) else: uptime = 'unknown' values = { 'uuid_lookup_url': settings.UUID_LOOKUP_URL, 'owner_lookup_url': settings.OWNER_LOOKUP_URL, 'computer': computer, 'applesus_installs': applesus_installs, 'installs': installs, 'client_log_files': client_log_files, 'msu_log': msu_log, 'install_problems': install_problems, 'preflight_exits': exits, 'uptime': uptime, 'host_report': True, 'limit': SINGLE_HOST_DATA_FETCH_LIMIT, 'is_admin': auth.IsAdminUser(), 'is_support_user': auth.IsSupportUser(), 'is_security_user': auth.IsSecurityUser(), } self.response.out.write( RenderTemplate('templates/stats_host.html', values))
def _DisplayPackagesList(self): """Displays list of all installs/removals/etc.""" installs, counts_mtime = models.ReportsCache.GetInstallCounts() pending, pending_mtime = models.ReportsCache.GetPendingCounts() packages = [] all_packages = self.request.get('all_packages') == '1' query = self._GetPackageQuery() for p in query: if not p.plist: self.error(httplib.FORBIDDEN) self.response.out.write('Package %s has a broken plist!' % p.filename) return pkg = {} pkg['count'] = installs.get(p.munki_name, {}).get('install_count', 'N/A') pkg['fail_count'] = installs.get(p.munki_name, {}).get('install_fail_count', 'N/A') pkg['pending_count'] = pending.get(p.munki_name, 'N/A') pkg['duration_seconds_avg'] = installs.get(p.munki_name, {}).get( 'duration_seconds_avg', None) or 'N/A' pkg['unattended'] = p.plist.get('unattended_install', False) pkg['unattended_uninstall'] = p.plist.get('unattended_uninstall', False) force_install_after_date = p.plist.get('force_install_after_date', None) if force_install_after_date: pkg['force_install_after_date'] = force_install_after_date pkg['catalogs'] = p.catalog_matrix pkg['manifests'] = p.manifest_matrix pkg['munki_name'] = p.munki_name or p.plist.GetMunkiName() pkg['filename'] = p.filename pkg['file_size'] = p.plist.get('installer_item_size', 0) * 1024 pkg['install_types'] = p.install_types pkg['manifest_mod_access'] = p.manifest_mod_access pkg['description'] = p.description pkg['plist_is_signed'] = p.plist_is_signed() packages.append(pkg) packages.sort(key=lambda pkg: pkg['munki_name'].lower()) self.Render( self.TEMPLATE, { 'packages': packages, 'counts_mtime': counts_mtime, 'pending_mtime': pending_mtime, 'report_type': self.REPORT_TYPE, 'active_pkg': self.request.GET.get('activepkg'), 'is_support_user': auth.IsSupportUser(), 'can_upload': auth.HasPermission(auth.UPLOAD), 'is_admin': auth.IsAdminUser(), 'all_packages': all_packages, })
def _DisplayMain(self): """Displays the main Manifest Modification report.""" error = self.request.get('error') mod_type = self.request.get('mod_type') or 'owner' model = models.MANIFEST_MOD_MODELS.get(mod_type) if mod_type and not model: raise ValueError('Invalid mod_type: %s' % mod_type) elif mod_type: mods_query = model.all().order('-mtime') mods = self.Paginate(mods_query, DEFAULT_MANIFEST_MOD_FETCH_LIMIT) is_admin = self.IsAdminUser() is_support = False is_security = False if not is_admin: is_support = auth.IsSupportUser() if not is_support: is_security = auth.IsSecurityUser() if is_admin: munki_pkg_names = models.PackageInfo.GetManifestModPkgNames( common.MANIFEST_MOD_ADMIN_GROUP) mod_types = MOD_TYPES elif is_support: munki_pkg_names = models.PackageInfo.GetManifestModPkgNames( common.MANIFEST_MOD_SUPPORT_GROUP) mod_types = MOD_GROUP_TYPES[common.MANIFEST_MOD_SUPPORT_GROUP] elif is_security: munki_pkg_names = models.PackageInfo.GetManifestModPkgNames( common.MANIFEST_MOD_SECURITY_GROUP) mod_types = MOD_GROUP_TYPES[common.MANIFEST_MOD_SECURITY_GROUP] else: munki_pkg_names = None mod_types = [] data = { 'mod_types': mod_types, 'mod_type': mod_type, 'mods': mods, 'error': error, 'can_add_manifest_mods': is_admin or is_support or is_security, 'munki_pkg_names': munki_pkg_names, 'install_types': common.INSTALL_TYPES, 'manifests': common.TRACKS, 'report_type': 'manifests_admin', } self.Render('manifest_modifications.html', data)
def post(self): """POST handler.""" if self.request.get('add_manifest_mod'): if (not self.IsAdminUser() and not auth.IsSupportUser() and not auth.IsSecurityUser()): self.response.set_status(httplib.FORBIDDEN) return self._AddManifestModification() elif self.IsAdminUser() and self.request.get('delete'): self._DeleteManifestModification() elif self.IsAdminUser() and self.request.get('enabled'): self._ToggleManifestModification() else: if not self.IsAdminUser(): self.response.set_status(httplib.FORBIDDEN) return self.response.set_status(httplib.NOT_FOUND)
def post(self, uuid=None): """POST handler.""" if not self.IsAdminUser() and not auth.IsSupportUser(): self.response.set_status(403) return action = self.request.get('action') if action == 'set_fixed': uuid = self.request.get('uuid') c = models.ComputerClientBroken.get_by_key_name(uuid) if not c: self.response.out.write('UUID not found') return c.fixed = True c.put() self.redirect('/admin/brokenclients')
def post(self, report=None, uuid=None): """Stats post handler.""" #logging.debug('POST called: report=%s, uuid=%s', report, uuid) if not auth.IsAdminUser() and not auth.IsSupportUser(): self.response.set_status(403) return if report not in ['host', 'clientlog', 'brokenclients']: self.response.set_status(404) return action = self.request.get('action') if action == 'set_inactive': c = models.Computer.get_by_key_name(uuid) if not c: self.response.out.write('UUID not found') return c.active = False c.put(update_active=False) elif action == 'set_loststolen': models.ComputerLostStolen.SetLostStolen(uuid) elif action == 'upload_logs': c = models.Computer.get_by_key_name(uuid) if not c: self.response.out.write('UUID not found') return c.upload_logs_and_notify = users.get_current_user().email() c.put() elif action == 'delete_client_log': key = uuid # for /admin/clientlog/ it's really the uuid_logname l = models.ClientLogFile.get_by_key_name(key) l.delete() return elif action == 'set_fixed': c = models.ComputerClientBroken.get_by_key_name(uuid) if not c: self.response.out.write('UUID not found') return c.fixed = True c.put() else: self.response.set_status(404) self.redirect('/admin/%s/%s' % (report, uuid))
def post(self, uuid=None): """POST handler.""" if not self.IsAdminUser() and not auth.IsSupportUser(): self.response.set_status(httplib.FORBIDDEN) return action = self.request.get('action') if action == 'set_inactive': c = models.Computer.get_by_key_name(uuid) if not c: self.response.out.write('UUID not found') return c.active = False c.put(update_active=False) msg = 'Host set as inactive.' elif action == 'upload_logs': c = models.Computer.get_by_key_name(uuid) if not c: self.response.set_status(httplib.NOT_FOUND) return c.upload_logs_and_notify = users.get_current_user().email() c.put() self.response.set_status(httplib.OK) self.response.headers['Content-Type'] = 'application/json' self.response.out.write( json.dumps({'email': c.upload_logs_and_notify})) return elif action == 'delete_client_log': key = uuid # for /admin/clientlog/ it's really the uuid_logname l = models.ClientLogFile.get_by_key_name(key) if not l: self.response.set_status(httplib.NOT_FOUND) return l.delete() return else: self.response.set_status(httplib.BAD_REQUEST) return self.redirect('/admin/host/%s?msg=%s' % (uuid, msg))
def testDoUserAuthWithAllDomainUsersOff(self): self.stubs.Set(auth.settings, 'ALLOW_ALL_DOMAIN_USERS_READ_ACCESS', False) self.stubs.Set(auth, 'users', self.mox.CreateMock(auth.users)) self.mox.StubOutWithMock(auth, 'IsAdminUser') self.mox.StubOutWithMock(auth, 'IsSupportUser') self.mox.StubOutWithMock(auth, 'IsSecurityUser') self.mox.StubOutWithMock(auth, 'IsPhysicalSecurityUser') mock_user = self.mox.CreateMockAnything() email = '*****@*****.**' auth.users.get_current_user().AndReturn(mock_user) mock_user.email().AndReturn(email) auth.IsAdminUser(email).AndReturn(False) auth.IsSupportUser(email).AndReturn(False) auth.IsSecurityUser(email).AndReturn(False) auth.IsPhysicalSecurityUser(email).AndReturn(True) self.mox.ReplayAll() self.assertEqual(mock_user, auth.DoUserAuth()) self.mox.VerifyAll()
def post(self, report=None, uuid=None): """Misc post handler.""" if not self.IsAdminUser() and not auth.IsSupportUser(): self.response.set_status(403) return if report not in ['clientlog']: self.response.set_status(404) return action = self.request.get('action') if action == 'delete_client_log': key = uuid # for /admin/clientlog/ it's really the uuid_logname l = models.ClientLogFile.get_by_key_name(key) if not l: self.response.set_status(404) return l.delete() return else: self.response.set_status(404) self.redirect('/admin/%s/%s' % (report, uuid))
def _DisplayPackagesList(self): """Displays list of all installs/removals/etc.""" installs, counts_mtime = models.ReportsCache.GetInstallCounts() pending, pending_mtime = models.ReportsCache.GetPendingCounts() packages = [] for p in models.PackageInfo.all(): if not p.plist: self.error(403) self.response.out.write('Package %s has a broken plist!' % p.filename) return pkg = {} pkg['count'] = installs.get(p.munki_name, {}).get('install_count', 'N/A') pkg['fail_count'] = installs.get(p.munki_name, {}).get( 'install_fail_count', 'N/A') pkg['pending_count'] = pending.get(p.munki_name, 'N/A') pkg['duration_seconds_avg'] = installs.get(p.munki_name, {}).get( 'duration_seconds_avg', None) or 'N/A' pkg['unattended'] = p.plist.get('unattended_install', False) force_install_after_date = p.plist.get('force_install_after_date', None) if force_install_after_date: pkg['force_install_after_date'] = force_install_after_date pkg['catalogs'] = p.catalogs pkg['manifests'] = p.manifests pkg['munki_name'] = p.munki_name or p.plist.GetMunkiName() pkg['filename'] = p.filename pkg['file_size'] = p.plist.get('installer_item_size', 0) * 1024 pkg['install_types'] = p.install_types pkg['manifest_mod_access'] = p.manifest_mod_access pkg['description'] = p.description packages.append(pkg) packages.sort(key=lambda pkg: pkg['munki_name'].lower()) self.Render('packages.html', {'packages': packages, 'counts_mtime': counts_mtime, 'pending_mtime': pending_mtime, 'report_type': 'packages', 'active_pkg': self.request.GET.get('activepkg'), 'is_support_user': auth.IsSupportUser()})
def _DisplayMain(self): """Displays the main Manifest Modification report.""" error_msg = self.request.get('error') mod_type = self.request.get('mod_type') or 'owner' model = models.MANIFEST_MOD_MODELS.get(mod_type) if mod_type and not model: error_msg = 'Unknown mod_type provided; defaulting to owner' mod_type = 'owner' model = models.MANIFEST_MOD_MODELS.get(mod_type) mods_query = model.all().order('-mtime') filter_value = self.request.get('filter_value') filter_field = self.request.get('filter_field') if filter_value: if filter_field == 'target': mods_query.filter(model.TARGET_PROPERTY_NAME, filter_value) elif filter_field == 'package': mods_query.filter('value', filter_value) elif filter_field == 'admin': if '@' not in filter_value: filter_value += '@' + settings.AUTH_DOMAIN mods_query.filter('user', users.User(email=filter_value)) mods = self.Paginate(mods_query, DEFAULT_MANIFEST_MOD_FETCH_LIMIT) is_admin = self.IsAdminUser() is_support = False is_security = False if not is_admin: is_support = auth.IsSupportUser() if not is_support: is_security = auth.IsSecurityUser() if is_admin: munki_pkg_names = models.PackageInfo.GetManifestModPkgNames( common.MANIFEST_MOD_ADMIN_GROUP) mod_types = MOD_TYPES elif is_support: munki_pkg_names = models.PackageInfo.GetManifestModPkgNames( common.MANIFEST_MOD_SUPPORT_GROUP) mod_types = MOD_GROUP_TYPES[common.MANIFEST_MOD_SUPPORT_GROUP] elif is_security: munki_pkg_names = models.PackageInfo.GetManifestModPkgNames( common.MANIFEST_MOD_SECURITY_GROUP) mod_types = MOD_GROUP_TYPES[common.MANIFEST_MOD_SECURITY_GROUP] else: munki_pkg_names = None mod_types = [] data = { 'mod_types': mod_types, 'mod_type': mod_type, 'mods': mods, 'error': error_msg, 'can_add_manifest_mods': is_admin or is_support or is_security, 'munki_pkg_names': munki_pkg_names, 'install_types': common.INSTALL_TYPES, 'manifests': common.TRACKS, 'report_type': 'manifests_admin', 'mods_filter': filter_field, 'mods_filter_value': filter_value, } self.Render('manifest_modifications.html', data)
def get(self, filename): """GET Args: filename: str, package filename like 'foo.dmg' Returns: None if a blob is being returned, or a response object """ auth_return = auth.DoAnyAuth() if hasattr(auth_return, 'email'): email = auth_return.email() if not auth.IsAdminUser(email) and not auth.IsSupportUser(email): raise auth.IsAdminMismatch filename = urllib.unquote(filename) pkg = models.PackageInfo.MemcacheWrappedGet(filename) if pkg is None or not pkg.blobstore_key: self.error(404) return if common.IsPanicModeNoPackages(): self.error(503) return # Get the Blobstore BlobInfo for this package; memcache wrapped. memcache_key = 'blobinfo_%s' % filename blob_info = memcache.get(memcache_key) if not blob_info: blob_info = blobstore.BlobInfo.get(pkg.blobstore_key) if blob_info: memcache.set(memcache_key, blob_info, 300) # cache for 5 minutes. else: logging.critical( 'Failure fetching BlobInfo for %s. Verify the blob exists: %s', pkg.filename, pkg.blobstore_key) self.error(404) return header_date_str = self.request.headers.get('If-Modified-Since', '') etag_nomatch_str = self.request.headers.get('If-None-Match', 0) etag_match_str = self.request.headers.get('If-Match', 0) pkg_date = blob_info.creation pkg_size_bytes = blob_info.size # TODO(user): The below can be simplified once all of our clients # have ETag values set on the filesystem for these files. The # parsing of If-Modified-Since could be removed. Removing it prematurely # will cause a re-download of all packages on all clients for 1 iteration # until they all have ETag values. # Reduce complexity of elif conditional below. # If an If-None-Match: ETag is supplied, don't worry about a # missing file modification date -- the ETag supplies everything needed. if etag_nomatch_str and not header_date_str: resource_expired = False else: resource_expired = handlers.IsClientResourceExpired( pkg_date, header_date_str) # Client supplied If-Match: etag, but that etag does not match current # etag. return 412. if (etag_match_str and pkg.pkgdata_sha256 and etag_match_str != pkg.pkgdata_sha256): self.response.set_status(412) # Client supplied no etag or If-No-Match: etag, and the etag did not # match, or the client's file is older than the mod time of this package. elif ((etag_nomatch_str and pkg.pkgdata_sha256 and etag_nomatch_str != pkg.pkgdata_sha256) or resource_expired): self.response.headers['Content-Disposition'] = str( 'attachment; filename=%s' % filename) # header date empty or package has changed, send blob with last-mod date. if pkg.pkgdata_sha256: self.response.headers['ETag'] = str(pkg.pkgdata_sha256) self.response.headers['Last-Modified'] = pkg_date.strftime( handlers.HEADER_DATE_FORMAT) self.response.headers['X-Download-Size'] = str(pkg_size_bytes) self.send_blob(pkg.blobstore_key) else: # Client doesn't need to do anything, current version is OK based on # ETag and/or last modified date. if pkg.pkgdata_sha256: self.response.headers['ETag'] = str(pkg.pkgdata_sha256) self.response.set_status(304)
def get(self, filename=None): """GET handler.""" if not filename: self.error(httplib.NOT_FOUND) return elif not auth.HasPermission(auth.VIEW_PACKAGES): self.error(httplib.FORBIDDEN) return filename = urllib.unquote(filename) p = models.PackageInfo.get_by_key_name(filename) if not p: self.error(httplib.NOT_FOUND) self.Render('error.html', {'message': 'PackageInfo not found: %s' % filename}) return p.name = p.plist['name'] p.display_name = p.plist.get('display_name', '') p.unattended = p.plist.get('unattended_install') p.unattended_uninstall = p.plist.get('unattended_uninstall') p.version = p.plist['version'] force_install_after_date = p.plist.get('force_install_after_date', None) if force_install_after_date: p.force_install_after_date = datetime.datetime.strftime( force_install_after_date, '%Y-%m-%d') p.force_install_after_date_time = datetime.datetime.strftime( force_install_after_date, '%H:%M') if self.request.referrer and self.request.referrer.endswith( 'proposals'): return_address = '/admin/proposals' return_title = 'proposals' else: return_address = '/admin/packages' return_title = 'package' if self.request.get('plist_xml'): self.Render( 'plist.html', { 'report_type': 'packages', 'plist_type': 'package_plist', 'xml': admin.XmlToHtml(p.plist.GetXml()), 'title': 'Plist for %s' % p.name, 'raw_xml_link': '/pkgsinfo/%s' % filename, }) else: categories = ([ x.strip() for x in settings.LIST_OF_CATEGORIES.split(',') if x ]) manifests_and_catalogs_unlocked = ( p.blob_info or p.plist.get('PackageCompleteURL')) data = { 'pkg': p, 'report_type': 'package', 'tracks': common.TRACKS, 'install_types': common.INSTALL_TYPES, 'manifest_mod_groups': common.MANIFEST_MOD_GROUPS, 'approval_required': settings.APPROVAL_REQUIRED, 'is_admin_user': self.IsAdminUser(), 'is_support_user': auth.IsSupportUser(), 'pkg_safe_to_modify': p.IsSafeToModify(), 'editxml': self.request.get('editxml'), 'manifests_and_catalogs_unlocked': manifests_and_catalogs_unlocked, 'return_address': return_address, 'return_title': return_title, 'categories': categories } self.Render('package.html', data)
def _AddManifestModification(self): """Adds a new manifest modification to Datastore.""" mod_type = self.request.get('mod_type') target = self.request.get('target').strip() munki_pkg_name = self.request.get('munki_pkg_name').strip() manifests = self.request.get_all('manifests') install_types = self.request.get_all('install_types') # Security users are only able to inject specific packages. if not self.IsAdminUser(): grp = None if auth.IsSupportUser(): grp = common.MANIFEST_MOD_SUPPORT_GROUP # Support users can only inject items into optional_installs. install_types = ['optional_installs'] elif auth.IsSecurityUser(): grp = common.MANIFEST_MOD_SECURITY_GROUP # Security users can only inject items into managed_installs. install_types = ['managed_installs'] munki_pkg_names = models.PackageInfo.GetManifestModPkgNames( grp, only_names=True) if munki_pkg_name not in munki_pkg_names: self.response.out.write('You are not allowed to inject: %s' % munki_pkg_name) self.response.set_status(403) return elif mod_type not in [k for k, v in MOD_GROUP_TYPES.get(grp, [])]: self.response.out.write( 'You are not allowed to inject to: %s' % mod_type) self.response.set_status(403) return # Validation. error_msg = None if not target or not munki_pkg_name or not install_types: error_msg = ( 'target, munki_pkg_name, and install_types are all required') if not error_msg: for manifest in manifests: if manifest not in common.TRACKS: error_msg = 'manifest %s is not in %s' % (manifest, common.TRACKS) if not error_msg: for install_type in install_types: if install_type not in common.INSTALL_TYPES: error_msg = 'install_type %s is not in %s' % ( install_type, common.INSTALL_TYPES) if not error_msg: if not models.PackageInfo.all().filter('name =', munki_pkg_name).get(): error_msg = 'No package found with Munki name: %s' % munki_pkg_name if error_msg: self.redirect('/admin/manifest_modifications?msg=%s' % error_msg) return mod = models.BaseManifestModification.GenerateInstance( mod_type, target, munki_pkg_name, manifests=manifests, install_types=install_types, user=users.get_current_user()) mod.put() models.BaseManifestModification.ResetModMemcache(mod_type, target) msg = 'Manifest Modification successfully saved.' self.redirect('/admin/manifest_modifications?mod_type=%s&msg=%s' % (mod_type, msg))
def get(self, filename=None): """GET Args: filename: string like Firefox-1.0.dmg """ auth_return = auth.DoAnyAuth() if hasattr(auth_return, 'email'): email = auth_return.email() if not any(( auth.IsAdminUser(email), auth.IsSupportUser(email), )): raise auth.IsAdminMismatch if filename: filename = urllib.unquote(filename) hash_str = self.request.get('hash') if hash_str: lock = models.GetLockForPackage(filename) try: lock.Acquire(timeout=30, max_acquire_attempts=5) except datastore_locks.AcquireLockError: self.response.set_status(httplib.FORBIDDEN) self.response.out.write('Could not lock pkgsinfo') return pkginfo = models.PackageInfo.get_by_key_name(filename) if pkginfo: self.response.headers[ 'Content-Type'] = 'text/xml; charset=utf-8' if hash_str: self.response.headers['X-Pkgsinfo-Hash'] = self._Hash( pkginfo.plist) self.response.out.write(pkginfo.plist) else: if hash_str: lock.Release() self.response.set_status(httplib.NOT_FOUND) return if hash_str: lock.Release() else: query = models.PackageInfo.all() filename = self.request.get('filename') if filename: query.filter('filename', filename) install_types = self.request.get_all('install_types') for install_type in install_types: query.filter('install_types =', install_type) catalogs = self.request.get_all('catalogs') for catalog in catalogs: query.filter('catalogs =', catalog) pkgs = [] for p in query: pkg = {} for k in p.properties(): if k != '_plist': pkg[k] = getattr(p, k) pkgs.append(pkg) self.response.out.write('<?xml version="1.0" encoding="UTF-8"?>\n') self.response.out.write(plist.GetXmlStr(pkgs)) self.response.headers['Content-Type'] = 'text/xml; charset=utf-8'
def _DisplayHost(self, uuid=None, computer=None): """Displays the report for a single host. Args: uuid: str uuid for host to display. computer: models.Computer object to display. """ if not uuid and not computer: self.response.set_status(404) return elif not computer: computer = models.Computer.get_by_key_name(uuid) else: uuid = computer.uuid popup = self.request.get('format', None) == 'popup' if popup: limit = 1 else: limit = SINGLE_HOST_DATA_FETCH_LIMIT client_log_files = models.ClientLogFile.all().filter( 'uuid =', uuid).order('-mtime').fetch(limit) msu_log = models.ComputerMSULog.all().filter( 'uuid =', uuid).order('-mtime').fetch(limit) applesus_installs = models.InstallLog.all().filter( 'uuid =', uuid).filter('applesus =', True).order('-mtime').fetch(limit) installs = models.InstallLog.all().filter('uuid =', uuid).filter( 'applesus =', False).order('-mtime').fetch(limit) exits = models.PreflightExitLog.all().filter( 'uuid =', uuid).order('-mtime').fetch(limit) install_problems = models.ClientLog.all().filter( 'action =', 'install_problem').filter('uuid =', uuid).order('-mtime').fetch(limit) tags = {} tags_list = [] if computer: # Generate tags data. tags_list = models.Tag.GetAllTagNamesForEntity(computer) for tag in tags_list: tags[tag] = True for tag in models.Tag.GetAllTagNames(): if tag not in tags: tags[tag] = False tags = json.dumps(tags, sort_keys=True) admin.AddTimezoneToComputerDatetimes(computer) computer.connection_dates.reverse() computer.connection_datetimes.reverse() try: uuid_lookup_url = settings.UUID_LOOKUP_URL except AttributeError: uuid_lookup_url = None try: owner_lookup_url = settings.OWNER_LOOKUP_URL except AttributeError: owner_lookup_url = None values = { 'uuid_lookup_url': uuid_lookup_url, 'owner_lookup_url': owner_lookup_url, 'computer': computer, 'applesus_installs': applesus_installs, 'installs': installs, 'client_log_files': client_log_files, 'msu_log': msu_log, 'install_problems': install_problems, 'preflight_exits': exits, 'tags': tags, 'tags_list': tags_list, 'host_report': True, 'limit': SINGLE_HOST_DATA_FETCH_LIMIT, 'is_support_user': auth.IsSupportUser(), 'is_security_user': auth.IsSecurityUser(), 'is_physical_security_user': auth.IsPhysicalSecurityUser(), } if popup: self.Render('host_popup.html', values) else: self.Render('host.html', values)
def _DisplayHost(self, computer, self_report): """Displays the report for a single host. Args: computer: models.Computer object to display. self_report: if True, display as self report. """ uuid = computer.uuid popup = self.request.get('format', None) == 'popup' if popup: limit = 1 else: limit = SINGLE_HOST_DATA_FETCH_LIMIT client_log_files = models.ClientLogFile.all().filter( 'uuid =', uuid).order('-mtime').fetch(limit) msu_log = models.ComputerMSULog.all().filter( 'uuid =', uuid).order('-mtime').fetch(limit) applesus_installs = models.InstallLog.all().filter( 'uuid =', uuid).filter('applesus =', True).order('-mtime').fetch(limit) installs = models.InstallLog.all().filter('uuid =', uuid).filter( 'applesus =', False).order('-mtime').fetch(limit) exits = models.PreflightExitLog.all().filter( 'uuid =', uuid).order('-mtime').fetch(limit) install_problems = models.ClientLog.all().filter( 'action =', 'install_problem').filter('uuid =', uuid).order('-mtime').fetch(limit) tags = {} tags_list = [] groups = {} groups_list = [] duplicates = [] if computer: # Generate tags data. tags_list = models.Tag.GetAllTagNamesForEntity(computer) for tag in tags_list: tags[tag] = True for tag in models.Tag.GetAllTagNames(): if tag not in tags: tags[tag] = False tags = json.dumps(tags, sort_keys=True) # Generate groups data. groups_list = models.Group.GetAllGroupNamesForUser(computer.owner) for group in groups_list: groups[group] = True for group in models.Group.GetAllGroupNames(): if group not in groups: groups[group] = False groups = json.dumps(groups, sort_keys=True) admin.AddTimezoneToComputerDatetimes(computer) computer.connection_dates.reverse() computer.connection_datetimes.reverse() duplicates = models.Computer.all().filter( 'serial =', computer.serial).fetch(20) duplicates = [e for e in duplicates if e.uuid != computer.uuid] try: uuid_lookup_url = settings.UUID_LOOKUP_URL except AttributeError: uuid_lookup_url = None try: owner_lookup_url = settings.OWNER_LOOKUP_URL except AttributeError: owner_lookup_url = None values = { 'report_type': 'host', 'uuid_lookup_url': uuid_lookup_url, 'owner_lookup_url': owner_lookup_url, 'client_site_enabled': settings.CLIENT_SITE_ENABLED, 'computer': computer, 'applesus_installs': applesus_installs, 'installs': installs, 'client_log_files': client_log_files, 'msu_log': msu_log, 'install_problems': install_problems, 'preflight_exits': exits, 'tags': tags, 'tags_list': tags_list, 'groups': groups, 'groups_list': groups_list, 'host_report': True, 'limit': SINGLE_HOST_DATA_FETCH_LIMIT, 'is_support_user': auth.IsSupportUser(), 'is_security_user': auth.IsSecurityUser(), 'is_physical_security_user': auth.IsPhysicalSecurityUser(), 'self_report': self_report, 'duplicates': duplicates, 'tags_xsrf_token': xsrf.XsrfTokenGenerate('tags'), 'groups_xsrf_token': xsrf.XsrfTokenGenerate('groups'), } if popup: self.Render('host_popup.html', values) else: self.Render('host.html', values)
def _AddManifestModification(self): """Adds a new manifest modification to Datastore.""" mod_type = self.request.get('mod_type') targets = [ x.strip() for x in self.request.get('target').split(',') if x.strip() ] munki_pkg_name = self.request.get('munki_pkg_name').strip() manifests = self.request.get_all('manifests') install_types = self.request.get_all('install_types') remove_from_manifest = bool(self.request.get('remove-from-manifest')) # Security users are only able to inject specific packages. if not self.IsAdminUser(): grp = None if auth.IsSupportUser(): grp = common.MANIFEST_MOD_SUPPORT_GROUP # Support users can only inject items into optional_installs. install_types = ['optional_installs'] elif auth.IsSecurityUser(): grp = common.MANIFEST_MOD_SECURITY_GROUP # Security users can only inject items into managed_installs. install_types = ['managed_installs'] munki_pkg_names = models.PackageInfo.GetManifestModPkgNames( grp, only_names=True) if munki_pkg_name not in munki_pkg_names: self.response.out.write('You are not allowed to inject: %s' % munki_pkg_name) self.response.set_status(httplib.FORBIDDEN) return elif mod_type not in [k for k, _ in MOD_GROUP_TYPES.get(grp, [])]: self.response.out.write( 'You are not allowed to inject to: %s' % mod_type) self.response.set_status(httplib.FORBIDDEN) return # Validation. error_msg = None if not targets or not munki_pkg_name or not install_types: error_msg = ( 'target, munki_pkg_name, and install_types are all required') if not error_msg: for manifest in manifests: if manifest not in common.TRACKS: error_msg = 'manifest %s is not in %s' % (manifest, common.TRACKS) if not error_msg: for install_type in install_types: if install_type not in common.INSTALL_TYPES: error_msg = 'install_type %s is not in %s' % ( install_type, common.INSTALL_TYPES) if not error_msg: if not models.PackageInfo.all().filter('name =', munki_pkg_name).get(): error_msg = 'No package found with Munki name: %s' % munki_pkg_name if not error_msg and len(targets) > MAX_TARGETS_PER_POST: error_msg = 'too many targets' if error_msg: self.redirect('/admin/manifest_modifications?msg=%s' % error_msg) return to_put = [] for target in targets: mod = models.BaseManifestModification.GenerateInstance( mod_type, target, munki_pkg_name, manifests=manifests, install_types=install_types, user=users.get_current_user(), remove=remove_from_manifest) to_put.append(mod) gae_util.BatchDatastoreOp(db.put, to_put) for target in targets: models.BaseManifestModification.ResetModMemcache(mod_type, target) msg = 'Manifest Modification successfully saved.' self.redirect('/admin/manifest_modifications?mod_type=%s&msg=%s' % (mod_type, msg))
def get(self, filename=None): """GET Args: filename: string like Firefox-1.0.dmg """ auth_return = auth.DoAnyAuth() if hasattr(auth_return, 'email'): email = auth_return.email() if not any(( auth.IsAdminUser(email), auth.IsSupportUser(email), )): raise auth.IsAdminMismatch if filename: filename = urllib.unquote(filename) hash_str = self.request.get('hash') if hash_str: lock = 'pkgsinfo_%s' % filename if not gae_util.ObtainLock(lock, timeout=5.0): self.response.set_status(403) self.response.out.write('Could not lock pkgsinfo') return pkginfo = models.PackageInfo.get_by_key_name(filename) if pkginfo: self.response.headers[ 'Content-Type'] = 'text/xml; charset=utf-8' if hash_str: self.response.headers['X-Pkgsinfo-Hash'] = self._Hash( pkginfo.plist) self.response.out.write(pkginfo.plist) else: if hash_str: gae_util.ReleaseLock(lock) self.response.set_status(404) return if hash_str: gae_util.ReleaseLock(lock) else: query = models.PackageInfo.all() filename = self.request.get('filename') if filename: query.filter('filename', filename) install_types = self.request.get_all('install_types') for install_type in install_types: query.filter('install_types =', install_type) catalogs = self.request.get_all('catalogs') for catalog in catalogs: query.filter('catalogs =', catalog) pkgs = [] for p in query: pkg = {} for k in p.properties(): if k != '_plist': pkg[k] = getattr(p, k) pkgs.append(pkg) self.response.out.write('<?xml version="1.0" encoding="UTF-8"?>\n') self.response.out.write(plist.GetXmlStr(pkgs)) self.response.headers['Content-Type'] = 'text/xml; charset=utf-8'