Beispiel #1
0
    def swap(self, old_con_id, new_con_id, dryrun=False):
        """
        Replace an EAA connector with another in:
        - application
        - directory

        Args:
            old_con_id (EAAItem): Existing connector to be replaced
            new_con_id (EAAItem): New connector to attach on the applications and directories
            dryrun (bool, optional): Enable dry run. Defaults to False.
        """
        infos_by_conid = {}
        old_con = EAAItem(old_con_id)
        new_con = EAAItem(new_con_id)
        for c in [old_con, new_con]:
            connector_info = self.load(c)
            if not connector_info:
                cli.print_error("EAA connector %s not found." % c)
                cli.print_error("Please check with command 'akamai eaa connector'.")
                cli.exit(2)
            # Save the details for better
            infos_by_conid[c] = connector_info
        app_api = ApplicationAPI(self._config)
        app_processed = 0
        cli.header("#Operation,connector-id,connector-name,app-id,app-name")
        for app_using_old_con, app_name, app_host in self.findappbyconnector(old_con):
            if dryrun:
                cli.print("DRYRUN +,%s,%s,%s,%s" % (
                    new_con, infos_by_conid[new_con].get('name'),
                    app_using_old_con, app_name))
                cli.print("DRYRUN -,%s,%s,%s,%s" % (
                    old_con, infos_by_conid[old_con].get('name'),
                    app_using_old_con, app_name))
            else:
                app_api.attach_connectors(app_using_old_con, [{'uuid_url': new_con.uuid}])
                cli.print("+,%s,%s,%s,%s" % (
                    new_con, infos_by_conid[new_con].get('name'),
                    app_using_old_con, app_name))
                app_api.detach_connectors(app_using_old_con, [{'uuid_url': old_con.uuid}])
                cli.print("-,%s,%s,%s,%s" % (
                    old_con, infos_by_conid[old_con].get('name'),
                    app_using_old_con, app_name))
            app_processed += 1
        if app_processed == 0:
            cli.footer("Connector %s is not used by any application." % old_con_id)
            cli.footer("Check with command 'akamai eaa connector %s apps'" % old_con_id)
        else:
            cli.footer("Connector swapped in %s application(s)." % app_processed)
            cli.footer("Updated application(s) is/are marked as ready to deploy")
Beispiel #2
0
 def detach_connectors(self, app_moniker, connectors):
     """
     Detach connector/s to an application.
     Payload is different from attach above:
     {"agents":["cht3_GEjQWyMW9LEk7KQfg"]}
     """
     logging.info("Detaching {} connectors...".format(len(connectors)))
     api_resp = self.post(
         'mgmt-pop/apps/{applicationId}/agents'.format(applicationId=app_moniker.uuid),
         params={'method': 'delete'}, json={'agents': [c.get('uuid_url') for c in connectors]}
     )
     logging.info("Detach connector response: %s" % api_resp.status_code)
     logging.info("Detach connector app response: %s" % api_resp.text)
     if api_resp.status_code not in (200, 204):
         cli.print_error("Connector(s) %s were not detached from application %s [HTTP %s]" %
                         (','.join([c.get('uuid_url') for c in connectors]), app_moniker, api_resp.status_code))
         cli.print_error("use 'akamai eaa -v ...' for more info")
         cli.exit(2)
Beispiel #3
0
    def rotate(self):
        """
        Update an existing certificate.
        """
        cert_moniker = EAAItem(self._config.certificate_id)
        cli.print("Rotating certificate %s..." % cert_moniker.uuid)
        api_url = 'mgmt-pop/certificates/{certificate_id}'.format(
            certificate_id=cert_moniker.uuid)

        get_resp = self.get(api_url)
        current_cert = get_resp.json()
        # cli.print(json.dumps(current_cert, sort_keys=True, indent=4))

        payload = {}
        payload['name'] = current_cert.get('name')
        cli.print("Certificate CN: %s (%s)" %
                  (current_cert.get('cn'), payload['name']))
        payload['cert_type'] = current_cert.get('cert_type')
        with self._config.cert as f:
            payload['cert'] = f.read()
        with self._config.key as f:
            payload['private_key'] = f.read()
        if self._config.passphrase:
            payload['password'] = self._config.passphrase
        put_resp = self.put(api_url,
                            json=payload,
                            params={
                                'expand': 'true',
                                'limit': 0
                            })
        if put_resp.status_code == 200:
            new_cert = put_resp.json()
            cli.footer(("Certificate %s updated, %s application/IdP(s) "
                        "have been marked ready for deployment.") %
                       (cert_moniker.uuid, new_cert.get('app_count')))
            if self._config.deployafter:
                self.deployafter(cert_moniker.uuid)
            else:
                cli.footer("Please deploy at your convience.")
        else:
            cli.print_error("Error rotating certificate, see response below:")
            cli.print_error(put_resp.text)
            cli.exit(2)
