-
Notifications
You must be signed in to change notification settings - Fork 18
Create hostfactory token vanilla flow 12446 #339
Conversation
'10.0.11.1/32,10.0.20.0/24")') | ||
hostfactory_create_token.add_argument('-d', '--duration-days', metavar='VALUE', type=int, | ||
help='(Optional) the number of days the token will be valid.') | ||
hostfactory_create_token.add_argument('-H', '--duration-hours', metavar='VALUE', type=int, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please consult with Sapir on this
@@ -180,6 +181,12 @@ def get_many(self, *variable_ids) -> Optional[bytes]: | |||
""" | |||
return self._api.get_variables(*variable_ids) | |||
|
|||
def create_token(self, create_token_data: CreateTokenData) -> str: | |||
""" | |||
Create token/s for hosts with restrictions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we create only a single token, correct?
Create token/s for hosts with restrictions | |
Create token for hosts with restrictions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You pass an argument count and get the multiple tokens in response
@@ -13,7 +13,11 @@ | |||
from datetime import datetime, timedelta | |||
|
|||
# Internals | |||
from urllib import parse | |||
import requests |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should be under # Third Parties
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
very good work
conjur/api/api.py
Outdated
@@ -221,6 +225,28 @@ def get_variables(self, *variable_ids) -> dict: | |||
|
|||
return remapped_keys_dict | |||
|
|||
def create_token(self, create_token_data: CreateTokenData) -> requests.Response: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you return response here but in the client.py calling this function you say you are returning a str. is it correct?
conjur/api/api.py
Outdated
This method is used to create token/s for hosts with restrictions. | ||
""" | ||
if create_token_data is None: | ||
raise MissingRequiredParameterException('create_token_data') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For consistency I suggest show an error message for MissingRequiredParameterException
like in other places for example in hte api constructor "Account cannot be empty!"
conjur/api/client.py
Outdated
@@ -180,6 +181,12 @@ def get_many(self, *variable_ids) -> Optional[bytes]: | |||
""" | |||
return self._api.get_variables(*variable_ids) | |||
|
|||
def create_token(self, create_token_data: CreateTokenData) -> str: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
actually return response no?
Method that facilitates create token call to the logic | ||
""" | ||
if create_token_data is None: | ||
raise MissingRequiredParameterException('create_token_data') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For consistency maybe something like Token data is missing
? this is for the UI. Even though I have to say that your way is much easier to debug. @sigalsax maybe we should use this approach for all our MissingRequiredParameterException
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mbenita-Cyberark agreed. This error isn't enough to explain what happened. It will just print out 'create_token_data'. Please a more explanatory error of what happened at tag Shuli
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sigalsax his should not happen to user unless a developer did something wrong.
Used for organizing the params the user passed in to execute the CreateToken command | ||
""" | ||
|
||
def __init__(self, **arg_params): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why kwargs and not just simple parameters? is the input dynamically changes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should always be as explicit as possible and avoid kwargs
if days == 0 and hours == 0 and minutes == 0: | ||
return default_expiration | ||
|
||
return datetime.now() + timedelta( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is the datetime.now allign to the server? for our testing env working with aws n.virgia it's not. should we work with utc?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@eladkug I think this is a very shaky point that could cause future bugs due to server changes. can you suggest a test for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
both working with UTC so its o.k. (I verified with @eranha )
conjur/api/api.py
Outdated
params.update(self._default_params) | ||
|
||
token_params = {} | ||
for attr, value in create_token_data.__dict__.items(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we use repr function only here maybe we could set it to return only fields that have values?
conjur/logic/hostfactory_logic.py
Outdated
if create_token_data is None: | ||
raise MissingRequiredParameterException('create_token_data') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is being validate in three different places.. with only two possible paths (sdk going to the api, and cli going controller-> logic -> api) maybe we can skip this validation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the case now it can change I prefer not to make this assumption
conjur/logic/hostfactory_logic.py
Outdated
|
||
response = self.client.create_token(create_token_data) | ||
|
||
if response is not None and response.json() is not None and len(response.json()) > 0: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we are deserializing the content three times with response.json()
maybe
if response is not None and response.json() is not None and len(response.json()) > 0: | |
if response is not None : | |
data = response.json() | |
if data is not None and len(data) > 0 : | |
return json.dumps(data, indent=4, sort_keys=True) |
conjur/wrapper/http_wrapper.py
Outdated
# pylint: disable=too-many-locals,consider-using-f-string | ||
# ssl_verify can accept Boolean or String as per requests docs | ||
# https://requests.readthedocs.io/en/master/api/#main-interface | ||
def invoke_endpoint(http_verb: HttpVerb, endpoint: ConjurEndpoint, params: dict, *args, | ||
check_errors: bool = True, ssl_verify: bool = True, | ||
auth: tuple = None, api_token: str = None, | ||
query: dict = None) -> requests.Response: | ||
query: dict = None, headers: dict = {}) -> requests.Response: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is very dangerous and possibly the source of all evil headers: dict = {}
pycharm should warn you about this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
great point @mbenita-Cyberark
hostfactory_create_token.add_argument('--cidr', metavar='VALUE', | ||
help='(Optional) the CIDR address that contains all IPs that can ' | ||
'use this token to create hosts. Yoo can specify multiple cidr, ' | ||
'separated by commas (for example --cidr "10.0.10.0/24,' | ||
'10.0.11.1/32,10.0.20.0/24")') | ||
hostfactory_create_token.add_argument('-d', '--duration-days', metavar='VALUE', type=int, | ||
help='(Optional) the number of days the token will be valid.') | ||
hostfactory_create_token.add_argument('-H', '--duration-hours', metavar='VALUE', type=int, | ||
help='(Optional) the number of hours the token will be valid.') | ||
hostfactory_create_token.add_argument('-m', '--duration-minutes', metavar='VALUE', type=int, | ||
help='(Optional) the number of minutes the token will be valid.') | ||
hostfactory_create_token.add_argument('-c', '--count', metavar='VALUE', type=int, | ||
help='(Optional) the number of times the token can be used.') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isn't this PR for vanilla flow only?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean no args?
conjur/cli.py
Outdated
@classmethod | ||
def handle_hostfactory_logic(cls, args:list=None, client=None): | ||
""" | ||
Method that wraps the hostfdactory call logic |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Method that wraps the hostfdactory call logic | |
Method that wraps the hostfactory call logic |
parse.urlencode(token_params), | ||
api_token=self.api_token, | ||
ssl_verify=self._ssl_verify, | ||
headers={'Content-Type': 'application/x-www-form-urlencoded'}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we return a JSON or a string instead of the response itself? Would be clearer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The documentation of the logic class
`HostFactoryLogic
This class holds the business logic for executing and manipulating
returned data`
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i dont follow
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sigalsax The logic responsible to manipulate the response
|
||
hostfactory_create_subcommand_parser = menu \ | ||
.add_parser(name="token", | ||
help=' Create token/s for hosts with restrictions', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
help=' Create token/s for hosts with restrictions', | |
help='Create token/s for hosts with restrictions', |
# Options | ||
hostfactory_create_token = hostfactory_create_subcommand_parser.add_argument_group( | ||
title=title_formatter("Options")) | ||
hostfactory_create_token.add_argument('-action_type', default='create_token', help=argparse.SUPPRESS) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It helps diff create from revoke they both end in cli.handle_hostfactory_logic
if args.action_type == 'create_token': | ||
hostfactory_logic = HostFactoryLogic(client) | ||
create_token_data = CreateTokenData(hostfactoryid=args.hostfactoryid, | ||
cidr=args.cidr, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i would make attributes in CreateTokenData more readible maybe id, cidr, days, hours, minutes, count
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
omit duration? is token days more clear than duration days?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the attribute names are just very long and we should make them dev-friendly :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sigalsax I agree
conjur/cli.py
Outdated
@@ -290,6 +310,9 @@ def run_action(resource:str, args): | |||
result = client.whoami() | |||
print(json.dumps(result, indent=4)) | |||
|
|||
elif resource == 'hostfactory': |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks better if we put it under 'host' logic. it becomes easier to spot
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't follow
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
switch the order of the elif
s. Move this block under the host elif
block for readability
from conjur.errors import MissingRequiredParameterException | ||
|
||
""" | ||
VariableController module |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
VariableController module | |
HostfactoryController module |
self.count = arg_params['count'] if arg_params['count'] else 1 | ||
|
||
self.expiration = self.get_expiration( | ||
arg_params['duration_days'] if arg_params['duration_days'] else 0, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please get Sapir's approval on this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
on count?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
on the default
conjur/logic/hostfactory_logic.py
Outdated
# -*- coding: utf-8 -*- | ||
|
||
""" | ||
VariableLogic module |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
VariableLogic module | |
HostfactoryLogic module |
""" | ||
|
||
# Internals | ||
import json |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
json isn't an Internals
it is Builtins
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work! @eranha
Left a few comments and please see the failing build. We are failing on the code linter suggestions so please address them :)
hostfactory_create_token = hostfactory_create_subcommand_parser.add_argument_group( | ||
title=title_formatter("Options")) | ||
hostfactory_create_token.add_argument('-action_type', default='create_token', help=argparse.SUPPRESS) | ||
hostfactory_create_token.add_argument('-i', '--hostfactoryid', metavar='VALUE', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@eranha i think this sould be --hostfactory-id
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sigalsax see my comment below
description=command_description(hostfactory_name, | ||
hostfactory_usage), | ||
epilog=command_epilog( | ||
'conjur hostfactory create token --hostfactoryid my_factory --cidr 10.10.1.2/31 ' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
think it should be --hostfactory-id
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sigalsax this is how it appears in the sign-off doc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
consult Sapir with this because the rest of the flags have -
(duration-days, etc)
This method is used to create token/s for hosts with restrictions. | ||
""" | ||
if create_token_data is None: | ||
raise MissingRequiredParameterException('create_token_data cannot be empty!') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@eranha is this a user facing log? If so, this doesn't really explain anything
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sigalsax I consider it as an argument exception (runtime so to speak) it is intended for the developer
description=command_description(hostfactory_name, | ||
hostfactory_usage), | ||
epilog=command_epilog( | ||
'conjur hostfactory create token --hostfactoryid my_factory ' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not a blocker but this should be verified by Sapir (if it needs a -
)
'10.0.11.1/32,10.0.20.0/24")') | ||
create_token.add_argument('-d', '--duration-days', metavar='VALUE', type=int, | ||
help='(Optional) the number of days the token will be valid.') | ||
create_token.add_argument('-H', '--duration-hours', metavar='VALUE', type=int, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
was anything decided here regarding the -H?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sigalsax not that I know of
@staticmethod | ||
def get_expiration(duration: timedelta) -> datetime: | ||
""" | ||
Returns the token expiration in UTC; One hour of not specified. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Returns the token expiration in UTC; One hour of not specified. | |
Returns the token expiration in UTC; One hour if not specified. |
doseq=True)) | ||
if response is not None: | ||
data = response.json() | ||
if data is not None and len(data) > 0: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we need a compound check?
if Data is not none then its length automatically will be greater > 0 no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not necessarily
Basic skeleton was created to allow easy entry into task (Addition was made to help screen, appropriate classes were added)
Basic skeleton was created to allow easy entry into task (Addition was made to help screen, appropriate classes were added)
Basic skeleton was created to allow easy entry into task (Addition was made to help screen, appropriate classes were added)
Basic skeleton was created to allow easy entry into task (Addition was made to help screen, appropriate classes were added)
Basic skeleton was created to allow easy entry into task (Addition was made to help screen, appropriate classes were added)
Basic skeleton was created to allow easy entry into task (Addition was made to help screen, appropriate classes were added)
Basic skeleton was created to allow easy entry into task (Addition was made to help screen, appropriate classes were added)
Basic skeleton was created to allow easy entry into task (Addition was made to help screen, appropriate classes were added)
Basic skeleton was created to allow easy entry into task (Addition was made to help screen, appropriate classes were added)
Basic skeleton was created to allow easy entry into task (Addition was made to help screen, appropriate classes were added)
e8d48d2
to
12c4563
Compare
Basic skeleton was created to allow easy entry into task (Addition was made to help screen, appropriate classes were added)
Basic skeleton was created to allow easy entry into task (Addition was made to help screen, appropriate classes were added)
conjur/cli.py
Outdated
days = args.duration_days if args.duration_days else 0 | ||
hours = args.duration_hours if args.duration_hours else 0 | ||
minutes = args.duration_minutes if args.duration_minutes else 0 | ||
duration = timedelta(days=days, hours=hours, minutes=minutes) | ||
default_duration = timedelta(hours=1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thought to put this in the controller actually @eranha
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since this is the view
Basic skeleton was created to allow easy entry into task (Addition was made to help screen, appropriate classes were added)
Basic skeleton was created to allow easy entry into task (Addition was made to help screen, appropriate classes were added)
Basic skeleton was created to allow easy entry into task (Addition was made to help screen, appropriate classes were added)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work!
What does this PR do?
Create token - vanilla flow
Added menu parser controller logic and data classes
Basic skeleton was created to allow easy entry into task (Addition was made to help screen, appropriate classes were added)
A token can be created after adding a hostfactory policy
What ticket does this PR close?
12446
Checklists
Change log
Test coverage
Documentation
README
s) were updated in this PR, and/or there is a follow-on issue to update docs, or