User details being displayed in the browser dev tools - javascript

I have a rails application where i am using javascript. Now when the user logsin inside the application i can see all the user details(email,pw,phone etc.) in the Network->Preview section of the browser dev tools. Is there a way to hide it in rails? so that when i open the browser dev tools i dont see the user details. I am using React js in the front end.

As far as I could understand, this is an authorization issue. You probably want to have different roles for your users and, based on such roles, decide what to display.
You may use policies to define which content each user is allowed to access. To this end, you could use the pundit gem. It is very useful to define authorization mechanisms for controller actions.
If different users can access the same route but you need to hide some fields/details for some of them, you should pass the current_user to your serializations. For example, if you are using the Active Model Serializers gem, you need to add the current_user to the scope of your serializers:
class ApplicationController < ActionController::Base
serialization_scope :current_user
end
And then use that scope to display user details:
class UserSerializer < BaseSerializer
attribute :name # Everyone can see
attribute :email do # Only admins can see
user = scope
if user
if user.admin?
object.email
end
end
end
end
This article have a deeper discussion regarding authorization in rails apps

Related

What is the safest and most effective approach when you need to pass data to another component through navigation in Angular8?

I´m currently developing an application based on user authentication where each user can register a student-campus as a teacher and currently, I'm on a feature where I have two routes:
Route 1: It has a Datagrid where I'm listing all of the student campuses that I've already created and each row has an edit button that navigates to "Route 2" and the purpose of that is to edit the already created student campus.
Route 2: It has a form with all the necessary fields to create a student-campus.
As you can see I need to pass the student-campus ID to fetch data in the ngOnInit to fill the fields and be able to edit the above-mentioned, so I have several options in consideration:
Option 1: Pass ID in the URL.
this.router.navigate(['planteles/registrar', idPlantel]);
https://myapplication/planteles/registrar/1
Option 2: Pass ID in the URL with queryParams.
this.router.navigate(['planteles/registrar'], { queryParams: { ID: idPlantel } });
https://myapplication/planteles/registrar?ID=1
Option 3: Pass ID in the state object of navigation extras.
this.router.navigate(['planteles/registrar'], { state: { id: idPlantel } });
Option 4: Shared service and BehaviorSubject to subscribe to data.
I owe you the code
I'm able to use any of these but I have a problem with each one of them.
I can't use Option 1 and Option 2 because the ID cannot be changed by the teacher because that gives him the possibility to fetch the student-campus data of another teacher and edit it, so it isn't safe.
The problem with option 3 and option 4 is when I refresh the page the state is lost.
Currently, I have a workaround with option 3 which is to redirect the user to the previous page if the state is undefined but I don't like that solution. I'd like to persist data if the user reloads the page without using LocalStorage.
Thanks in advance, all help or contribution is well appreciated.
Option 1 is the correct option here (and the way you will find most sites in the real world are implemented... including this one we're on now). The problem is your approach to web security, and what you need to fix is your backend. You're approaching web security as though front end security is real, it's not. Web security exists on your backend. Users should not be able to fetch or view or manipulate data that does not belong to them, and this must be enforced by your backend.
A high level example of how this might work: some secure authentication token should be granted when the user logs in, then this authentication token should be attached to each request. The API then uses this token to check which user is making the request and ensures they have the proper permissions. If they do not (as in the case of the user editing their URL param to some ID they do not have permissions for) or if there is no token, the API should return a 401 or 403 response and the front end should handle it appropriately (ie sending them back to list, or showing an error page, whatever you decide)... how to issue this token, make it secure, and make use of it is an entirely separate topic beyond the scope of this answer.
In any of the options, I could open my dev tools, and view any API requests being made, and change the ID's and use that to view or manipulate other people's data without any effort at all. So options 3 / 4 are barely more "safe" than 1 or 2. As none of these are safe without properly implemented backend security.
Front end "security" exists only as user experience. Me and you are both using the same URL to view this page, but we see different options and buttons, like you can edit or delete your post and accept answers, while I can't, but I can edit or delete my answer etc. This isn't for true security purposes, SO's servers enforce who can and can't take what actions. It's just showing me and you the UI that reflects our different permissions, ie, its all just UX.
There's another way too, which is defined in Angular docs itself.
NavigationExtras
Example:
let navigationExtras: NavigationExtras = {
queryParams: {
"firstname": "Nic",
"lastname": "Raboy"
}
};
this.router.navigate(["page2"], navigationExtras);

How to open a specific page of the app when I click on a push notification

I'm developing an IOS app that receives push notifications. Is it possible to open a specific page of my app when I receive it and click on the notification?
Yes it is possible and its part of a "thing" called Deep Linking.
Basically the idea is to architect your project where each view controller would be mapped with a URL-Scheme (yeah that string thing).
So that in applicationDidFinish you know which viewcontroller represents which URL-Scheme.
Now send the data in the push notification along wit the URL-Scheme of the viewController, you want the data to be shown. Write code in the method to parse and push that particular controller. This is the basic rough idea of how it would/should work, I might have missed out a few details, but here is a good article and tutorial on it, if you wanna go through.
Hope this helps
Our app does this, and to be quite honest, its a pain in the ass. But here is how we went about it:
Have a root view controller that handles all incoming notifications (usually via some kind of data source or manager class)
Create a model object to represent your notification and make sure this has a type property.
Create a typedef enum to store the different kinds of notifications your app can expect to receive.
The root view controller should implement a method with a switch statement that switches on the type of notification being handled.
The root view controller can then load up the correct view controller for your notification, so you pass the notification object to the view controller if you need to use any information held within it.
This is a basic overview, and you will need to adjust it to suit your navigation stack depending on how complex it is. It may be that you need to pass that notification object down a view levels in your view hierarchy (as we have to do).
Depending on how your application behaves, you may also need to consider how your application handles returning from the background to the foreground i.e. what state is it in when the app receives its notification and reset your navigation stack where appropriate so that your navigation stack reacts correctly.
You will receive control in the "didReceiveRemoteNotification" whenever user tap on the notification.
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let navigationController = storyboard.instantiateViewControllerWithIdentifier("DetailViewController") as! DetailViewController
self.window?.rootViewController?.presentViewController(navigationController, animated: true, completion: {})
}
Here you can also detect if the application is in active state or inactive and perform the presentation accordingly.
So get rootViewController and present the screen you need to show.
Also if you need to push the controller in already presented navigation stack, then you can fetch the navigation controller from window, then push the specific controller over it.
The decision to show the controller you need to present can be taken from the userInfo received.
This way you can open any specific controller you want.