Beispiel #4
0
    def attach_connectors(self, app_moniker, connectors):
        """
        Attach connector/s to an application.

        :param EAAItem app_moniker: Application Moniker (prefix + UUID)
        :param list connectors: Connector list, expected list item format is dict {'uuid_url': '<UUID>'}
        """
        # POST on mgmt-pop/apps/DBMcU6FwSjKa7c9sny4RLg/agents
        # Body
        # {"agents":[{"uuid_url":"cht3_GEjQWyMW9LEk7KQfg"}]}
        logging.info("Attaching {} connectors...".format(len(connectors)))
        api_resp = self.post(
            'mgmt-pop/apps/{applicationId}/agents'.format(applicationId=app_moniker.uuid),
            json={'agents': connectors}
        )
        logging.info("Attach connector response: %s" % api_resp.status_code)
        logging.info("Attach connector app response: %s" % api_resp.text)
        if api_resp.status_code not in (200, 201):
            cli.print_error("Connector(s) %s were not attached to application %s [HTTP %s]" %
                            (','.join([c.get('uuid_url') for c in connectors]), app_moniker, api_resp.status_code))
            cli.print_error("use 'akamai eaa -v ...' for more info")
            cli.exit(2)
Beispiel #5
0
    def synchronize_group(self, group_uuid):
        """
        Synchronize a group within the directory

        Args:
            group_uuid (EAAItem): Group UUID e.g. grp://abcdef
        """
        """
        API call

        POST https://control.akamai.com/crux/v1/mgmt-pop/groups/16nUm7dCSyiihfCvMiHrMg/sync
        Payload: {}
        Response: {"response": "Syncing Group Sales Department"}
        """
        group = EAAItem(group_uuid)
        retry_remaining = self._config.retry + 1
        while retry_remaining > 0:
            retry_remaining -= 1
            cli.print("Synchronizing %s [retry=%s]..." %
                      (group_uuid, retry_remaining))
            resp = self.get(
                'mgmt-pop/directories/{dir_uuid}/groups/{group_uuid}'.format(
                    dir_uuid=self._directory_id, group_uuid=group.uuid))
            if resp.status_code != 200:
                logging.error("Error retrieve group info (%s)" %
                              resp.status_code)
                cli.exit(2)
            group_info = resp.json()
            if group_info.get('last_sync_time'):
                last_sync = datetime.datetime.fromisoformat(
                    group_info.get('last_sync_time'))
                delta = datetime.datetime.utcnow() - last_sync
                cli.print(
                    "Last sync of group %s was @ %s UTC (%d seconds ago)" %
                    (group_info.get('name'), last_sync, delta.total_seconds()))
                if delta.total_seconds() > self._config.mininterval:
                    sync_resp = self.post(
                        'mgmt-pop/groups/{group_uuid}/sync'.format(
                            group_uuid=group.uuid))
                    if sync_resp.status_code != 200:
                        cli.print_error(
                            "Fail to synchronize group (API response code %s)"
                            % sync_resp.status_code)
                        cli.exit(3)
                    else:
                        cli.print(
                            "Synchronization of group %s (%s) successfully requested."
                            % (group_info.get('name'), group))
                        break
                else:
                    cli.print_error(
                        "Last group sync is too recent, sync aborted. %s seconds interval is required."
                        % self._config.mininterval)
                    if retry_remaining == 0:
                        cli.exit(2)
                    else:
                        sleep_time = last_sync + datetime.timedelta(seconds=self._config.mininterval) - \
                                     datetime.datetime.utcnow()
                        cli.print(
                            "Sleeping for %s, press Control-Break to interrupt"
                            % sleep_time)
                        time.sleep(sleep_time.total_seconds())
