marksull / fmcapi

A Python package designed to help users of Cisco's FMC interface with its API.
BSD 3-Clause "New" or "Revised" License
81 stars 57 forks source link

Adding accessrule based on application instead of destination port #72

Closed MSD101 closed 4 years ago

MSD101 commented 4 years ago

Hello

I am trying to post/put the access rule to add the application ( eg. Facebook) instead of ports based rule.

I checked the unit test script and don't see the option for the application.

Would it be possible to add the access rule to reference the application instead of the destination port?

Thanks for your help

daxm commented 4 years ago

Though the VALID_FOR_JSON lists an "application" variable there is no code in the AccessRule class to use it. Are you interested in developing that feature out?

MSD101 commented 4 years ago

Thanks for looking into this.

I am still learning Python and have very basic knowledge of it. I would love to help in anyway to develop this feature if someone can guide/help me.

Thanks again.

daxm commented 4 years ago

I hear ya! When I started this project I was a novice (probably still am) in programming in Python. That said, check out the AccessRules file (https://github.com/daxm/fmcapi/blob/master/fmcapi/api_objects/policy_services/accessrules.py) and try to read through it. I think you should be able to see how to add a new "sub-section" to the AccessRules Class.

Give it a shot! If you can get it to work in your environment just ping me and I'll help you get your changes integrated into fmcapi project.

daxm commented 4 years ago

I hear ya! When I started this project I was a novice (probably still am) in programming in Python. That said, check out the AccessRules file (https://github.com/daxm/fmcapi/blob/master/fmcapi/api_objects/policy_services/accessrules.py) and try to read through it. I think you should be able to see how to add a new "sub-section" to the AccessRules Class.

Give it a shot! If you can get it to work in your environment just ping me and I'll help you get your changes integrated into fmcapi project.

daxm commented 4 years ago

I hear ya! When I started this project I was a novice (probably still am) in programming in Python. That said, check out the AccessRules file (https://github.com/daxm/fmcapi/blob/master/fmcapi/api_objects/policy_services/accessrules.py) and try to read through it. I think you should be able to see how to add a new "sub-section" to the AccessRules Class.

Give it a shot! If you can get it to work in your environment just ping me and I'll help you get your changes integrated into fmcapi project.

MSD101 commented 4 years ago

Yes I agree and will try. I was looking into the accessrules.py and added below

from fmcapi.api_objects.object_services.applications import Applications !

def application_name(self, action, name=""): """ Add/modify name to applicatonname field of AccessRules object.

    :param action: (str) 'add', 'remove', or 'clear'
    :param name: (str) Name of Application in FMC.
    :return: None
    """
    logging.debug("In application_name() for AccessRules class.")
    if action == "add":
        app = Applications(fmc=self.fmc)
        app.get(name=name)
        if "id" in app.__dict__:
            if "app_name" in self.__dict__:
                new_app = {"name": app.name, "id": app.id, "type": app.type}
                duplicate = False
                for obj in self.app_name["objects"]:
                    if obj["name"] == new_app["name"]:
                        duplicate = True
                        break
                if not duplicate:
                    self.app_name["objects"].append(new_app)
                    logging.info(
                        f'Adding "{name}" to applicationname for this AccessRules.'
                    )
            else:
                self.applicationname = {
                    "objects": [
                        {"name": app.name, "id": app.id, "type": app.type}
                    ]
                }
                logging.info(
                    f'Adding "{name}" to applicationname for this AccessRules.'
                )
        else:
            logging.warning(
                f'Application: "{name}", '
                f"not found.  Cannot add to AccessRules."
            )

And I tried to create the accessrule with NTP application and see below while executing the script :

INFO:root:GET success. Object with name: "test-POLICY" and id: "0050569D-669F-0ed3-0000-146028892476" fetched from FMC. INFO:root:GET success. Object with name: "test-1" and id: "641c654e-4f4a-11ea-a9d3-d4e43ad758b1" fetched from FMC. INFO:root:Adding "test-1" to sourceZones for this AccessRules. INFO:root:GET success. Object with name: "test-2" and id: "40facaec-4f4a-11ea-a9d3-d4e43ad758b1" fetched from FMC. INFO:root:Adding "test-2" to destinationZones for this AccessRules. INFO:root:GET success. Object with name: "NTP" and id: "767" fetched from FMC. INFO:root:Adding "NTP" to applicationname for this AccessRules. INFO:root:POST success. Object with name: "test-rule" and id: "" created in FMC. INFO:root:Test ACPRule done.

But when I check the rule with GUI, I don't see NTP application added to rule.

Still troubleshooting to see if I find what I am doing wrong.

Thanks

MysticRyuujin commented 4 years ago

I believe what you're doing wrong is re-using the "objects" key, it should be "applications"

When posting / getting an ACE you'll notice that Ports come back as "objects": [] but Applications come back as "applications": []

MSD101 commented 4 years ago

Thanks for your help.

I updated the function to below and replace the objects with applications.

def application_name(self, action, name=""): """ Add/modify name to applicatonname field of AccessRules object.

    :param action: (str) 'add', 'remove', or 'clear'
    :param name: (str) Name of Application in FMC.
    :return: None
    """
    logging.debug("In application_name() for AccessRules class.")
    if action == "add":
        app = Applications(fmc=self.fmc)
        app.get(name=name)
        if "id" in app.__dict__:
            if "app_name" in self.__dict__:
                new_app = {"name": app.name, "id": app.id, "type": app.type}
                duplicate = False
                for obj in self.app_name["applications"]:
                    if obj["name"] == new_app["name"]:
                        duplicate = True
                        break
                if not duplicate:
                    self.app_name["applications"].append(new_app)
                    logging.info(
                        f'Adding "{name}" to applicationname for this AccessRules.'
                    )
            else:
                self.applicationname = {
                    "applications": [
                        {"name": app.name, "id": app.id, "type": app.type}
                    ]
                }
                logging.info(
                    f'Adding "{name}" to applicationname for this AccessRules.'
                )
        else:
            logging.warning(
                f'Application: "{name}", '
                f"not found.  Cannot add to AccessRules."
            )

Still samething.

INFO:root:GET success. Object with name: "NTP" and id: "767" fetched from FMC. INFO:root:Adding "NTP" to applicationname for this AccessRules. INFO:root:POST success. Object with name: ""test-rule" and id: "" created in FMC. INFO:root:Test ACPRule done.

MysticRyuujin commented 4 years ago

Can you do a show_json on the ACE before it's posted?

MysticRyuujin commented 4 years ago

Try this:

    def application(self, action, name=""):
        """
        Add/modify name to applications field of AccessRules object.
        :param action: (str) 'add', 'remove', or 'clear'
        :param name: (str) Name of Application in FMC.
        :return: None
        """
        logging.debug("In application() for AccessRules class.")
        if action == "add":
            app = Applications(fmc=self.fmc)
            app.get(name=name)
            if "id" in app.__dict__:
                if "applications" in self.__dict__:
                    new_app = {"name": app.name, "id": app.id, "type": app.type}
                    duplicate = False
                    if "applications" not in self.applications:
                        self.__dict__["applications"]["applications"] = []
                    for obj in self.applications["applications"]:
                        if obj["name"] == new_app["name"]:
                            duplicate = True
                            break
                    if not duplicate:
                        self.applications["applications"].append(new_app)
                        logging.info(
                            f'Adding "{name}" to applications for this AccessRules.'
                        )
                else:
                    self.applications = {
                        "applications": [
                            {"name": app.name, "id": app.id, "type": app.type}
                        ]
                    }
                    logging.info(
                        f'Adding "{name}" to applications for this AccessRules.'
                    )
            else:
                logging.warning(
                    f'Application: "{name}", '
                    f"not found.  Cannot add to AccessRules."
                )

I think you're incorrectly creating a property called applicationname instead of applications

MSD101 commented 4 years ago

It worked.

I just need to understand below code to better understand the flow.

if "applications" in self.dict: new_app = {"name": app.name, "id": app.id, "type": app.type} duplicate = False if "applications" not in self.applications: self.dict["applications"]["applications"] = [] for obj in self.applications["applications"]: if obj["name"] == new_app["name"]: duplicate = True break if not duplicate: self.applications["applications"].append(new_app) logging.info( f'Adding "{name}" to applications for this AccessRules.' ) else: self.applications = { "applications": [ {"name": app.name, "id": app.id, "type": app.type} ] } else: self.applications = { "applications": [ {"name": app.name, "id": app.id, "type": app.type} ] } logging.info( f'Adding "{name}" to applications for this AccessRules.' )

I'll also try to add remove and clear action using the your suggestions.

Thanks a lot.

MysticRyuujin commented 4 years ago

The primary difference between what I wrote and what you wrote was just the name of the property.

If you turn around and get the newly created ACE from the FCM you'll see the JSON it returns is:

{
    "applications": {
        "applications": [
            { some application object here }
        ]
    }
}

Compare that to the JSON that you're sending and you'll see what I mean.

daxm commented 4 years ago

MysticRyuujin, thanks for helping out here! Looks like you both have almost gotten the code snippet done.

MSD101, when you have the code how you like it, do you want to do a Pull Request or do you want to post your snippet here and I'll add it in for you. One of the cool things about doing a PR is that your name will be added to the contributors list. (You can say you've contributed code to an open source project!)

MSD101 commented 4 years ago

Thanks MysticRyuujin and daxm. Appreciate all your help.

I was able to successfully add and test the add/remove/clear action for the application. It's working fine.

I made below Changes to accessrules.py. Please review just to make sure I didn't do anything wrong.

I can also do the pull request ( never done it before), if you can walk me through the process.

Accessrules.py changes: -

from fmcapi.api_objects.object_services.applications import Applications

def application(self, action, name=""):
    """
    Add/modify name to applications field of AccessRules object.
    :param action: (str) 'add', 'remove', or 'clear'
    :param name: (str) Name of Application in FMC.
    :return: None
    """
    logging.debug("In application() for AccessRules class.")
    if action == "add":
        app = Applications(fmc=self.fmc)
        app.get(name=name)
        if "id" in app.__dict__:
            if "applications" in self.__dict__:
                new_app = {"name": app.name, "id": app.id, "type": app.type}
                duplicate = False
                if "applications" not in self.applications:
                    self.__dict__["applications"]["applications"] = []
                for obj in self.applications["applications"]:
                    if obj["name"] == new_app["name"]:
                        duplicate = True
                        break
                if not duplicate:
                    self.applications["applications"].append(new_app)
                    logging.info(
                        f'Adding "{name}" to applications for this AccessRules.'
                    )
            else:
                self.applications = {
                    "applications": [
                        {"name": app.name, "id": app.id, "type": app.type}
                    ]
                }
                logging.info(
                    f'Adding "{name}" to applications for this AccessRules.'
                )
        else:
            logging.warning(
                f'Application: "{name}", '
                f"not found.  Cannot add to AccessRules."
            )
    elif action == "remove":
        app = Applications(fmc=self.fmc)
        app.get(name=name)
        if "id" in app.__dict__:
            if "applications" in self.__dict__:
                applications = []
                for obj in self.applications["applications"]:
                    if obj["name"] != name:
                        applications.append(obj)
                self.applications["applications"] = applications
                logging.info(
                        f'Removed "{name}" from applications for this AccessRules.'
                    )
            else:
                 logging.info(
                    "Application doesn't exist for this AccessRules.  Nothing to remove."
                )
        else:
            logging.warning(
                f"Application, {name}, not found.  Cannot remove from AccessRules."
            )        
    elif action == "clear":
        if "applications" in self.__dict__:
            del self.applications
            logging.info("All Applications removed from this AccessRules object.")
daxm commented 4 years ago

Let me add it in as I think it would be best served as a subroutine in the AccessRules class instead of being imported. (All the other Access Rule features are there too.)  I'm sure your way is more versatile but it would be a 1-off.  I'd love to have you issue a PR.  In order to do so you'll need to fork the project, make the changes in your fork, and than issue a pull request.

On 4/21/20 3:33 PM, MSD101 wrote:

Thanks MysticRyuujin and daxm. Appreciate all your help.

I was able to successfully add and test the add/remove/clear action for the application. It's working fine.

I made below Changes to accessrules.py. Please review just to make sure I didn't do anything wrong.

I can also do the pull request ( never done it before), if you can walk me through the process.

Accessrules.py changes: -

from fmcapi.api_objects.object_services.applications import Applications

|def application(self, action, name=""): """ Add/modify name to applications field of AccessRules object. :param action: (str) 'add', 'remove', or 'clear' :param name: (str) Name of Application in FMC. :return: None """ logging.debug("In application() for AccessRules class.") if action == "add": app = Applications(fmc=self.fmc) app.get(name=name) if "id" in app.dict: if "applications" in self.dict: new_app = {"name": app.name, "id": app.id, "type": app.type} duplicate = False if "applications" not in self.applications: self.dict["applications"]["applications"] = [] for obj in self.applications["applications"]: if obj["name"] == new_app["name"]: duplicate = True break if not duplicate: self.applications["applications"].append(new_app) logging.info( f'Adding "{name}" to applications for this AccessRules.' ) else: self.applications = { "applications": [ {"name": app.name, "id": app.id, "type": app.type} ] } logging.info( f'Adding "{name}" to applications for this AccessRules.' ) else: logging.warning( f'Application: "{name}", ' f"not found. Cannot add to AccessRules." ) elif action == "remove": app = Applications(fmc=self.fmc) app.get(name=name) if "id" in app.dict: if "applications" in self.dict: applications = [] for obj in self.applications["applications"]: if obj["name"] != name: applications.append(obj) self.applications["applications"] = applications logging.info( f'Removed "{name}" from applications for this AccessRules.' ) else: logging.info( "Application doesn't exist for this AccessRules. Nothing to remove." ) else: logging.warning( f"Application, {name}, not found. Cannot remove from AccessRules." ) elif action == "clear": if "applications" in self.dict: del self.applications logging.info("All Applications removed from this AccessRules object.") |

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/daxm/fmcapi/issues/72#issuecomment-617425843, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAZOMZ6MPZ226J7RSO63U6TRNYGJLANCNFSM4MK6IHQA.

MSD101 commented 4 years ago

Daxm, I tried to do the pull request based on your above suggestion. Let me know if I did it correctly or not?

Also while configuring the applications in access rule policy there are two options 1) applications and 2) applicationfilters.

I added separate options for application ( add/remove/clear) and for applicationfilters ( addappfilter/removeappfilter/clear) under accessrules.py. Not sure if you want to include both options to have the support to add applicationfilters while configuring the access rules.

Thanks

daxm commented 4 years ago

I don't see a PR incoming. Not sure where it went. :-)

daxm commented 4 years ago

@MSD101, Your changes have been committed and published. Can you triple check they work from the official release? If so, then we can close this issue.

MSD101 commented 4 years ago

Thank you. Looks good.