-
Notifications
You must be signed in to change notification settings - Fork 28
add configurable blacklist for all URL endpoints #47
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,18 +30,21 @@ provider(App, Module) -> | |
| App, chttpd_handlers, Module). | ||
|
|
||
| url_handler(HandlerKey, DefaultFun) -> | ||
| ok = ensure_enabled(HandlerKey), | ||
| case collect(url_handler, [HandlerKey]) of | ||
| [HandlerFun] -> HandlerFun; | ||
| [] -> DefaultFun | ||
| end. | ||
|
|
||
| db_handler(HandlerKey, DefaultFun) -> | ||
| ok = ensure_enabled(HandlerKey), | ||
| case collect(db_handler, [HandlerKey]) of | ||
| [HandlerFun] -> HandlerFun; | ||
| [] -> DefaultFun | ||
| end. | ||
|
|
||
| design_handler(HandlerKey, DefaultFun) -> | ||
| ok = ensure_enabled(HandlerKey), | ||
| case collect(design_handler, [HandlerKey]) of | ||
| [HandlerFun] -> HandlerFun; | ||
| [] -> DefaultFun | ||
|
|
@@ -51,6 +54,20 @@ design_handler(HandlerKey, DefaultFun) -> | |
| %% Internal Function Definitions | ||
| %% ------------------------------------------------------------------ | ||
|
|
||
| is_disabled(KeyBin) -> | ||
| BlacklistConfig = config:get("chttpd", "url_handler_blacklist", []), | ||
| {ok, Blacklist} = couch_util:parse_term(BlacklistConfig), | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You'll get badmatch with the default value here: |
||
| Key = binary_to_list(KeyBin), | ||
| lists:member(Key, Blacklist). | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. all a bit expensive per request, how about
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Like it. Thought, may be "disable.foo = true" or something else in imperative mood.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alternatively, we could cache the parsed list representation somewhere any only access it here. Or is the
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A (very) dirty version of this, just to illustrate the idea: get_blacklist() ->
case config:get("chttpd", "_url_handler_blacklist", []) of
[] -> % no cached version
case config:get("chttpd", "url_handler_blacklist", []) of
[] -> []; % no config value
BlacklistConfig -> % we have a config value, let’s parse it once
{ok, Blacklist} = couch_util:parse_term(BlacklistConfig),
config:set("chttpd", "_url_handler_blacklist", Blacklist),
Blacklist
end;
Blacklist1 -> Blacklist1 % we already have the parsed list cached
end.
is_disabled(KeyBin) ->
Blacklist = get_blacklist(),
Key = ?b2l(KeyBin),
lists:member(Key, Blacklist).Of course, we’d need a thing that invalidates the “cache”. I’m abusing the config system here as a cache, we obviously don’t want to do that for real. I’m not too familiar with chttpd yet, would there be an obvious location for this?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mochiglobal would be a better spot for a cache, but I think we should avoid additional caching.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with avoid. Cache invalidation is one of the hardest CS problems (: |
||
|
|
||
| ensure_enabled(Key) -> | ||
| case is_disabled(Key) of | ||
| true -> | ||
| throw("The URL endpoint " ++ Key ++ "has been disabled."); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please select different value for error so it matches one of the clauses here:
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was aiming for; error_info({not_found, Reason}) -> |
||
| false -> | ||
| ok | ||
| end. | ||
|
|
||
| collect(Func, Args) -> | ||
| Results = do_apply(Func, Args, [ignore_providers]), | ||
| [HandlerFun || HandlerFun <- Results, HandlerFun /= no_match]. | ||
|
|
||
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.
How about dedicating whole section in config for this. Then you would be able to choose error message
Something like:
just an idea. Fill free to ignore.
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.
Good idea. But how to avoid collisions when same-named resource is defined on different levels like /_foo and /db/_foo? We don't have such cases, but theoretically this is a limitation.
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 involve your idea, then we'll end with chttpd_blacklist_global_handlers, chttpd_blacklist_database_handlers, chttpd_blacklist_design_handlers ala httpd defines them now. Not bad decision after all.
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.
Hm, I think that's getting complicated. I think we just send 404 for all these things.
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.
Good side of @iilyak idea as it also allows to make previously existed resources gone gracefully. It has a difference from regular 404. Not implemented also may have a point for basement of missed features. But that's all about details.
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.
@kxepal I get the benefits there, but I think giving end-users control over what to return per resource is taking it too far. We can do this smarter :)
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.
@janl If end user decided to black list something, they may (or need in this proposal) define a reason why, right?
We can do this smarter, but smarter solutions takes more time/resources while that's stupid one just works and quite trivial in implementation. We can always improve it later. Especially, after collecting user experience for this blacklist management - may be they wouldn't touch it a lot (actually, they indeed will not) and that decision is good enough.
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.
@kxepal Well, configuration is part of what the end-user sees, so we can’t just fix it later. We need to consider an upgrade plan and everything. I’d like to avoid doing that and I don’t mind spending another day of nailing this one shut so we can forget about it.
But you make a good point, I’ll keep thinking 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.
I played a bit with your proposal. One more point against lists: it's hard to operate with them via API.
You cannot simple remove one endpoint from blacklist: you need to GET
/_config/chttpd/httpd_global_handler_blacklist, decode, pop element, encode, PUT it back. Same story if you need to add something to blacklist. Compare it with a single PUT/DELETE/_config/chttpd_global_handlers_blacklist/_uuidsrequest, which both are atomic.Simplicity of using that blacklist via API also matters, imho.
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 like this. Thanks, I’ll wrap things around.