Beispiel #6
0
    def get_logs(self, drpc_args, logtype=EventType.USER_ACCESS, output=None):
        """
        Fetch the logs, by default the user access logs.
        """
        if not isinstance(logtype, self.EventType):
            raise ValueError("Unsupported log type %s" % logtype)

        scroll_id = None
        try:
            # Fetches the logs for given drpc args
            resp = self.post(self.get_api_url(logtype), json=drpc_args)
            if resp.status_code != requests.codes.ok:
                logging.error("Invalid API response status code: %s" %
                              resp.status_code)
                return None

            resj = resp.json()
            logging.debug("JSON> %s" % json.dumps(resj, indent=2))

            if 'message' in resj:
                # Get msg and scroll_id based on the type of logs
                # Since it is two different API in the back-end
                if logtype == self.EventType.USER_ACCESS:
                    msg = resj.get('message')[0][1]
                    scroll_id = msg.get('scroll_id')
                elif logtype == self.EventType.ADMIN:
                    msg = resj.get('message')
                    if 'scroll_id' in msg.get('metadata'):
                        scroll_id = msg.get('metadata').get('scroll_id')
                else:
                    raise NotImplementedError("Doesn't support log type %s" %
                                              logtype)

                logging.debug("scroll_id: %s" % scroll_id)
                count = 0

                if logtype == self.EventType.USER_ACCESS:
                    for timestamp, response in six.iteritems(msg):
                        try:
                            if not timestamp.isdigit():
                                logging.debug("Ignored timestamp '%s': %s" %
                                              (timestamp, response))
                                continue
                            logging.debug("flog is %s" %
                                          type(response['flog']).__name__)
                            logging.debug("Scanned timestamp: %s" % timestamp)
                            if int(timestamp) < int(drpc_args.get('sts')):
                                raise Exception(
                                    "Out of bound error: incoming event time %s vs. start set to %s"
                                    % (timestamp, drpc_args.get('sts')))
                            if int(timestamp) >= int(drpc_args.get('ets')):
                                raise Exception(
                                    "Out of bound error: incoming event time %s vs. end set to %s"
                                    % (timestamp, drpc_args.get('ets')))
                            local_time = datetime.datetime.fromtimestamp(
                                int(timestamp) / 1000)
                            if isinstance(response,
                                          dict) and 'flog' in response:
                                line = "%s\n" % ' '.join(
                                    [local_time.isoformat(), response['flog']])
                                if config.json:
                                    result = self._userlog_regexp.search(line)
                                    output.write("%s\n" % json.dumps(
                                        EventLogAPI.userlog_prepjson(
                                            result.groupdict())))
                                else:
                                    output.write(line)
                                logging.debug("### flog ## %s" %
                                              response['flog'])
                                self.line_count += 1
                                count += 1
                        except Exception:
                            logging.exception("Error parsing access log line")
                elif logtype == self.EventType.ADMIN:
                    for item in msg.get('data'):
                        try:
                            local_time = datetime.datetime.fromtimestamp(
                                int(item.get('ts') / 1000))
                            line = u"{},{}\n".format(local_time.isoformat(),
                                                     item.get('splunk_line'))
                            if config.json:
                                admin_event_data = line.split(',')
                                if isinstance(
                                        admin_event_data,
                                        list) and len(admin_event_data) == 6:
                                    admin_event_dict = {
                                        'datetime': admin_event_data[0],
                                        'username': admin_event_data[1],
                                        'resource_type': admin_event_data[2],
                                        'resource': admin_event_data[3],
                                        'event': admin_event_data[4],
                                        'event_type':
                                        admin_event_data[5].strip()
                                    }
                                    output.write("%s\n" %
                                                 json.dumps(admin_event_dict))
                                else:
                                    cli.print_error("Error parsing line ")
                            else:
                                output.write(line)
                            self.line_count += 1
                            count += 1
                        except Exception as e:
                            logging.exception(
                                'Error parsing admin log line: %s, content: %s'
                                % (e, item.get('splunk_line')))
            else:
                logging.error('Error: no data(message) in response.')
                logging.error(drpc_args)
                logging.error(json.dumps(resj))
                self.error_count += 1
            resp.close()
        except Exception:
            if "resp" in locals():
                logging.debug("resp.status_code %s" % resp.status_code)
                logging.debug("resp.text %s" % resp.text)
            logging.error(drpc_args)
            logging.exception("Exception in get_logs")
        return scroll_id