Angular ui-router: dynamically add states not during config

Like this question, I want to dynamically add ui-router states, but I'm not sure how to do it given the following:
We start with a small set of routes (max 5) that allow the user to navigate the login process: The user logs in (multi step login process), then selects the product type and role they want to use (assuming user has more than one product type/role, else they will bypass this view). At that point, I want to go out to my service and get the list of routes the user has access to, given the userid, product type, & role - I plan to send down an array of data structures that very closely match what is provided to $stateProvider.state(...).
In my app.run.js, I am checking $rootScope.$on("$stateChangeStart" and moving the user along from view to view in the multi-step login process and therefore know when I need to go out to my service to load up the available routes.
When the routes are loaded, I'll probably put a flag in a cookie to indicate that it was properly loaded for this user/product/role.
I don't think a provider really makes sense here since I don't want the routes loaded at config. It feels wrong (not sure why) to call the service to load the routes in the stateChangeStart.
Is this approach a reasonable one?
Note: I also see that there is an outstanding request to be able to remove states from $stateProvider. In the meantime until this request is implemented, how do we clear the $stateProvider of routes (apart from a browser refresh)?
For adding states after the config phase, you should use the Future State functionality in the ui-router-extra package.
Since there's no official support for deleting routes, you could probably merge in this PR to get something going, but you'd have to add functionality to remove the condition from urlRouterProvider as well since that's a bug with that PR.

rspec testing: putting :js => true also affects the before block

I have a page that is for booking an appointment and it has some javascript code that selects the earliest time and day when the appointment is available, after which user can click on the button to schedule the appointment.
So in order to test that, I was writing some rspec test like the following,
book_appointment_spec.rb
context "when on the profile page" do
before do
click_linkedin_button
end
it 'book an appointment', :js => true do
click_link "new-appointment"
choose "doctor1"
click_button "Submit and Schedule"
expect(page).to have_content "Congrats!"
end
end
click_linkedin_button is a method definition that just logins a user via linkedin oauth. The problem is that even after setting OmniAuth.config.mock_auth[:linkedin], whenever I set :js => true around it block, it asks me to login via linkedin, http://imgur.com/mYUOxgD
I was wondering if anyone knows how to fix this problem.
Following are other files that might be relevant to this problem.
spec_helper.rb
require 'capybara/webkit/matchers'
Capybara.javascript_driver = :webkit
Gemfile
gem 'capybara-webkit'
gem 'database_cleaner'
gem 'capybara'
As you've discoverd, you can't run part of a test using one driver and part of the test using another. This would be equivalent to saying
Given I login in using Safari.
Then I should be logged in using Firefox.
So your solution is that you have to run your js login code in the test environment to login. This is actually a good thing (you want to test your js login code). If you want to avoid actually connecting to linkedin, every time you run this test, then you need to mock the connection to linkedin. Have a look at VCR (https://github.com/vcr/vcr), this will allow you to record your connection to linkedin, so that in subsequent test runs you don't have to goto linkedin.
Setting js: true in your rspec block is a shortcut to use the javascript-enabled driver for the whole example. So this driver will be available and used during the whole execution or the example, which includes all before/after/around blocks.
To work around this, instead of using js: true, you can manually set which driver to use at the point(s) of your example where you need to.
it {
do_some_stuff_with_default_driver
Capybara.current_driver = :webkit
do_some_stuff_with_webkit
Capybara.current_driver = :selenium
do_some_stuff_with_selenium
}
EDIT
Oops I just read this, so perhaps that solution will not be working. please let me know
Note: switching the driver creates a new session, so you may not be able to switch in the middle of a test.
I solved it by creating an actual linkedin account, put that auth info in the .env and just called fill_in method to fill out email and password field during its callback. And stuck with running a js driver throughout the entire context block.
EDIT: This is of course not the best answer so I am still accepting other answers.

How to dynamically update other pages' content

New to programming here. I'm using Rails to create a web app that does reviews, but am having a little trouble figuring out where to start on this one particular part. I'd appreciate any and all help:
Let's say that on my homepage I want to have a top 10 list of restaurants. Beside each place there would be a score. If you were to click on the link to that restaurant, it would bring you to that restaurant's detail page where you can rate a number of different qualities. As users rate the place the score will update. How can I get that score and ranking to be reflected on my main homepage based on how users rate each place? Thinking this might have to be done with some Javascript (or is there a way to do this in Rails?). Thanks!
The pure answer to your question is you need data persistence - a place to centrally store data & render it in the view for the user.
It's funny why you should ask this question in the Ruby on Rails section - this is exactly what this framework is for, and I would question your competency if you didn't consider this
--
Database
Rails uses a central database to store your data. It uses the MVC programming pattern to give you the ability to access that data wherever you require; allowing you to manipulate it as per your requirements:
Without detailing how to make your app from scratch, I'll give you the basic principle you should use:
#config/routes.rb
root "restaurants#index"
resources :restaurants
#app/controllers/restaurants_controller.rb
Class RestaurantsController < ApplicationController
def index
#restaurants = Restaurant.all
end
end
#app/models/restaurant.rb
Class Restaurant < ActiveRecord::Base
has_many :reviews
end
#app/models/review.rb
Class Review < ActiveRecord::Base
belongs_to :restaurant
end
#app/views/restaurants/index.html.erb
<% for restaurant in #restaurants do %>
<%= restaurant.reviews.count %>
<% end %>
--
Recommended Reading
You'll be best reading the Rails beginner guide on how to get this working properly. It's basically what Rails is for ;)
Only javascript is not enough.
When website is loaded in user's browser, all information from database are loaded once, like html files. Even if you'll update your restaurant object using ajax or simple redirect_to :back method, there will be no change on other page or other browser.
To solve it, you could use something like pusher to send events each time when somebody will trigger an event in your app and receive this events on your home page. If functionality of your app isn't complicated, you can use your own push server like faye in your rails app. Here is reference to railscast about using it:
http://railscasts.com/episodes/260-messaging-with-faye?view=similar
Anyway, i prefer to use pusher every time when I need to add some realtime functionality in my app.
http://pusher.com/tutorials
And about voting process, the nice solutions for doing that is:
"twitter/activerecord-reputation-system"
If you don't want to use ajax which will update your voted restaurant page's content, you can add vote method in your controller with redirect_to :back . This instruction will redirect your app to new url and after finish whole method will redirect back to refreshed page with updated voting status.
def vote
value = params[:type] == "up" ? 1 : -1
#haiku = Haiku.find(params[:id])
#haiku.add_or_update_evaluation(:votes, value, current_user)
redirect_to :back, notice: "Thank you for voting!"
end
To refresh home page dynamically when other users will vote on restaurants, u should create a rake task which will update the information on the page based on updated database structure.

Categories