Exemplo n.º 1
0
    def findappbyconnector(self, connector_moniker):
        """
        Find EAA Applications using a particular connector.

        Args:
            connector_moniker (EAAItem): Connector ID.

        Returns:
            Tuple of 3 values:
                - application moniker
                - application name
                - application host (external hostname)

        Raises:
            TypeError: If the argument is wrong type.
        """
        if not isinstance(connector_moniker, EAAItem):
            raise TypeError("EAAItem expected.")
        url_params = {'limit': ApplicationAPI.LIMIT_SOFT, 'expand': 'true'}
        search_app = self.get('mgmt-pop/apps', params=url_params)
        apps = search_app.json()
        logging.debug("Searching app using %s..." % connector_moniker)
        for app in apps.get('objects', []):
            for con in app.get('agents', []):
                app_moniker = EAAItem("app://" + app.get('uuid_url'))
                con_moniker = EAAItem("con://" + con.get('uuid_url'))
                if con_moniker == connector_moniker:
                    yield app_moniker, app.get('name'), app.get('host')
Exemplo n.º 2
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")
Exemplo n.º 3
0
 def __init__(self, configuration, directory_moniker=None):
     super(DirectoryAPI, self).__init__(configuration,
                                        BaseAPI.API_Version.OpenAPI)
     self._directory = None
     self._directory_id = None
     if directory_moniker:
         self._directory = EAAItem(directory_moniker)
         self._directory_id = self._directory.uuid
Exemplo n.º 4
0
 def findidpbycert(self, certid):
     """Find IdP using certificate identified by `certid`"""
     url_params = {'limit': 10000}
     search_idp = self.get('mgmt-pop/idp', params=url_params)
     idps = search_idp.json()
     for i in idps.get('objects', []):
         if i.get('cert') == certid:
             yield (EAAItem("idp://%s" % i.get('uuid_url')), i.get('name'))
Exemplo n.º 5
0
 def findappsbycert(self, certid):
     """Find application using certificate identified by `certid`"""
     url_params = {'limit': 10000}
     search_app = self.get('mgmt-pop/apps', params=url_params)
     apps = search_app.json()
     for a in apps.get('objects', []):
         if a.get('cert') == certid:
             yield (EAAItem("app://%s" % a.get('uuid_url')), a.get('name'))
Exemplo n.º 6
0
    def create(self, raw_app_config):
        """
        Create a new EAA application configuration.
        :param app_config: configuration as JSON string

        Note: the portal use the POST to create a new app with a minimal payload:
              {"app_profile":1,"app_type":1,"client_app_mode":1,"app_profile_id":"Fp3RYok1EeSE6AIy9YR0Dw",
              "name":"tes","description":"test"}
              We should do the same here
        """
        app_config = json.loads(self.parse_template(raw_app_config))
        logging.debug("Post Jinja parsing:\n%s" % json.dumps(app_config))
        app_config_create = {
            "app_profile":
            app_config.get('app_profile'),
            "app_type":
            app_config.get('app_type', ApplicationAPI.Type.Hosted.value),
            "name":
            app_config.get('name'),
            "description":
            app_config.get('description')
        }
        newapp = self.post('mgmt-pop/apps', json=app_config_create)
        logging.info("Create app core: %s %s" %
                     (newapp.status_code, newapp.text))
        if newapp.status_code != 200:
            cli.exit(2)
        newapp_config = newapp.json()
        logging.info("New app JSON:\n%s" % newapp.text)
        app_moniker = EAAItem("app://{}".format(newapp_config.get('uuid_url')))
        logging.info("UUID of the newapp: %s" % app_moniker)

        # Now we push everything else as a PUT
        self.put('mgmt-pop/apps/{applicationId}'.format(
            applicationId=app_moniker.uuid),
                 json=app_config)

        # Sub-components of the application configuration definition

        # --- Connectors
        if app_config.get('agents'):
            self.attach_connectors(app_moniker, app_config.get('agents', []))

        # IdP, Directories, Groups
        self.create_auth(app_moniker, app_config)

        # --- Access Control rules
        self.create_acl(app_moniker, app_config)

        # --- Other services
        # TODO: implement

        # URL based policies
        self.create_urlbasedpolicies(app_moniker, app_config)

        # At the end we reload the app entirely
        cli.print(json.dumps(self.load(app_moniker)))
