i need help on how to disable HTTP methods on my cowboy server.
Tried to search on the internet but eneded up with no solutions
The documentation for method gives this example:
init(Req, State) ->
case lists:member(cowboy_req:method(Req), [<<"GET">>, <<"POST">>]) of
true -> handle(Req, State);
false -> method_not_allowed(Req, State)
end.
You can easily adapt that to be a blacklist instead of a whitelist. For example, to ban OPTIONS and TRACE, you'd do this:
init(Req, State) ->
case lists:member(cowboy_req:method(Req), [<<"OPTIONS">>, <<"TRACE">>]) of
false -> handle(Req, State);
true -> method_not_allowed(Req, State)
end.
Related
I'm currently making a Scotty API and I couldn't find any examples of basicAuth implementations (Wai Middleware HttpAuth).
Specifically, I want to add basic auth headers (user, pass) to SOME of my endpoints (namely, ones that start with "admin"). I have everything set up, but I can't seem to make the differentiation as to which endpoints require auth and which ones don't. I know I need to use something like this, but it uses Yesod, and I wasn't able to translate it to Scotty.
So far, I have this:
routes :: (App r m) => ScottyT LText m ()
routes = do
-- middlewares
middleware $ cors $ const $ Just simpleCorsResourcePolicy
{ corsRequestHeaders = ["Authorization", "Content-Type"]
, corsMethods = "PUT":"DELETE":simpleMethods
}
middleware $ basicAuth
(\u p -> return $ u == "username" && p == "password")
"My Realm"
-- errors
defaultHandler $ \str -> do
status status500
json str
-- feature routes
ItemController.routes
ItemController.adminRoutes
-- health
get "/api/health" $
json True
But it adds authentication to all my requests. I only need it in some of them.
Thank you so much!
You can use the authIsProtected field of the AuthSettings to define a function Request -> IO Bool that determines if a particular (Wai) Request is subject to authorization by basic authentication. In particular, you can inspect the URL path components and make a determination that way.
Unfortunately, this means that the check for authorization is completely separated from the Scotty routing. This works fine in your case but can make fine-grained control of authorization by Scotty route difficult.
Anyway, the AuthSettings are the overloaded "My Realm" string in your source, and according to the documentation, the recommended way of defining the settings is to use the overloaded string to write something like:
authSettings :: AuthSettings
authSettings = "My Realm" { authIsProtected = needsAuth }
That looks pretty horrible, but anyway, the needsAuth function will have signature:
needsAuth :: Request -> IO Bool
so it can inspect the Wai Request and render a decision in IO on whether or not the page needs basic authentication first. Calling pathInfo on the Request gives you a list of path components (no hostname and no query parameters). So, for your needs, the following should work:
needsAuth req = return $ case pathInfo req of
"admin":_ -> True -- all admin pages need authentication
_ -> False -- everything else is public
Note that these are the parsed non-query path components, so /admin and /admin/ and /admin/whatever and even /admin/?q=hello are protected, but obviously /administrator/... is not.
A full example:
{-# LANGUAGE OverloadedStrings #-}
import Web.Scotty
import Network.Wai.Middleware.HttpAuth
import Data.Text () -- needed for "admin" overloaded string in case
import Network.Wai (Request, pathInfo)
authSettings :: AuthSettings
authSettings = "My Realm" { authIsProtected = needsAuth }
needsAuth :: Request -> IO Bool
needsAuth req = return $ case pathInfo req of
"admin":_ -> True -- all admin pages need authentication
_ -> False -- everything else is public
main = scotty 3000 $ do
middleware $ basicAuth (\u p -> return $ u == "username" && p == "password") authSettings
get "/admin/deletedb" $ do
html "<h1>Password database erased!</h1>"
get "/" $ do
html "<h1>Homepage</h1><p>Please don't <a href=/admin/deletedb>Delete the passwords</a>"
I added a new module (-behavior(gen_mod)) inside ejabberd and I was able to open a ssl connection to a server and send a message directly using the ssl connection(ssl:send() function). However, I am unable to receive response after this.
I tried using the receive option in erlang to catch messages, and still no luck.
I tried changing the module to be a stateful module by using the gen_server behavior and I still don't observe any handle_info calls with any message.
I could not get any response using ssl:recv/2 either.
Am I missing something? How do I asynchronously receive the response from the ssl socket in erlang?
Any pointers would be really appreciated. Thank you!
code: Adding only relevant parts of the code.
sendPacketToServer() ->
case ssl:connect(Gateway, Port, Options, ?SSL_TIMEOUT) of
{ok, Socket} ->
ssl:controlling_process(Socket, self()),
Packet = .....,
Result = ssl:send(Socket, Packet),
receiveMessage(),
ssl:close(Socket),
?INFO_MSG("~n~n~n~n Successfully sent payload to the server, result: ~p for the user: ~p", [Result, Username]);
{error, Reason} = Err ->
?ERROR_MSG("Unable to connect to the server: ~s for the user: ~p", [ssl:format_error(Reason), Username]),
Err
end
...
....
receiveMessage() ->
receive ->
{ssl, Msg, Data} -> % incoming msg from SSL, send it to process
......
{ssl_closed, Msg} -> % incoming msg from SSL, send it to process
.....
{ssl_error, Msg} -> % incoming msg from SSL, send it to process
.....
{ssl_passive, Msg} -> % incoming msg from SSL, send it to process
....
end.
Added the following code for gen_server: (when doing the following, i do not close the socket immediately, but it still does not work).
start(Host, Opts) ->
gen_mod:start_child(?MODULE, Host, Opts).
stop(Host) ->
gen_mod:stop_child(?MODULE, Host).
init([ServerHost|_]) ->
Opts = gen_mod:get_module_opts(ServerHost, ?MODULE),
start(ServerHost, Opts),
{ok, #state{host = ServerHost}}.
handle_call(Request, From, State) ->
?WARNING_MSG("Unexpected call from ~p: ~p", [From, Request]),
{noreply, State}.
handle_cast(Msg, State) ->
?WARNING_MSG("Unexpected cast: ~p", [Msg]),
{noreply, State}.
handle_info(Info, State) ->
?WARNING_MSG("Unexpected info: ~p", [Info]),
{noreply, State}.
terminate(_Reason, State) ->
ok.
code_change(_OldVsn, State, _Extra) -> {ok, State}.
I was able to overcome the problem by using a gen_server module, since it has explicit callbacks for any messages.
I'm writing meteor tests that require authentication, and having a series of problems.
This is my code:
MochaWeb?.testOnly ->
describe "Login", ->
describe "security", ->
it 'should take you to /login/ if you are not logged in', ->
Meteor.flush()
chai.assert.equal Router.current().url, '/login/'
it 'should allow logins and then take us to /', ->
Meteor.flush()
Accounts.createUser username: 'test', password: 'test'
Meteor.loginWithPassword 'test', 'test', (err) ->
console.log err
chai.expect(err).to.be undefined
chai.assert.equal Router.current().url, '/'
My tests pass, even though I get console messages such as Exception in delivering result of invoking 'login': TypeError: object is not a function
My console.log call gives me
{error: 403, reason: "User not found", details: undefined, message: "User not found [403]", errorType: "Meteor.Error"…}
on the console, and nothing on the velocity log window
My user doesn't authenticate as I'd expect. One cause could be that my app doesn't have the accounts-password package, because I don't want it (I just want google apps users to be able to login). However I want an easy way to handle authentication in meteor tests, as most of my tests involve authenticated users.
I'm not sure whether the assert equal would work or I'd have to set some sort of timeout to wait for the redirection. In this case it wouldn't be a problem, but do I have to nest every test method I have inside loginWithPassword? I'd find this a bit uncomfortable.
Any help and suggestions much appreciated!
Best,
Are you sure you have user already created? I think you should create it in your fixture.js.
MochaWeb.testOnly(function() {
describe("Client", function() {
describe("firstTest", function() {
// Meteor.users.remove({});
before(function(done) {
Accounts.createUser(currentUser, function(err, success) {
Meteor.loginWithPassword(currentUser, function(err) {
console.log("This works");
// done();
});
});
});
});
});
});
Here is the source file, in which I have implemented the same
https://github.com/trinisofttechnologies/mocha-test/blob/master/tests/mocha/client/client.coffee
and this is the repo where the code resides
https://github.com/trinisofttechnologies/mocha-test
I have a module App which will check if the user has sign in.
App.run ['$rootScope', 'UserService', ($rootScope, UserService) ->
UserService.current_user()
]
The UserService.current_user() will trigger a $http request.
So how can i write the $httpBackend to mockup the request? I have tried some method:
describe 'App', ->
$httpBackend = null
beforeEach(module('App')) # one
beforeEach inject ($injector) -> # two
$httpBackend = $injector.get('$httpBackend')
$httpBackend
.when('GET', '/api/1/users/current_user')
.respond(403, {"error":"not signin"})
it "should get current_user request", () ->
$httpBackend.expectGET('/api/1/users/current_user').respond(403, {'error': 'not signin'})
This will show the error:
Error: Unexpected request: GET /api/1/users/current_user No more request expected
If I change the sequence of # one and # two. It will show error
Error: Injector already created, can not register a module!
This makes me fall in depression. I need some help.
I made it working with this :
describe 'App', ->
beforeEach angular.mock.module('App')
it "should get current_user request", inject ($httpBackend) ->
$httpBackend.expectGET('/api/1/users/current_user').respond(403, {'error': 'not signin'})
$httpBackend.flush()
And it works also if you put the backend expectation in a before each :
describe 'App', ->
beforeEach angular.mock.module('App')
beforeEach inject ($httpBackend) ->
$httpBackend.expectGET('/api/1/users/current_user').respond(403, {'error': 'not signin'})
$httpBackend.flush()
it 'should test someting else', ->
See it in action in this Plunker.
I have an object route in the router (using ember-data with standard REST backend) with connectOutlets that simply deserializes and loads the object and plugs it into the outlet.
# inside router
action: Ember.Route.extend
route: 'object/:object_id'
connectOutlets: (router, object) ->
unless object.get('isLoaded') # What goes here to tell if the object wasn't found?
#
# handle this case (e.g., redirect)
#
else # otherwise proceed as normal
router.get('applicationController').connectOutlet('object', object)
When I navigate to localhost/#object/object_that_doesnt_exist, the router deserializes the url, attempts to load the object (server logs show a HTTP GET request for localhost/objects/object_that_doesnt_exist), gets a 404, and instead creates a new object with id set to object_that_doesnt_exist.
I want to detect this and handle the case. Right now, I am checking the isLoaded property, which does differentiate between existing models and nonexisting models, but I'm not sure this is the best way.
Ideally, there would be a method similar to Rails' new_record?.
Have a look at the source code: https://github.com/emberjs/data/blob/master/packages/ember-data/lib/system/model/model.js#L15
isError: retrieveFromCurrentState,
isNew: retrieveFromCurrentState,
isValid: retrieveFromCurrentState,
Haven't tried myself but isNew might be what you are looking for.
You don't want to do this in connectOutlet because it will require the application to wait while it checks the DB for the record.
Personally I would use a custom find method in my adapter and handle the 404 error from there.
find: function(store, type, id) {
var root = this.rootForType(type);
this.ajax(this.buildURL(root, id), "GET", {
success: function(json) {
this.didFindRecord(store, type, json, id);
},
statusCode: {
404: function() {
# I can never remember the exact semantics, but I think it's something like this
this.trigger('didNotFindRecord');
}
}
})
}
connectOutlets: (router, object) ->
router.get('store').addObserver('didNotFindRecord', this, 'handle404')
router.get('applicationController').connectOutlet('object', object)
handle404: ->
#
# handle this case (e.g., redirect)
#
You will have to be careful to tear down the observers correctly though.