From cdab0462424a28065f3fc66826a72b0b495cdac2 Mon Sep 17 00:00:00 2001 From: Tim Jarrett Date: Fri, 20 Sep 2024 09:21:16 -0400 Subject: [PATCH 1/3] work in progress on custom roles --- docs/docs.md | 1 + docs/roles.md | 20 +++++++++++++ veracode_api_py/api.py | 29 ++++++++++++++++++- veracode_api_py/identity.py | 57 ++++++++++++++++++++++++++++++++++++- 4 files changed, 105 insertions(+), 2 deletions(-) create mode 100644 docs/roles.md diff --git a/docs/docs.md b/docs/docs.md index 43dace5..7959632 100644 --- a/docs/docs.md +++ b/docs/docs.md @@ -22,6 +22,7 @@ See the topics below for more information on how to use this library. * [Teams](teams.md) - create, update, access, and delete teams. * [Business Units](businessunits.md) - create, update, access, and delete business units. * [API Credentials](apicreds.md) - create, access, renew, and revoke API credentials. +* [Roles](roles.md) - access system roles and permissions; create, update, access, and delete custom roles. ## API Object diff --git a/docs/roles.md b/docs/roles.md new file mode 100644 index 0000000..edf19ea --- /dev/null +++ b/docs/roles.md @@ -0,0 +1,20 @@ +# Roles and Permissions + +The following methods call Veracode REST APIs and return JSON. + +- `Roles().get_all()`: get the list of roles for the organization. +- `Roles().get(role_guid)`: get the role definition for a given role. +- `Roles().create(role_name,role_description,is_api (opt),jit_assignable(opt),jit_assignable_default (opt),permissions (opt),child_roles (opt))`: create a role named `role_name`. You must specify either `permissions` or `child_roles`, or both. Arguments include: + - `role_description`: The human readable description of the role. + - `is_api`: Set to `True` to create a role for an API user. Defaults to `False`. + - `jit_assignable`: Set to `True` to allow the role to be assigned by a SAML assertion using just-in-time provisioning. Defaults to `True`. + - `jit_assignable_default`: Set to `True` to allow the role to be assigned by default during just-in-time provisioning. Defaults to `True`. + - `permissions`: An array of permission names. Use `Permissions().get_all()` to see the list of assignable permissions. + - `child_roles`: An array of role names. Adding a child role to a custom role gives the user all the permissions contained in the child role, in addition to any permissions defined in `permissinos`. You can add more than one child role. +- `Roled().update(role_name,role_description,role_guid,is_api (opt),jit_assignable(opt),jit_assignable_default (opt),permissions (opt),child_roles (opt))`: update the role identified by `role_guid` with the provided information. +- `Roles().delete(role_guid)`: delete the role identified by `role_guid`. Note: You can only delete custom roles. +- `Permissions().get_all()`: get the list of permissions that can be part of custom roles. +- `Permissions().get(permission_guid)`: get the permission definition for a given permission. + + +[All docs](docs.md) diff --git a/veracode_api_py/api.py b/veracode_api_py/api.py index b2473a4..3c5154b 100644 --- a/veracode_api_py/api.py +++ b/veracode_api_py/api.py @@ -24,7 +24,7 @@ from .policy import Policies from .sca import ComponentActivity, Workspaces, SBOM, SCAApplications from .collections import Collections -from .identity import Users, Teams, BusinessUnits, APICredentials, Roles +from .identity import Users, Teams, BusinessUnits, APICredentials, Roles, Permissions from .healthcheck import Healthcheck from .dynamic import Analyses, Scans, Occurrences, Configuration, CodeGroups, ScanCapacitySummary, ScanOccurrences, \ ScannerVariables, DynUtils @@ -314,6 +314,33 @@ def revoke_creds(self, api_id): def get_roles(self): return Roles().get_all() + + def get_role(self, role_guid: GUID): + return Roles().get(role_guid=role_guid) + + def create_role(self, role_name, role_description, is_api=False, jit_assignable=True, + jit_assignable_default=True, permissions=[], child_roles=[]): + return Roles().create(role_name=role_name, role_description=role_description, + is_api=is_api, jit_assignable=jit_assignable, + jit_assignable_default=jit_assignable_default, + permissions=permissions, child_roles=child_roles) + + def update_role(self, role_name, role_description, role_guid: UUID, is_api=False, + jit_assignable=True, jit_assignable_default=True, + permissions=[], child_roles=[]): + return Roles().update(role_name=role_name, role_description=role_description, + role_guid=role_guid, is_api=is_api, jit_assignable=jit_assignable, + jit_assignable_default=jit_assignable_default, + permissions=permissions, child_roles=child_roles) + + def delete_role(self, role_guid: UUID): + return Roles().delete(role_guid=role_guid) + + def get_permissions(self): + return Permissions().get_all() + + def get_permission(self, permission_guid: UUID): + return Permissions().get(permission_guid=permission_guid) ## SCA APIs - note must be human user to use these, not API user diff --git a/veracode_api_py/identity.py b/veracode_api_py/identity.py index 471f6c8..fa387b3 100644 --- a/veracode_api_py/identity.py +++ b/veracode_api_py/identity.py @@ -251,5 +251,60 @@ def revoke (self, api_id): return APIHelper()._rest_request(self.base_uri + '/{}'.format(api_id), "DELETE") class Roles(): + base_uri = "api/authn/v2/roles" def get_all(self): - return APIHelper()._rest_paged_request("api/authn/v2/roles","GET","roles",{'page':0}) + return APIHelper()._rest_paged_request(self.base_uri,"GET","roles",{'page':0}) + + def get(self, role_guid: UUID): + return APIHelper()._rest_request("{}/{}".format(self.base_uri,role_guid),"GET") + + def create(self, role_name, role_description, is_api=False, jit_assignable=True, + jit_assignable_default=True, permissions=[], child_roles=[]): + return self._create_or_update("CREATE", role_name=role_name, role_description=role_description, + is_api=is_api, jit_assignable=jit_assignable, + jit_assignable_default=jit_assignable_default, + permissions=permissions, child_roles=child_roles) + + def update(self, role_name, role_description, role_guid: UUID, is_api=False, + jit_assignable=True, jit_assignable_default=True, + permissions=[], child_roles=[]): + # TODO handle partial and incremental + return self._create_or_update("UPDATE", role_name=role_name, role_description=role_description, + role_guid=role_guid, is_api=is_api, jit_assignable=jit_assignable, + jit_assignable_default=jit_assignable_default, + permissions=permissions, child_roles=child_roles) + + def delete(self, role_guid: UUID): + return APIHelper()._rest_request("{}/{}".format(self.base_uri,role_guid),"DELETE") + + def _create_or_update(self, method, role_name, role_description, role_guid: UUID=None, is_api=False, + jit_assignable=True,jit_assignable_default=True, + permissions=[], child_roles=[]): + uri = self.base_uri + if method == 'CREATE': + httpmethod = 'POST' + elif method == 'UPDATE': + uri = uri + '/{}'.format(role_guid) + httpmethod = 'PUT' + else: + return + + role_def = { 'role_name': role_name, 'role_description': role_description, 'is_api': is_api, + 'jit_assignable': jit_assignable, 'jit_assignable_default': jit_assignable_default} + + if len(permissions) > 0: + role_def['permissions'] = permissions + + if len(child_roles) > 0: + role_def['child_roles'] = child_roles + + payload = json.dumps({"profile": role_def}) + return APIHelper()._rest_request(uri,httpmethod,body=payload) + +class Permissions(): + base_uri = "api/authn/v2/permissions" + def get_all(self): + return APIHelper()._rest_paged_request( self.base_uri,"GET","permissions",{'page':0}) + + def get(self, permission_guid: UUID): + return APIHelper()._rest_request("{}/{}".format(self.base_uri,permission_guid),"GET") \ No newline at end of file From a6cbc1965094ba4da93103601e02cd073e11abae Mon Sep 17 00:00:00 2001 From: Tim Jarrett Date: Thu, 26 Sep 2024 12:10:45 -0400 Subject: [PATCH 2/3] typo --- docs/roles.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/roles.md b/docs/roles.md index edf19ea..8f42e8c 100644 --- a/docs/roles.md +++ b/docs/roles.md @@ -11,7 +11,7 @@ The following methods call Veracode REST APIs and return JSON. - `jit_assignable_default`: Set to `True` to allow the role to be assigned by default during just-in-time provisioning. Defaults to `True`. - `permissions`: An array of permission names. Use `Permissions().get_all()` to see the list of assignable permissions. - `child_roles`: An array of role names. Adding a child role to a custom role gives the user all the permissions contained in the child role, in addition to any permissions defined in `permissinos`. You can add more than one child role. -- `Roled().update(role_name,role_description,role_guid,is_api (opt),jit_assignable(opt),jit_assignable_default (opt),permissions (opt),child_roles (opt))`: update the role identified by `role_guid` with the provided information. +- `Roles().update(role_name,role_description,role_guid,is_api (opt),jit_assignable(opt),jit_assignable_default (opt),permissions (opt),child_roles (opt))`: update the role identified by `role_guid` with the provided information. - `Roles().delete(role_guid)`: delete the role identified by `role_guid`. Note: You can only delete custom roles. - `Permissions().get_all()`: get the list of permissions that can be part of custom roles. - `Permissions().get(permission_guid)`: get the permission definition for a given permission. From 6343e9e33af32caa7a9dc2df00e102d42ca7afad Mon Sep 17 00:00:00 2001 From: Tim Jarrett Date: Fri, 27 Sep 2024 14:30:16 -0400 Subject: [PATCH 3/3] add jit defaults --- docs/docs.md | 3 ++- docs/jitdefaults.md | 19 +++++++++++++ veracode_api_py/identity.py | 53 ++++++++++++++++++++++++++++++++++++- 3 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 docs/jitdefaults.md diff --git a/docs/docs.md b/docs/docs.md index d6dfb2f..d9d7ab5 100644 --- a/docs/docs.md +++ b/docs/docs.md @@ -23,7 +23,8 @@ See the topics below for more information on how to use this library. * [Teams](teams.md) - create, update, access, and delete teams. * [Business Units](businessunits.md) - create, update, access, and delete business units. * [API Credentials](apicreds.md) - create, access, renew, and revoke API credentials. -* [Roles](roles.md) - access system roles and permissions; create, update, access, and delete custom roles. +* [Roles and Permissions](roles.md) - access system roles and permissions; create, update, access, and delete custom roles. +* [JIT Default Settings](jitdefaults.md) - create and update default Just-In-Time Provisioning settings. ## API Object diff --git a/docs/jitdefaults.md b/docs/jitdefaults.md new file mode 100644 index 0000000..24087c1 --- /dev/null +++ b/docs/jitdefaults.md @@ -0,0 +1,19 @@ +# Just In Time Provisioning Default Settings + +The following methods call Veracode REST APIs and return JSON. More information about the JIT settings is available in the [Veracode Docs](https://docs.veracode.com/r/Configure_SAML_Self_Registration). + +- `JITDefaultSettings().get()` - retrieve the current Just In Time (JIT) default settings. +- `JITDefaultSettings().create(ip_restricted(opt),prefer_veracode_data(opt), allowed_ip_addresses(opt), use_csv_for_roles_claim(opt), use_csv_for_teams_claim(opt), use_csv_for_teams_managed_claim(opt), use_csv_for_ip_address_claim(opt),teams(opt),roles(opt))` - create new Just In Time (JIT) default settings. Settings include: + - `ip_restricted`: set to `True` to apply IP restrictions (defined in `allowed_ip_addresses`) for a JIT user. + - `prefer_veracode_data`: set to `True` to allow an administrator to manage roles, teams, and other settings for users in the Veracode administrative console after user creation. If set to `False`, the SAML assertion sent from the customer's Identity Provider must contain these values. + - `allowed_ip_addresses`: an array of IP addresses. See the [Veracode Docs](https://docs.veracode.com/r/admin_ip) for more information. + - `use_csv_for_roles_claim`: set to `True` if your IDP will send a comma separated list of roles (instead of an array). + - `use_csv_for_teams_claim`: set to `True` if your IDP will send a comma separated list of teams (instead of an array). + - `use_csv_for_teams_managed_claim`: set to `True` if your IDP will send a comma separated list of teams managed by a team admin (instead of an array). + - `use_csv_for_ip_address_claim`: set to `True` if your IDP will send a comma separated list of IP address restrictions (instead of an array). + - `teams`: an array of team IDs (UUIDs) that should be assigned to a JIT user by default. + - `roles`: an array of role IDs (UUIDs) that should be assigned to a JIT user by default. +- `JITDefaultSettings().update(jit_default_id, ip_restricted(opt),prefer_veracode_data(opt), allowed_ip_addresses(opt), use_csv_for_roles_claim(opt), use_csv_for_teams_claim(opt), use_csv_for_teams_managed_claim(opt), use_csv_for_ip_address_claim(opt),teams(opt),roles(opt))` - update existing Just In Time (JIT) default settings identified by `jit_default_id`. +- `JITDefaultSettings().delete(jit_default_id)` - delete the Just In Time (JIT) default settings identified by `jit_default_id`. + +[All docs](docs.md) diff --git a/veracode_api_py/identity.py b/veracode_api_py/identity.py index fa387b3..2f9224e 100644 --- a/veracode_api_py/identity.py +++ b/veracode_api_py/identity.py @@ -307,4 +307,55 @@ def get_all(self): return APIHelper()._rest_paged_request( self.base_uri,"GET","permissions",{'page':0}) def get(self, permission_guid: UUID): - return APIHelper()._rest_request("{}/{}".format(self.base_uri,permission_guid),"GET") \ No newline at end of file + return APIHelper()._rest_request("{}/{}".format(self.base_uri,permission_guid),"GET") + +class JITDefaultSettings(): + base_uri = "api/authn/v2/jit_default_settings" + + def get(self): + return APIHelper()._rest_request( self.base_uri, "GET") + + def create(self, ip_restricted=False,prefer_veracode_data=True, allowed_ip_addresses=[], + use_csv_for_roles_claim=False, use_csv_for_teams_claim=False, use_csv_for_teams_managed_claim=False, + use_csv_for_ip_address_claim=True,teams=[],roles=[]): + return self._create_or_update("CREATE", ip_restricted=ip_restricted, prefer_veracode_data=prefer_veracode_data, + allowed_ip_addresses=allowed_ip_addresses, use_csv_for_roles_claim=use_csv_for_roles_claim, + use_csv_for_teams_claim=use_csv_for_teams_claim, + use_csv_for_teams_managed_claim=use_csv_for_teams_managed_claim, + use_csv_for_ip_address_claim=use_csv_for_ip_address_claim, teams=teams, roles=roles) + + def update(self, jit_default_id: UUID, ip_restricted=False,prefer_veracode_data=True, allowed_ip_addresses=[], + use_csv_for_roles_claim=False, use_csv_for_teams_claim=False, use_csv_for_teams_managed_claim=False, + use_csv_for_ip_address_claim=True,teams=[],roles=[]): + return self._create_or_update("UPDATE", jit_default_id = jit_default_id, ip_restricted=ip_restricted, + prefer_veracode_data=prefer_veracode_data,allowed_ip_addresses=allowed_ip_addresses, + use_csv_for_roles_claim=use_csv_for_roles_claim, + use_csv_for_teams_claim=use_csv_for_teams_claim, + use_csv_for_teams_managed_claim=use_csv_for_teams_managed_claim, + use_csv_for_ip_address_claim=use_csv_for_ip_address_claim, teams=teams, roles=roles) + + def _create_or_update(self, method, jit_default_id: UUID=None, ip_restricted=False,prefer_veracode_data=True, allowed_ip_addresses=[], + use_csv_for_roles_claim=False, use_csv_for_teams_claim=False, use_csv_for_teams_managed_claim=False, + use_csv_for_ip_address_claim=True,teams=[],roles=[]): + + if method == "CREATE": + uri = self.base_uri + httpmethod = "POST" + elif method == "UPDATE": + uri = '{}/{}'.format(self.base_uri, jit_default_id) + httpmethod = "PUT" + else: + return + + params = { 'ip_restricted': ip_restricted, 'prefer_veracode_data': prefer_veracode_data, 'allowed_ip_addresses': allowed_ip_addresses, + 'use_csv_for_roles_claim': use_csv_for_roles_claim, 'use_csv_for_teams_claim': use_csv_for_teams_claim, + 'use_csv_for_teams_managed_claim': use_csv_for_teams_managed_claim, 'use_csv_for_ip_address_claim': use_csv_for_ip_address_claim, + 'teams': teams, 'roles': roles} + + body = json.dumps(params) + + return APIHelper()._rest_request(url=uri, method=httpmethod, params=body) + + def delete(self, jit_default_id: UUID): + uri = '{}/{}'.format(self.base_uri, jit_default_id) + return APIHelper()._rest_request( uri, "DELETE") \ No newline at end of file