Some small tweaks and "fixes" to halauth#115
Conversation
|
Ah coveralls, you bone in my brisket. Nevermind, fixed it probably. |
| def grantPermission(self, ri, identity, perm): | ||
| if not self.enabled: | ||
| return | ||
| return False |
There was a problem hiding this comment.
We should be raising exceptions instead of the return successful paradigm.
There was a problem hiding this comment.
Oh, yeah I suppose this would be the case for exceptions here, wouldn't it. Is there an existing exception class I should use here, or should I start inventing my own?
There was a problem hiding this comment.
I'm having second thoughts on this, but for a different reason. Should we allow permissions to be edited when they are not being enforced? Without, there may be a chicken-and-egg problem where you enable permission enforcement, but no one is permitted to modify the permissions table.
Proposal: split enabled and enforced. Setting enabled = True simply enables access to the permissions table internally. Setting enforced = True implies enabled = True, and is checked in hasPermission calls.
Therefore, calls to grantPermission when not enabled should be an Exception because you cannot grant a permission when auth is not enabled at all. True or False still apply for if the permission was added, or already existed.
There was a problem hiding this comment.
Note, enabled = True and enforced = False is like selinux "permissive" mode. We should still log failed auth checks. Side note, thinking about adding a "seperate" auth log that logs checks, rejections, etc. May not actually be a separate physical log, but something easy to grep for.
| def wrapper(self, *args, **kwargs): | ||
| msg = None | ||
| for i in list(args) + list(kwargs.values()): | ||
| if i.__class__ == Message: |
There was a problem hiding this comment.
Not very pythonic, what about subclasses of Message?
There was a problem hiding this comment.
Also, what should we do in the cases with multiple message arguments? I don't think just taking the first one that appears is the most transparent behavior. Maybe an argument to the decorator to specify which to use?
There was a problem hiding this comment.
Agh, that should be a subclass of Message check not a equality check. I blame hacktoberfest-rush @richteer on that laziness.
As for multiple message arguments: The previous convention stated that the message argument should be first, and I agree that should be the case now as well. That said, that means this wrapper doesn't get any easier to use if you still have to uphold the convention, so convenience is lost I suppose.
Alternative suggestion: Have two different wrappers:
- One that requires a
Messageobject as the first parameter - One that requires exactly one
Messageobject in*args
...now that I write that, it feels like it just adds needless complexity.
There was a problem hiding this comment.
Maybe for simplicity we just have one wrapper with the general use case, and when people need to do fancy things, like pass multiple messages around, they just have to call hasPermission themself?
There was a problem hiding this comment.
Probably the better choice. I'm leaning towards requiring the message to be in **kwargs, as msg, since that is what CommandModule uses as convention.
There was a problem hiding this comment.
I'm fine with that. More clear than first positional.
|
Update: Instead of crawling New decorator args are |
| if i.__class__ == Message: | ||
| msg = i | ||
| break | ||
| if argnum >= 0 and argnum < len(args): |
There was a problem hiding this comment.
I think you should check for None here. A negative argnum makes sense, for example if you want to refer to the last position argument with -1. Also, None is more semantically significant of a value to not be used.
There was a problem hiding this comment.
...yep, that makes a lot more sense. I've been writing too much C lately. Default argnum should be None, and I will explicitly check against None to see if it has been set.
| msg = kwargs.get(key) | ||
|
|
||
| if not msg: | ||
| self.log.error("Probable module bug! -- hasPermission decorator called on a function that doesn't have a Message argument!") |
There was a problem hiding this comment.
Not necessarily true: it could have successfully retrieved a message from the kwargs that was set to a false value. Either change the log or make the check if not key in kwargs.
There was a problem hiding this comment.
What about if not (hasattr(msg, "origin") and hasattr(msg, "identity")) ? That's all we are really looking to extract from the message object anyway, less chance for false positives.
There was a problem hiding this comment.
What about just catching AttributeErrors for those when we try to use them?
There was a problem hiding this comment.
I guess that surfaces less to the module. Erg. Yeah, we should probably just check for those attrs.
There was a problem hiding this comment.
Actually, I just added a MalformedMsgException in #131 , maybe we should utilize that here too? If attr check fails raise that complaining about the bad attr.
Thinking I might add that definition to base halibot, and expand it to properly complain about the specific problem attrs.
There was a problem hiding this comment.
Seems reasonable. Should we throw that where we use those attrs then? It would keep that which enforces constraints and the reason for them together.
There was a problem hiding this comment.
We probably should, but not as a .__getattr__() action. There's always a possibility that we are handed a non-Message object, so we should probably check those attrs where it is critical to do so. That is to say, .raw_send() should, and auth checks should.
There was a problem hiding this comment.
Something I just thought of here, should we handle the case of key not in kwargs.keys() separately from kwargs[key] = None/Badobject?
|
I like this approach much better though. |
|
Alright, complete U-turn on this. I had some fun rebasing this to a more recent branch, since all the regex stuff for RI matching was merged before this. I nuked the "return True/False" stuff entirely. Since we should probably be returning exceptions anyway, figured it'd probably be best to completely change that behavior anyway. So, what is in this branch now? Really only the decorator change as discussed in the threads above. That was largely the only chunk worth salvaging for now. I archived the old branch to Minor update: squashed patches down into one, since there's really only one rework now. |
| if argnum != None and argnum < len(args): | ||
| msg = args[argnum] | ||
| else: | ||
| msg = kwargs.get(key) |
There was a problem hiding this comment.
Maybe this should be kwargs[key]? If they provide the wrong key, it is probably better to point that out directly than to get a MalformedMsgException.
There was a problem hiding this comment.
Yeah, I think that resolves my question in the other thread. We should propagate the KeyError upwards.
|
Update: changed |
The `@hasPermission` decorator was intended to be simple to use for module developers. As it turns out however, it is completely incompatible with modules that subclass CommandModule. Considering this is also a class intended to make life easier, something had to be fixed. Thus, the decorator now supports two optional parameters when applying to a function: "argnum" and "key". Since we need to extract the source origin and identity at runtime, but different modules may have different calling conventions, this was the easiest and most flexible method to instruct the decorator where to look for its information. argnum can be supplied to specify which positional argument contains a Message object, indexing by 0. Remember, class methods do not count self in *args!. The default value is None, so unless supplied, this parameter is ignored. key can be supplied to specify a key for retrieveing the Message object from **kwargs. The default value is "msg", which is the default convention for classes that inherit from CommandModule. If argnum is specified (i.e. not None), it takes priority over key. Example: class MyModule(HalModule): # Uses first non-self positional argument @hasPermission("FOO", argnum=0) def myfunc(self, msg): pass # key= is not necessary here, since "msg" is the default # make sure msg= is supplied! @hasPermission("BAR") def mybar(self, msg=None): pass
Pretty straightforward PR, nothing invasive this time.1898759 returns a boolean based on if.grantor.revokeactually did something to the internal permissions list -- largely as an indicator to the caller if they should bother calling.writefe3cfc2 changes the behavior of the@hasPermissiondecorator slightly, to no longer require theMessageobject it parsesriandidentityfrom as a positional argument. Best to look at the code/commit message for more info.Haha jk wasn't straightforward at all. See comments for what happened.