How do I define the response to a missing claim in NancyFX? - authentication

I have a module meant to enable administrators to manage users. Of course, this module requires not only authentication but also a specific claim. I have discovered that, if you are missing the claim in question, you actually get only a blank page as a response to your request. This isn't ideal.
How can I change that?
Module code below (if anyone needs to look)...
public class UserModule : NancyModule
{
public UserModule()
: base("/users")
{
this.RequiresAnyClaim(new[] { "evil-dictator" });
Get["/"] = _ =>
{
ViewBag.UserName = Context.CurrentUser.UserName;
return Negotiate.WithView("Index");
};
// Generate an invitation for a pre-approved user
Get["/invite"] = _ =>
{
throw new NotImplementedException();
};
}
}

You can use an After hook to alter the response in case the claim is missing. Note that the response you get when you do not have the required claim has HTTP status code 403 Forbidden. Check for that in the After hook and alter the response as needed.
E.g. the following will redirect to the root - "/" - of the application, when the user does have the evil dictator claim:
public class UserModule : NancyModule
{
public UserModule()
: base("/users")
{
After += context =>
{
if (ctx.Response.StatusCode == HttpStatusCode.Forbidden)
ctx.Response = this.Response.AsRedirect("/");
}
this.RequiresAnyClaim(new[] { "evil-dictator" });
Get["/"] = _ =>
{
ViewBag.UserName = Context.CurrentUser.UserName;
return Negotiate.WithView("Index");
};
// Generate an invitation for a pre-approved user
Get["/invite"] = _ =>
{
throw new NotImplementedException();
};
}
}

Related

How to add tenant / organization id to every URL and read it inside controller

I am writing an ASP.NET Core web application. In my application, the user can be a member of one or more organizations (this information is stored in the DB). I would like to give the users the possibility to select the context of organization in which the whole app should be running. This context should be passed to every controller so that I can do proper checks and return only the data from the selected org.
In other words, I would like to achieve the state when the user can access the app via:
app.mydomain.com/org1/controller/action/id and using a dropdown they should be able to switch to org2 and access app.mydomain/org2/controller/action/id
In both cases I need to be able to read the organization inside the controller. This should apply to every controller.
How should this be done? Is it possible to construct a route that would handle it and just add new parameter to every controller, e.g. orgId? Or maybe there should be a service that reads this information from the URL and can be injected into controllers? How would the route look then? I have read some tutorials about handling multiple languages in a similar way (with the culture in URL) but I am not able to translate that into my case.
For how to pass the select org id to the controller:
View:
<form action="/Orgnization/Test">
<select id="select" name="select" asp-items="#ViewBag.Sel"></select>
</form>
#section Scripts
{
<script>
$(document).ready(function () {
$("#select").change(function () {
$('form').submit();
});
})
</script>
}
Configure selectlist:
public class HomeController : Controller
{
public IActionResult Index()
{
//for easy testing,I just set it manually...
ViewBag.Sel = new List<SelectListItem>() {
new SelectListItem() { Value = "-1", Text = "--- Select ---" },
new SelectListItem() { Value = "org1", Text = "org1" },
new SelectListItem() { Value = "org2", Text = "org2" },
new SelectListItem() { Value = "org3", Text = "org3" },
};
return View();
}
}
Controller:
public class OrgnizationController : Controller
{
public IActionResult Test(string select)
{
//the select is the value you choose
//do your stuff....
return View();
}
}
If you want to add prefix to the route and get it in the controller:
Startup.cs:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{orgid?}/{controller=Home}/{action=Privacy}/{id?}");
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Privacy}/{id?}");
});
Controller:
public class OrgnizationController : Controller
{
public IActionResult Test(string select)
{
var org = "";
if (select==null)
{
var path = Request.Path.ToString();
org = path.Split('/')[1];
}
//do your stuff....
return View();
}
}
Result:

How to add captcha required only for a particular condition yii2