Exemplo n.º 7
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)
Exemplo n.º 8
0
    def status(self):
        """
        Display status for a particular certificate.
        """
        cert_moniker = EAAItem(self._config.certificate_id)
        cli.header("#App/IdP ID,name,status")
        app_api = ApplicationAPI(config)
        for app_id, app_name in self.findappsbycert(cert_moniker.uuid):
            # We don't need much info so expand=False to keep it quick
            app_config = app_api.load(app_id, expand=False)
            #            cli.print(app_config)
            app_status = ApplicationAPI.Status(app_config.get('app_status'))
            cli.print("%s,%s,%s" % (app_id, app_name, app_status.name))

        idp_api = IdentityProviderAPI(config)
        for idp_id, idp_name in self.findidpbycert(cert_moniker.uuid):
            idp_config = idp_api.load(idp_id)
            idp_status = ApplicationAPI.Status(idp_config.get('idp_status'))
            cli.print("%s,%s,%s" % (idp_id, idp_name, idp_status.name))
Exemplo n.º 9
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())
Exemplo n.º 10
0
    def process_command(self):
        """
        Process command passed from the CLI.
        """
        applications = list()
        appgroups = list()
        if self._config.application_id == '-':
            # nested if below because we don't do anything on create in this section
            if self._config.action != 'create':
                for line in sys.stdin:
                    scanned_items = line.split(',')
                    if len(scanned_items) == 0:
                        logging.warning("Cannot parse line: %s" % line)
                        continue
                    try:
                        scanned_obj = EAAItem(scanned_items[0])
                        if scanned_obj.objtype == EAAItem.Type.Application:
                            applications.append(scanned_obj)
                        elif scanned_obj.objtype == EAAItem.Type.ApplicationGroupAssociation:
                            appgroups.append(scanned_obj)
                    except EAAInvalidMoniker:
                        logging.warning("Invalid application moniker: %s" % scanned_items[0])
        else:
            logging.info("Single app %s" % config.application_id)
            applications.append(EAAItem(config.application_id))
            logging.info("%s" % EAAItem(config.application_id))

        if config.action == "deploy":
            for a in applications:
                self.deploy(a)
                cli.print("Application %s deployment requested, it may take a few minutes before it gets live." % a)
        elif config.action == "create":
            # new_config = json.load(sys.stdin)
            new_config = sys.stdin.read()
            self.create(new_config)
        elif config.action == "update":
            if len(applications) > 1:
                raise Exception("Batch operation not supported")
            app = applications[0]
            new_config = json.load(sys.stdin)
            self.update(app, new_config)
            cli.print("Configuration for application %s has been updated." % app)
        elif config.action == "delete":
            for a in applications:
                self.delete_app(a)
        elif config.action == "add_dnsexception":
            for a in applications:
                self.add_dnsexception(a)
        elif config.action == "del_dnsexception":
            for a in applications:
                self.del_dnsexception(a)
        elif config.action == 'viewgroups':
            for a in applications:
                self.loadgroups(a)
        elif config.action == 'delgroup':
            for ag in appgroups:
                self.delgroup(ag)
        elif config.action in ('attach', 'detach'):
            for a in applications:
                connectors = []
                for c in set(config.connector_id):
                    con_moniker = EAAItem(c)
                    if con_moniker.objtype is not EAAItem.Type.Connector:
                        raise TypeError("Invalid type of connector: %s" % c)
                    connectors.append({"uuid_url": con_moniker.uuid})
                if config.action == 'attach':
                    self.attach_connectors(a, connectors)
                elif config.action == 'detach':
                    self.detach_connectors(a, connectors)
        else:  # view by default
            for a in applications:
                app_config = self.load(a)
                print(json.dumps(app_config))