I'm currently in the process of removing IP logging from our app, I was wondering what's the best way to go about doing this with Devise?
Your answer looks good, but if you're looking to track the IP for only specific users, one (less verbose but perhaps more confusing) alternative, could be...
protected
# Override Devise logic for IP tracking
# https://github.com/plataformatec/devise/blob/master/lib/devise/models/trackable.rb#L45
def extract_ip_from(request)
# Only track the IP for admin users (per GDPR rules).
request.remote_ip if admin?
end
This would cause a nil IP to be set for non-admin users.
Adding this method to the user model can allow you to be selective in what you track, in my case I'm being selective in when I track IP addresses:
def update_tracked_fields(request)
old_current = current_sign_in_at
new_current = Time.now.utc
self.last_sign_in_at = old_current || new_current
self.current_sign_in_at = new_current
if admin?
old_current = current_sign_in_ip
new_current = request.remote_ip
self.last_sign_in_ip = old_current || new_current
self.current_sign_in_ip = new_current
end
self.sign_in_count ||= 0
self.sign_in_count += 1
end
Related
Using the FLASK framework in Python, my application needs to:
register and log in users (with either a sqlite or postgres database)
access a specific google spreadsheet that the logged in user owns and output that data in a json format.
I am required to have my own authorization & authentication system
I am having a lot of trouble figuring out how to even structure the application - what directories and sub-directories should I have?
I have done A LOT of playing around (about 1 months worth). I am using a virtual environment but don't know how to test my code well either. In general, my code runs but I have no idea how they work together really.** I am completely new to flask.**
Structuring the app:
|app
|----run.py
|----config.py
|----database
|---------database.db
|----app
|---------views.py
|---------models.py
|---------forms.py
|---------extensions.py
|----templates
|---------....
|----static
|--------....
Authorization / Authentication:
I have looked at Flask-Login, Flask-Auth, Flask-Security. I understand the general idea but do not know how to securely implement a complete authorization & authentication system.
app = Flask(__name__)
app.config.from_object(config)
login_manager = LoginManager()
login_manager.init_app(app)
def create_app():
db.init_app()
db.app = app
db.create_all()
return app
#app.route('/')
def index():
#needs to render the homepage template
#app.route('/signup', methods = ['GET', 'POST'])
def register():
form = SignupForm()
if request.method == 'GET':
return render_template('signup.html', form=form)
elif request.method == 'POST':
if form.validate_on_submit():
if User.query.filter_by(email=form.email.data).first():
return "email exists"
else:
newuser = User(form.email.data, form.password.data)
db.session.add(newuser)
db.session.commit()
login_user(newuser)
return "New User created"
else:
return "form didn't validate"
return "Signup"
#app.route('/login', methods = ['GET', 'POST'])
def login():
form = SignupForm()
if request.method == 'GET':
return render_template('login.html', form=form)
elif request.method == 'POST':
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
if user:
if user.password == form.password.data:
login_user(user)
return "you are logged in"
else:
return "wrong password"
else:
return "user doesnt exist"
else:
return "form did not validate"
#login_manager.user_loader
def load_user(email):
return User.query.filter_by(email = email).first()
#app.route('/protected')
#login_required
def protected():
return "protected area for logged in users only"
if __name__ == '__main__':
#app.create_app()
app.run(port=5000, host='localhost')`
from flask_security import Security, SQLAlchemyUserDatastore, UserMixin, RoleMixin, login_required
import os
# Create app
app = Flask(__name__)
#app.config['DEBUG'] = True
app.config['SECRET_KEY'] = ''
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////'
app.config['SECURITY_PASSWORD_HASH'] = 'sha512_crypt'
app.config['SECURITY_PASSWORD_SALT'] = str(os.urandom(24))
# Create database connection object
db = SQLAlchemy(app)
# Define models
roles_users = db.Table('roles_users',
db.Column('user_id', db.Integer(), db.ForeignKey('user.id')),
db.Column('role_id', db.Integer(), db.ForeignKey('role.id')))
class Role(db.Model, RoleMixin):
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(80), unique=True)
description = db.Column(db.String(255))
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(255), unique=True)
password = db.Column(db.String(255))
active = db.Column(db.Boolean())
confirmed_at = db.Column(db.DateTime())
roles = db.relationship('Role', secondary=roles_users, backref=db.backref('users', lazy='dynamic'))
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
security = Security(app, user_datastore)
# Create a user to test with
#app.before_first_request
def create_user():
db.create_all()
user_datastore.create_user(email='', password='')
db.session.commit()
#app.route('/')
#login_required
def home():
#password = encrypt_password('mypass')
#print verify_and_update_password('mypass', password)
return "hello"
if __name__ == '__main__':
app.run(debug=True, use_reloader=False)
** I would really appreciate any guidance!**
Project structure:
If you're planning to build a larger Flask application, you should consider decomposing the functionality into Blueprints.
The official Flask documentation has a tutorial on how to structure larger applications:
http://flask.pocoo.org/docs/0.12/patterns/packages/
Also, take a look at the Hitchhiker's guide to organizing your project. It has some very good points: http://python-guide-pt-br.readthedocs.io/en/latest/writing/structure/
If you're designing an REST API consider using Flask-RESTful (which also works nicely with Blueprints)
ya'll, i figured it out & my app is LOOKING GOOD :)I am using blueprints & an application factory pattern.
APP
|_runserver.py
|_/app
|---__init__.py
|---config.py
|---extensions.py
|---forms.py
|---models.py
|---/login_dashboard #blueprint
|------__init__.py
|------views.py
|------/templates
|---------base.html
.
.
|------/static
|-----------/css
.
.
|-----------/js
.
.
|-----------/img
.
.
I have a Rails 3.2.13 Application to maintenance.
Because of authorization rules i want to limit the find(params[:file_registry_id]) method to accept all parameters except 752. (Only user tehen should be able to get it.)
def show
if current_user.tehen?
#file_registry = FileRegistry.find(752)
else
#file_registry = FileRegistry.find(params[:file_registry_id])
end
#rubric = Rubric.find(params[:id])
#rubrics = expanded_rubrics #rubric.ancestors_with_self.collect(&:id)
set_favorites
render :action => 'index'
end
Is there a method available to filter an element (here id 752) from the params hash? Or what's the best way to go?
Simple solution:
def show
#file_registry = get_file_registry
#....
end
private
def get_file_registry
if current_user.tehen?
FileRegistry.find(752)
else
unless params[:file_registry_id] == FORBIDDEN_ID_FOR_GUEST
FileRegistry.find(params[:file_registry_id])
else
false
end
end
end
FORBIDDEN_ID_FOR_GUEST should be defined outside of the controller, for example inside of a initializer.
But I suggest to use a authorization library like CanCan (https://github.com/ryanb/cancan) where you can define permissions for every use case.
I have a recurrent code with conditions in a new permission recently added code. It was not designed from the start, so it is a little messy:
#usuarios = Usuario.menores_de_edad.con_autorizacion(params[:autorizacion]).con_nombre(params[:nombre])
# master puede ver todos, asà que ignora los permisos
if !usuario_actual.es_master?
if usuario_actual.permiso.blank?
# Si es admin y no tiene permisos establecidos
#usuarios = Usuario.where(id: nil)
else
# Lee de que niveles puedes mostrar los usuarios
#usuarios = #usuarios.del_nivel(usuario_actual.permiso.niveles)
end
end
if usuario_actual.es_admin_occ?
#usuarios = #usuarios.de_occ
end
I want to make it a scope this way:
#usuarios = Usuario.menores_de_edad.con_autorizacion(params[:autorizacion]).con_nombre(params[:nombre])
#usuarios = #usuarios.permitibles(usuario_actual)
How can I make it work? I have currently this:
class Usuario < ActiveRecord::Base
scope :permitibles, lambda{ |usuario_actual|
# master can see everything, so, don't scope anything at all
if !usuario_actual.es_master?
if usuario_actual.permiso.blank?
# return nothing if you don't have permissions
where(id: nil)
else
# this is a scope
del_nivel(usuario_actual.permiso.niveles)
end
if usuario_actual.es_admin_occ?
# this is a scope
de_occ
end
end
}
end
The problem is, inside the scope, that I don't know how to chain the other scopes, I mean, "de_occ" must be chained depending on the other condition, but right now this will will not work as is, because it will return only one scope instead of chaining "de_occ" to the first condition. How can I achieve that?
Thanks!
Ok, it's little problematic for me to go through your code. The best practice is not to use you mother langauge as a source of model and method names.
The basic approach for your problem would be to move the logic into a method and chains scopes / sqls. Here is a proof of concept:
scope :menores_de_edad, ->() {where(something: true)}
scope :del_nivel, ->(params) {where(field: param)}
scope :de_occ, ->() { ... }
def self.we_are_chaining_scopes
result = self.scoped
if !usuario_actual.es_master?
if usuario_actual.permiso.blank?
# return nothing if you don't have permissions
result = result.menores_de_edad
else
# this is a scope
result = result.del_nivel(usuario_actual.permiso.niveles)
end
if usuario_actual.es_admin_occ?
# this is a scope
result = result.de_occ
end
end
result
end
You can make it even nicer by creating a service object for this query. Rough example:
class SpecificQuery
def initialize(relation = Model.scoped)
#relation = relation
end
private
def scoped_to(&block)
#relation = #relation.instance_eval(&block)
end
def scoped_behavior_method_one(param)
scoped_to { where(:something: param) }
end
def scoped_behavior_method_two(param)
scoped_to { ... }
end
end
And then you can chain scopes nicely as well + you're encapsulating the logic which should not necessarily belongs to your model. Also, it's easier to write specs :) I hope that helps.
I am pretty new to rails and I am trying to create a callback that will apply user information before a record is saved.
Here is the callback:
def add_resolution_name
if self.res_desc_changed?
self.res_provided_name = current_user.first_name
elsif self.res_desc_changed? && self.res_approved?
self.res_provided_name = current_user.first_name
self.res_approved_name = current_user.first_name
elsif self.res_approved_changed? && self.res_approved?
self.res_approved_name = current_user.first_name
elsif self.res_approved_changed? && !self.res_approved?
self.res_approved_name = nil
end
save
logger.info "pocessed resolution information... #{current_user.first_name}"
end
As you can see it's pretty ugly and I don't have access to the current_user inside the ticket model. Should I put this into a presenter or service? Any tips appreciated.
In rails, there's class called ActiveRecord::Observer which allows you to add some event listeners to your own model e.g. before_save, after_save. Have a look here http://api.rubyonrails.org/classes/ActiveRecord/Observer.html
For example
class UserObserver < ActiveRecord::Observer
def before_save(comment)
# your code for add resolution here
end
end
Related to my previous question I still have one thing that I would like to understand - why this:
= link_to(root_path)
= link_to(#some_path_set_in_mailer)
works in development mode (config.action_mailer.perform_deliveries was set to true and emails were actually sent) and in production or staging has to be changed to:
= link_to(#some_path_set_in_mailer, #some_path_set_in_mailer)
to avoid "No route matches {}" error?
I had this problem in rails 3.2.
I'm not entirely sure why there is a difference since there shouldn't be.
However, link_to typically has this format:
= link_to("Some link description here", root_path)
The only time you typically leave off the link description text is if you have a longer description that you need to put within a do block like this:
= link_to(root_path) do
%p Some link description here
= image_tag("some_image.jpg")
So I would recommend sticking to the preferred syntaxes above.
The docs for link_to don't really talk about the short-hand method you're using too much.
Here is the source for it:
# File actionpack/lib/action_view/helpers/url_helper.rb, line 231
def link_to(*args, &block)
if block_given?
options = args.first || {}
html_options = args.second
link_to(capture(&block), options, html_options)
else
name = args[0]
# this is probably where your issue is coming from
options = args[1] || {}
html_options = args[2]
html_options = convert_options_to_data_attributes(options, html_options)
url = url_for(options)
href = html_options['href']
tag_options = tag_options(html_options)
href_attr = "href=\"#{ERB::Util.html_escape(url)}\"" unless href
"<a #{href_attr}#{tag_options}>#{ERB::Util.html_escape(name || url)}</a>".html_safe
end
end