I am trying make the captcha field required only when the number of failed login attempts exceed 3 times. For which I have written below code till now.
In LoginForm model I have added the below rules
public function rules()
{
return [
[['username', 'password'], 'required'],
['password', 'validatePassword'],
['verifyCode', 'captcha', 'when' => function($model) {
return $this->checkattempts();
}],
];
}
public function validatePassword($attribute, $params)
{
if (!$this->hasErrors()) {
$user = $this->getUser();
if (!$user || !$user->validatePassword($this->password)) {
$this->addLoginAttempt($user->id);
$this->addError($attribute, 'Incorrect username or password.');
}
}
}
public function checkattempts()
{
$user = $this->getUser();
$ip = $this->get_client_ip();
$data = (new Query())->select('*')
->from('login_attempts')
->where(['ip' => $ip])->andWhere(['user_ref_id' => $user->id])
->one();
if($data["attempts"] >=3){
return false;
}else{
return true;
}
}
public function addLoginAttempt($uid) {
$ip = $this->get_client_ip();
$data = (new Query())->select('*')
->from('login_attempts')
->where(['ip' => $ip])->andWhere(['user_ref_id' => $uid])
->one();
if($data)
{
$attempts = $data["attempts"]+1;
#Yii::$app->db->createCommand("UPDATE login_attempts SET attempts=".$attempts." where ip = '$ip' AND user_ref_id=$uid")->execute();
}
else {
Yii::$app->db->createCommand("INSERT into login_attempts (attempts, user_ref_id, ip) VALUES(1,'$uid', '$ip')")->execute();
}
}
Here I am validating the password first. If the password is incorrect then I am incrementing the count by 1. This part is working fine. The count is incrementing successfully.
After this I am trying to get the count of failed attempts while validating captcha using the function checkattempts(), but it is not working.
Can anyone please tell me where I have made mistake.
Thanks in advance.
In your model:
if (!$model->checkattempts())
//show the captcha
Then, in your model rules you'll need something like:
['captcha', 'captcha'],
In your case, what you can do is use different scenarios depending on the user attempts, and in one scenario (more than X attempts) make the captcha required.
More documentation about the captcha and about scenarios.

Angular2 - Multiple dependent sequential http api calls

