I am using the embedded YAWS web-server with yaws cookie session.
I first authenticate the user with user-name & password to allow him the entry to the web pages.
My problem is if the user directly opens the internal web page instead of login page he can view it even without the authentication. How to restrict the user that he must have the cookie to view any internal web page.
In chapter 7 of the Yaws PDF documentation there's an example that does exactly what you're asking about. It uses arg rewriting to redirect unauthenticated requests to a login page.
First we configure an arg rewriter module named myapp in the server portion of yaws.conf:
arg_rewrite_mod = myapp
The myapp:arg_rewrite/1 function checks the incoming request via the #arg{} record to look for a specific cookie, and if not found and the request isn't trying to retrieve one of the three resources returned from the login_pages/0 function, it calls do_rewrite/1 to rewrite the request to deliver a login.yaws page instead:
arg_rewrite(Arg) ->
OurCookieName = "myapp_sid"
case check_cookie(Arg, OurCookieName) of
{error, _} ->
do_rewrite(Arg);
{ok, _Session} ->
%% return Arg untouched
Arg
end.
%% these pages must be shippable without a good cookie
login_pages() ->
["/banner.gif", "/login.yaws", "/post_login.yaws"].
do_rewrite(Arg) ->
Req = Arg#arg.req,
{abs_path, Path} = Req#http_request.path,
case lists:member(Path, login_pages()) of
true ->
Arg;
false ->
Arg#arg{req = Req#http_request{path = {abs_path, "/login.yaws"}},
state = Path}
end.
Please see the Yaws PDF documentation for further details.
Related
I do not know the difference between these two end points:
a) /api/sn_chg_rest/v1/change/emergency
b) /api/now/table/change_request?sys_id=-1&sysparm_query=type=emergency
b) once submitted changes to "normal" response type
Issue: Unable to submit a request of type Emergency, Standard, OR Expedited.
Things I have Tried: url = 'https://xxxx.service-now.com/api/now/table/change_request?sys_id=-1&sysparm_query=type=expedited <<changes to normal, the site only allows edits into emergency or normal once submitted with this link>>
url = 'https://xxxx.service-now.com/api/sn_chg_rest/v1/change/emergency <<This one seems to be working only for emergency & normal, also the user is locked into emergency and normal even when logged in to edit type manually once submitted via script >>
Outcome of the current code below in conjuction with the "Things I have Tried" There is a CHG#XXX created but no matter what the Key:xxxxxx "sys-pram_query=type=xxxxxx" changes to (i.e. "Normal", "Expedited", "Emergency", "Standard") looks like this ---> ("sys-pram_query=type= Emergency","sys-pram_query=type= Expedited","sys-pram_query=type= Standard") the type on the ServiceNow-site defaults to "Normal" once the code below runs creating the request using the POST Method.
#Need to install requests package for python
#easy_install requests
import requests
# Set the request parameters
url = 'https://xxxx.service-now.com/api/now/table/change_request?sysparm_fields=type'
# Eg. User name="admin", Password="admin" for this code sample.
user = 'admin'
pwd = 'admin'
# Set proper headers
headers = {"Content-Type":"application/json","Accept":"application/json"}
# Do the HTTP request
response = requests.post(url, auth=(user, pwd), headers=headers ,data="{\"type\":\"Emergency\"}")
# Check for HTTP codes other than 200
if response.status_code != 200:
print('Status:', response.status_code, 'Headers:', response.headers, 'Error Response:',response.json())
exit()
# Decode the JSON response into a dictionary and use the data
data = response.json()
print(data)
Alternative Options for url THAT MAY NOT WORK = 'https://xxxx.service-now.com/api/now/table/"optionsA" OR "B" OR "C" is as follows:
A) POST /sn_chg_rest/change/standard/{standard_change_template_id}
B) POST api/sn_chg_rest/change/normal
C) POST Versioned URL /api/sn_chg_rest/{version}/change/emergency
link for A, B , C above : https://developer.servicenow.com/dev.do#!/reference/api/orlando/rest/change-management-api#changemgmt-POST-emerg-create-chng-req
Resources:
https://docs.servicenow.com/bundle/paris-it-service-management/page/product/change-management/task/t_AddNewChangeType.html
https://developer.servicenow.com/dev.do#!/reference/api/orlando/rest/change-management-api
API_URL="/api/sn_chg_rest/v1/change/emergency"
this Might have worked, going to confirm.
Yup this works ! unable to submit Standard OR Expedited. But that might be a setting that needs to be enabled (Not sure). Looking into it further. Some progress.
We use IdentityServer3 (IdSvr3) for authorization/authentication. We want to offer the ability for our end user (or resource owner: RO) to log in (log through) to a second trusted website (site B) without login in to site B also, after they have already logged in to an initial website (site A). Site B administers a different set of resources for the RO. It is important that the RO is not redirected to the IdSvr3 login/consent screen. A possible approach I found so far is: inside site A an access token is created by calling RequestResourceOwnerPasswordAsync (passing username and password plus scope = "openid ..."). This access token is send to site B with which the RO can be authenticated. Site B retrieves the user info by calling the connect/userinfo endpoint. I want to know if this is a correct approach/flow. We assume that the RO will always enter site A first, not site B.
Thanks in advance for taking your time to think with me about this.
what you can do here is to send a authorize request to identity server for Site B Scope and request id_token or reference token. Make sure while sending the authorize request to idsrv you are using prompt=none, this way you will get the access_token without showing a consent to the user again if the user is already logged-in to site A.
Below example is doing the same from a JS file. In Site A you can refer to this script file and execute the script using IIFE.
function getIdentityServerURL() {
var url = global.appSettings.identityServerURL
+ "/connect/authorize?client_id=siteB&response_type=id_token token&redirect_uri="
+ global.appSettings.siteBUrl + "/Main/SsoCallback&scope=siteBscope openid email roles&prompt=none&nonce="
+ genNonce();
return encodeURI(url);
}
The code above will redirect you to SsoCallback page where you can create a virtual iframe and post the token back to site B after reducing the result from authorize request. Refer to code below.
<script type="text/javascript">
var identityresult = window.location.hash.split('&').reduce(function (result, item) {
var parts = item.split('=');
result[parts[0]] = parts[1];
return result;
}, {});
window.parent.postMessage(identityresult, '*');
your script can listen to postmessage event. Hope this helps.
I have functions in my API code, some of which should only return requested results only to those requests that have a correct token. If the request is not authorized, then return a generic 401 response.
I have created a helper function is_authorised() which returns true/false.
Now, in a function that I want to make accessible only to authorized users, I check the result of the is_authorised() function and return respective response.
Here is an example:
get_players(SessionID, _Env, _Input) ->
case is_authorized(_Env) of
true ->
Response = [json_header(), players:select_all()];
false ->
Response = get_unauthorized_response()
end,
mod_esi:deliver(SessionID, Response).
I wonder if it's possible to make this checking look more elegant, like Python decorators used in Flask.
#app.route('/user')
#required_roles('admin', 'user')
def user_page(self):
return "You've got permission to access this page."
I know I can't use custom functions in guards, so I think it's not possible to make it as a guard.
Please advise.
Not as elegant as Python decorators, but you can use higher order functions to make this much cleaner. Create a function that accepts SessionID, Env, Input, and a callback function that should be executed when the user is authorized, and call that from all the functions that need to do the authorization check.
(Untested) code:
authorized(SessionID, Env, Input, Fun) ->
Response = case is_authorized(Env) of
true ->
Fun();
false ->
get_unauthorized_response()
end,
mod_esi:deliver(SessionID, Response).
get_players(SessionID, Env, Input) ->
authorized(SessionID, Env, Input, fun() ->
[json_header(), players:select_all()]
end).
You can pass more arguments to authorized if you want to do more extensive checks. With a role_of/1 function that accepts Env and returns the role as an atom, you can allow access to certain users with something like this:
authorized(SessionID, Env, Input, Roles, Fun) ->
Response = case lists:member(role_of(Env), Roles) of
true ->
Fun();
false ->
get_unauthorized_response()
end,
mod_esi:deliver(SessionID, Response).
get_players(SessionID, Env, Input) ->
authorized(SessionID, Env, Input, [admin, user], fun() ->
[json_header(), players:select_all()]
end).
You might want to look into cowboy or webmachine as they'd give you a framework over a state machine to handle REST requests. Implement the 'forbidden/2' callback to indicate unauthorized access.
On a side note: a 401 status code indicates a failure of authentication at the http layer when doing one of the well known http auth mechanisms like basic. See also https://en.wikipedia.org/wiki/Basic_access_authentication
What you are dealing with is a authorization failure and the correct associated http status code for that case would be 403 (as the forbidden callback from above would return).
Quoting from the WebSharper 2.5 alpah docs the remoting component assumes that:
RPC-callable methods are safe to call from the web by an unauthenticated client.
Is there anyway to secure remote calls so they can only be called from an authenticated client?
One of the samples in the WebSharper website is a chat application that seems to do just that by providing a Login method that returns an authentication token, which is then required to call the other functions:
[<Rpc>]
let Login (user: string) : Option<Auth.Token> =
let s = State.Get()
if s.Users.ContainsKey user then
None
else
// (snip)
user |> Auth.Generate |> Some
[<Rpc>]
let Poll (auth: Auth.Token) (time: int) =
// (snip)
The full chat sample can be found here: http://www.websharper.com/samples/Chat
Just been playing with this myself. Turns out if you're using Forms Authentication you can read the current HTTPContext from inside RPC methods so you can do something like this:
[<Rpc>]
let protectedMethod () =
match IntelliFactory.WebSharper.Sitelets.UserSession.GetLoggedInUser() with
| Some(username) ->
// User is authenticated... do stuff
()
| None -> failwith "Authentication failed"
We used Erlang/Cowboy to develop a simple chatting service based on WebSockets. When user connects in, an authentication would be done based on the URL parameter, and it would return user id or none for the connection.
My stupid question is, how to store the user id into the Request data structure and the user id can be get for later-on processes?
If you are using cowboy_rest you can use the handler_state to store your user data after authorization. Something like:
-record(rs_state{user}).
rest_init(Req, Opts) ->
{ok, Req, #rs_state{}}.
is_authorized(Req, State) ->
%% authentication code
{ok, User} = ...
{true, Req, State#rs_state{user=User}}}.