I am building an Angular2 app and one of the components needs to make multiple API calls which are dependent on the previous ones.
I currently have a service which makes an API call to get a list of TV shows. For each show, I then need to call a different API multiple times to step through the structure to determine if the show exists on a Plex server.
The API documentation is here
For each show, I need to make the following calls and get the correct data to determine if it exists: (Assume we have variables <TVShow>, <Season>, <Episode>)
http://baseURL/library/sections/?X-Plex-Token=xyz will tell me:
title="TV Shows" key="2"
http://baseURL/library/sections/2/all?X-Plex-Token=xyz&title=<TVShow> will tell me: key="/library/metadata/2622/children"
http://baseURL/library/metadata/2622/children?X-Plex-Token=xyz will tell me: index="<Season>" key="/library/metadata/14365/children"
http://baseURL/library/metadata/14365/children?X-Plex-Token=xyz will tell me: index="<Episode>" which implies that the episode I have exists.
The responses are in json, I have removed a lot of the excess text. At each stage I need to check that the right fields exist (<TVShow>, <Season>, <Episode>) so that they can be used for the next call. If not, I need to return that the show does not exist. If it does, I will probably want to return an id for the show.
I have looked at lots of examples including promise, async & flatmap, but am not sure how to solve this based on the other examples I have seen.
How to chain Http calls in Angular2
Angular 2.0 And Http
Angular 2 - What to do when an Http request depends on result of another Http request
Angular 2 chained Http Get Requests with Iterable Array
nodejs async: multiple dependant HTTP API calls
How to gather the result of Web APIs on nodeJS with 'request' and 'async'
Here is what I have for getting the list of shows. (shows.service.ts)
export class ShowsHttpService {
getShows(): Observable<Show[]> {
let shows$ = this._http
.get(this._showHistoryUrl)
.map(mapShows)
.catch(this.handleError);
return shows$;
}
}
function mapShows(response:Response): Show[] {
return response.json().data.map(toShow);
}
function toShow(r:any): Show {
let show = <Show>({
episode: r.episode,
show_name: r.show_name,
season: r.season,
available : false, // I need to fill in this variable if the show is available when querying the Plex API mentioned above.
});
// My best guess is here would be the right spot to call the Plex API as we are dealing with a single show at a time at this point, but I cannot see how.
return show;
}
Here is the relevant code from the component (shows.component.ts)
public getShows():any {
this._ShowsHttpService
.getShows()
.subscribe(w => this.shows = w);
console.log(this.shows);
}
Bonus points
Here are the obvious next questions that are interesting, but not necessary:
The first API query will be much faster than waiting for all of the other queries to take place (4 queries * ~10 shows). Can the initial list be returned and then updated with the available status when it is ready.
The first Plex call to get the key="2" only needs to be performed once. It could be hard coded, but instead, can it be performmed once and remembered?
Is there a way to reduce the number of API calls? I can see that I could remove the show filter, and search through the results on the client, but this doesn't seam ideal either.
The 4 calls for each show must be done sequentially, but each show can be queried in parallel for speed. Is this achievable?
Any thoughts would be much appreciated!
Not sure if I totally understand your question, but here is what I do:
I make the first http call, then when the subscribe fires, it calls completeLogin. I could then fire another http call with its own complete function and repeat the chain.
Here is the component code. The user has filled in the login information and pressed login:
onSubmit() {
console.log(' in on submit');
this.localUser.email = this.loginForm.controls["email"].value;
this.localUser.password = this.loginForm.controls["password"].value;
this.loginMessage = "";
this.checkUserValidation();
}
checkUserValidation() {
this.loginService.getLoggedIn()
.subscribe(loggedIn => {
console.log("in logged in user validation")
if(loggedIn.error != null || loggedIn.error != undefined || loggedIn.error != "") {
this.loginMessage = loggedIn.error;
}
});
this.loginService.validateUser(this.localUser);
}
This calls the loginservice ValidateUser method
validateUser(localUser: LocalUser) {
this.errorMessage = "";
this.email.email = localUser.email;
var parm = "validate~~~" + localUser.email + "/"
var creds = JSON.stringify(this.email);
var headers = new Headers();
headers.append("content-type", this.constants.jsonContentType);
console.log("making call to validate");
this.http.post(this.constants.taskLocalUrl + parm, { headers: headers })
.map((response: Response) => {
console.log("json = " + response.json());
var res = response.json();
var result = <AdminResponseObject>response.json();
console.log(" result: " + result);
return result;
})
.subscribe(
aro => {
this.aro = aro
},
error => {
console.log("in error");
var errorObject = JSON.parse(error._body);
this.errorMessage = errorObject.error_description;
console.log(this.errorMessage);
},
() => this.completeValidateUser(localUser));
console.log("done with post");
}
completeValidateUser(localUser: LocalUser) {
if (this.aro != undefined) {
if (this.aro.errorMessage != null && this.aro.errorMessage != "") {
console.log("aro err " + this.aro.errorMessage);
this.setLoggedIn({ email: localUser.email, password: localUser.password, error: this.aro.errorMessage });
} else {
console.log("log in user");
this.loginUser(localUser);
}
} else {
this.router.navigate(['/verify']);
}
}
In my login service I make a call to the authorization service which returns an observable of token.
loginUser(localUser: LocalUser) {
this.auth.loginUser(localUser)
.subscribe(
token => {
console.log('token = ' + token)
this.token = token
},
error => {
var errorObject = JSON.parse(error._body);
this.errorMessage = errorObject.error_description;
console.log(this.errorMessage);
this.setLoggedIn({ email: "", password: "", error: this.errorMessage });
},
() => this.completeLogin(localUser));
}
In the authorization service:
loginUser(localUser: LocalUser): Observable<Token> {
var email = localUser.email;
var password = localUser.password;
var headers = new Headers();
headers.append("content-type", this.constants.formEncodedContentType);
var creds:string = this.constants.grantString + email + this.constants.passwordString + password;
return this.http.post(this.constants.tokenLocalUrl, creds, { headers: headers })
.map(res => res.json())
}
The point here in this code, is to first call the validateUser method of the login service, upon response, based on the return information, if its valid, I call the loginUser method on the login service. This chain could continue as long as you need it to. You can set class level variables to hold the information that you need in each method of the chain to make decisions on what to do next.
Notice also that you can subscribe to the return in the service and process it there, it doesn't have to return to the component.
Okay, Here goes:
public getShows():any {
this._ShowsHttpService
.getShows()
.subscribe(
w => this.shows = w,
error => this.errorMessage = error,
() => this.completeGetShows());
}
completeGetShow() {
//any logic here to deal with previous get;
this.http.get#2()
.subscribe(
w => this.??? = w),
error => this.error = error,
() => this.completeGet#2);
}
completeGet#2() {
//any logic here to deal with previous get;
this.http.get#3()
.subscribe(
w => this.??? = w),
error => this.error = error,
() => this.completeGet#3);
}
completeGet#3() {
//any logic here to deal with previous get;
//another http: call like above to infinity....
}

Is my order complete return approach correct?

When a customer is returned to the following URL (example);
http://prestashop.dev/index.php?action=completed&controller=callback&fc=module&hmac={valid-hmac}&merchant_order_id=14&module=chippin
After a successful payment, It will call on this FrontController sub-class;
class ChippinCallbackModuleFrontController extends ModuleFrontController
{
public function postProcess()
{
$chippin = new Chippin();
$payment_response = new PaymentResponse();
$payment_response->getPostData();
// if a valid response from gateway
if(ChippinValidator::isValidHmac($payment_response)) {
// "action" is passed as a param in the URL. don't worry, the Hmac can tell if it's valid or not.
if ($payment_response->getAction() === "completed") {
// payment_response->getMerchantOrderId() will just return the id_order from the orders table
$order_id = Order::getOrderByCartId((int) ($payment_response->getMerchantOrderId()));
$order = new Order($order_id);
// this will update the order status for the benefit of the merchant.
$order->setCurrentState(Configuration::get('CP_OS_PAYMENT_COMPLETED'));
// assign variables to smarty (copied this from another gateway, don't really understand smarty)
$this->context->smarty->assign(
array(
'order' => $order->reference,
)
);
// display this template
$this->setTemplate('confirmation.tpl');
I'm quite new to Prestashop. I'm just not sure if this is technically done or not. The confirmation.tlp view does display with the order->reference and the order status is updated to "Completed" but is this all I need?
Are there any other considerations? I have the opportunity to call a hookDisplayPaymentReturn at this point but why should I?
I seem to have a pretty standard return page. Is this enough;
Update - Do I just call a hook something like;
public function displayPaymentReturn()
{
$params = $this->displayHook();
if ($params && is_array($params)) {
return Hook::exec('displayPaymentReturn', $params, (int) $this->module->id);
}
return false;
}
As far as I can see everything seems okay for me.
You should consider adding hookDisplayPaymentReturn it allows other modules to add code to your confirmation page. For example a Google module could add javascript code that sends order informations to analytics on confirmation page.
EDIT
class ChippinCallbackModuleFrontController extends ModuleFrontController
{
public function postProcess()
{
$chippin = new Chippin();
$payment_response = new PaymentResponse();
$payment_response->getPostData();
// if a valid response from gateway
if(ChippinValidator::isValidHmac($payment_response)) {
// "action" is passed as a param in the URL. don't worry, the Hmac can tell if it's valid or not.
if ($payment_response->getAction() === "completed") {
// payment_response->getMerchantOrderId() will just return the id_order from the orders table
$order_id = Order::getOrderByCartId((int) ($payment_response->getMerchantOrderId()));
$order = new Order($order_id);
// this will update the order status for the benefit of the merchant.
$order->setCurrentState(Configuration::get('CP_OS_PAYMENT_COMPLETED'));
// assign variables to smarty (copied this from another gateway, don't really understand smarty)
$this->context->smarty->assign(
array(
'order' => $order->reference,
'hookDisplayPaymentReturn' => Hook::exec('displayPaymentReturn', $params, (int) $this->module->id);
)
);
$cart = $this->context->cart;
$customer = new Customer($cart->id_customer);
Tools::redirect('index.php?controller=order-confirmation&id_cart='.$cart->id.'&id_module='.$this->module->id.'&id_order='.$order->id.'&key='.$customer->secure_key);
And in your module :
class myPaymentModule extends PaymentModule
{
public function install()
{
if (!parent::install() || !$this->registerHook('paymentReturn'))
return false;
return true;
}
// Example taken from bankwire module
public function hookPaymentReturn($params)
{
$state = $params['objOrder']->getCurrentState();
$this->smarty->assign(array(
'total_to_pay' => Tools::displayPrice($params['total_to_pay'], $params['currencyObj'], false),
'bankwireDetails' => Tools::nl2br($this->details),
'bankwireAddress' => Tools::nl2br($this->address),
'bankwireOwner' => $this->owner,
'status' => 'ok',
'id_order' => $params['objOrder']->id
));
if (isset($params['objOrder']->reference) && !empty($params['objOrder']->reference))
$this->smarty->assign('reference', $params['objOrder']->reference);
return $this->display(__FILE__, 'confirmation.tpl');
}
}

Elliot Haughin API verify credentials error

I am currently building an Twitter client application for campus project using Codeigniter and Elliot Haughin Twitter library. It's just a standard application like tweetdeck. After login, user will be directed to the profile page containing timline. I am using Jquery to refresh the timeline every 20 second. At the beginning, everything run smoothly until i found the following error at the random time :
![the error][1]
A PHP Error was encountered
Severity: Notice
Message: Undefined property: stdClass::$request
Filename: libraries/tweet.php
Line Number: 205
I already search the web about this error but can't find satisfied explanation. So I tried to find it myself and found that the error comes out because credentials validation error. I tried to var_dump the line $user = $this->tweet->call('get', 'account/verify_credentials'); and resulting an empty array. My question is how come this error showed up when user already login and even after updated some tweets? is there any logical error in my script or is it something wrong with the library? Could anyone explain whats happening to me? please help me...
Here's my codes:
The Constructor Login.php
<?php
class Login extends CI_Controller
{
function __construct()
{
parent::__construct();
$this->load->library('tweet');
$this->load->model('login_model');
}
function index()
{
$this->tweet->enable_debug(TRUE); //activate debug
if(! $this->tweet->logged_in())
{
$this->tweet->set_callback(site_url('login/auth'));
$this->tweet->login();
}
else
{
redirect('profile');
}
}
//authentication function
function auth()
{
$tokens = $this->tweet->get_tokens();
$user = $this->tweet->call('get', 'account/verify_credentials');
$data = array(
'user_id' => $user->id_str,
'username' => $user->screen_name,
'oauth_token' => $tokens['oauth_token'],
'oauth_token_secret' => $tokens['oauth_token_secret'],
'level' => 2,
'join_date' => date("Y-m-d H:i:s")
);
//jika user sudah autentikasi, bikinkan session
if($this->login_model->auth($data) == TRUE)
{
$session_data = array(
'user_id' => $data['user_id'],
'username' => $data['username'],
'is_logged_in' => TRUE
);
$this->session->set_userdata($session_data);
redirect('profile');
}
}
}
profile.php (Constructor)
<?php
class Profile extends CI_Controller
{
function __construct()
{
parent::__construct();
$this->load->library('tweet');
$this->load->model('user_model');
}
function index()
{
if($this->session->userdata('is_logged_in') == TRUE)
{
//jika user telah login tampilkan halaman profile
//load data dari table user
$data['biography'] = $this->user_model->get_user_by_id($this->session->userdata('user_id'));
//load data user dari twitter
$data['user'] = $this->tweet->call('get', 'users/show', array('id' => $this->session->userdata('user_id')));
$data['main_content'] = 'private_profile_view';
$this->load->view('includes/template', $data);
}
else
{
//jika belum redirect ke halaman welcome
redirect('welcome');
}
}
function get_home_timeline()
{
$timeline = $this->tweet->call('get', 'statuses/home_timeline');
echo json_encode($timeline);
}
function get_user_timeline()
{
$timeline = $this->tweet->call('get', 'statuses/user_timeline', array('screen_name' => $this->session->userdata('username')));
echo json_encode($timeline);
}
function get_mentions_timeline()
{
$timeline = $this->tweet->call('get', 'statuses/mentions');
echo json_encode($timeline);
}
function logout()
{
$this->session->sess_destroy();
redirect('welcome');
}
}
/** end of profile **/
Default.js (The javascript for updating timeline)
$(document).ready(function(){
//bikin tampilan timeline jadi tab
$(function() {
$( "#timeline" ).tabs();
});
//home diupdate setiap 20 detik
update_timeline('profile/get_home_timeline', '#home_timeline ul');
var updateInterval = setInterval(function() {
update_timeline('profile/get_home_timeline', '#home_timeline ul');
},20*1000);
//user timeline diupdate pada saat new status di submit
update_timeline('profile/get_user_timeline', '#user_timeline ul');
//mention diupdate setiap 1 menit
update_timeline('profile/get_mentions_timeline', '#mentions_timeline ul');
var updateInterval = setInterval(function() {
update_timeline('profile/get_mentions_timeline', '#mentions_timeline ul');
},60*1000);
});
function update_timeline(method_url, target)
{
//get home timeline
$.ajax({
type: 'GET',
url: method_url,
dataType: 'json',
cache: false,
success: function(result) {
$(target).empty();
for(i=0;i<10;i++){
$(target).append('<li><article><img src="'+ result[i]['user']['profile_image_url'] +'">'+ result[i]['user']['screen_name'] + ''+ linkify(result[i]['text']) +'</li></article>');
}
}
});
}
function linkify(data)
{
var param = data.replace(/(^|\s)#(\w+)/g, '$1#$2');
var param2 = param.replace(/(^|\s)#(\w+)/g, '$1#$2');
return param2;
}
That's the codes. Please help me. After all, I really appreciate all comments and explanation from you guys. Thanks
NB: sorry if i had bad English grammar :-)
You are making a call to statuses/home_timeline which is an unauthenticated call. The rate limit for unauthenticated calls is 150 requests per hour.
Unauthenticated calls are permitted 150 requests per hour.
Unauthenticated calls are measured against the public facing IP of the
server or device making the request.
This would explain why you see the problem at the peak of your testing.
With the way you have it setup you would expire your rate limit after 50 minutes or less.
I suggest changing the interval to a higher number, 30 seconds would do. That way you'll be making 120 requests per hour and under the rate limit.