Multiple individual users on one database - mysql

I have a .sql database with which i interact using Django .
The database in the beginning is filled with public data that can be accessed from anynone.
Multiple individual users can add rows into a table(private data).
How can a user see only the changes he made in the database(private data)?

I assume you're using django.contrib.auth. You just need to do something like:
from django.contrib.auth.models import User
# ...
class PrivateData(models.Model):
# ... private data fields ...
user = models.ForeignKey(User)
Then you can get just that user's fields with:
PrivateData.objects.filter(user=request.user)
EDIT: So, if your users are just IP addresses, and you're not using a login mechanism, you don't really need django.contrib.auth... though it's good to have anyway since you can use it to authenticate yourself and use the built-in admin stuff to manage your site.
If you just want to tie data to IP addresses, set up an IPUser model:
class IPUser(models.Model):
address = models.CharField(max_length=64, unique=True) # Big enough for IPv6
# Add whatever other discrete (not list) data you want to store with this address.
class PrivateData(models.Model):
# ... private data fields ...
user = models.ForeignKey(IPUser)
The view function looks something like:
def the_view(request):
remoteAddr = request.META['REMOTE_ADDR']
try:
theUser = IPUser.objects.get(address=remoteAddr)
except IPUser.DoesNotExist:
theUser = IPUser.objects.create(address=remoteAddr)
userModifiedData = PrivateData.objects.filter(user=theUser)
One thing to note: when you're testing this with manage.py runserver, you'll need to specify the IP address via environment variable:
$ REMOTE_ADDR=127.0.0.1 manage.py runserver
When you use Django with a real web server like Apache, the server will set the variable for you.
There are probably several ways to optimize this, but this should get you started.

I'm assuming that users have to log into this application. If yes, add a column to every table for the username. Add WHERE username = ? to every query so they can see only their data.
For data manipulation requests, make sure that the username matches the value for every row; forbid the operation if not true.

Related

Laravel Eloquent is not saving properties to database ( possibly mysql )

I'm having a strange issue.
I created a model observer for my user model. The model observer is being run at 'saving'. when I dump the object at the very end of the user model to be displayed ( this is just before it saves.. according to laravel docs ) it displays all the attributes set correctly for the object, I've even seen an error that showed the correct attributes as set and being inserted into my database table. However, after the save has been completed and I query the database, two of the fields are not saved into the table.
There is no code written by myself sitting between the point where I dumped the attributes to check that they had been set and the save operation to the database. so I have no idea what could be causing this to happen. All the names are set correctly, and like I said, the attributes show as being inserted into the database, they just never end up being saved, I receive no error messages and only two out of ten attributes aren't being saved.
In my searches I have found many posts detailing that the $fillable property should be set, or issues relating to a problem with variables being misnamed or unset, however because I already have the specific attributes not being saved specified in the $fillable array, on top of the fact that they print out exactly as expected pre save, I don't believe those issues are related to the problem I am experiencing.
to save I'm calling:
User::create(Input::all());
and then the observer that handles the data looks like this:
class UserObserver {
# a common key between the city and state tables, helps to identify correct city
$statefp = State::where('id',$user->state_id)->pluck('statefp');
# trailing zeros is a function that takes the first parameter and adds zeros to make sure
# that in this case for example, the dates will be two characters with a trailing zero,
# based on the number specified in the second parameter
$user->birth_date = $user->year.'-'.$user->trailingZeros( $user->month, 2 ).'-'.$user->trailingZeros( $user->day, 2 );
if(empty($user->city)){
$user->city_id = $user->defaultCity;
}
$user->city_id = City::where( 'statefp', $statefp )->where('name', ucfirst($user->city_id))->pluck('id');
# if the user input zip code is different then suggested zip code then find location data
# on the input zip code input by the user from the geocodes table
if( $user->zip !== $user->defaultZip ){
$latlon = Geocode::where('zip', $user->zip)->first();
$user->latitude = $latlon['latitude'];
$user->longitude = $latlon['longitude'];
}
unset($user->day);
unset($user->month);
unset($user->year);
unset($user->defaultZip);
unset($user->defaultCity);
}
that is the code for the two values that aren't being set, when I run
dd($user);
all the variables are set correctly, and show up in the mysql insert attempt screen with correct values, but they do not persist past that point.. it seems to me that possibly mysql is rejecting the values for the city_id and the birth_date. However, I cannot understand why, or whether it is a problem with Laravel or mysql.
since I was calling
User::create();
I figured I'd try to have my observer listen to:
creating();
I'm not sure why it only effected the date and city variables, but changing the function to listen at creating() instead of saving() seems to have solved my problem.

How to create Delegation Signer (DS) record for a subdomain with PowerDNS?

I have a domain mydomain.com. The DNS servers for this domain are under my control and I'm running PowerDNS there. I've set up DNSSEC for my main domain mydomain.com and I've registered the keys at registrar. Everything works fine for the main domain. However when I create subdomains (zones) with PowerAdmin GUI then I get the following error for my subdomains from DNSSEC analysis tool at http://dnssec-debugger.verisignlabs.com/:
No DS records found for subdomain.mydomain.com in the mydomain.com zone
Question: how to add that DS record to mydomain.com zone with powerdns?
Note: I haven't used PowerAdmin, so this is a PowerDNS answer.
Assuming you have also signed these subdomains/zones, you would use pdnssec show-zone to see the DS records for them. Then, insert these DS records into the parent zone (mydomain.com), with fields set like this:
domain_id = id of domain.com
name = sub.mydomain.com
type = DS
content = copy/paste from pdnssec show-zone
You also need to insert NS records (as above, but with type=NS and content=name of your server), to get a valid delegation, even if it is local!
If you don't actually care about DNSSEC for the subdomains, just insert the NS records and leave out the DS. This will tell validators that the subdomain is deliberately insecure.

Anyway to get dkims records for verifying ses domain in boto?

Tinkering around with verifying a couple of domains and found the manual process rather tedius. My DNS controller offers API access so I figured why not script the whole thing.
Trick is I can't figure out how to access the required TXT & CNAME records for DKIMS verification from boto, when I punch in
dkims = conn.verify_domain_dkim('DOMAIN.COM')
it adds DOMAIN.COM to the list of domains pending verification but doesn't provide the needed records, the returned value of dkims is
{'VerifyDomainDkimResponse': {
'ResponseMetadata': {'RequestId': 'REQUEST_ID_STRING'},
'VerifyDomainDkimResult': {'DkimTokens': {
'member': 'DKIMS_TOKEN_STRING'}}}}
Is there some undocumented way to take the REQUEST_ID or TOKEN_STRING to pull up these records?
UPDATE
If you have an aws account you can see the records I'm after at
https://console.aws.amazon.com/ses/home?region=us-west-2#verified-senders:domain
tab: Details:: Record Type: TXT (Text)
tab: DKIM:: DNS Record 1, 2, 3
these are the records required to add to the DNS controller to validate & allow DKIM signatures to take place
This is how I do it with python.
DOMINIO = 'mydomain.com'
from boto3 import Session
session = Session(
aws_access_key_id=MY_AWS_ACCESS_KEY_ID,
aws_secret_access_key=MY_AWS_SECRET_ACCESS_KEY,
region_name=MY_AWS_REGION_NAME)
client = session.client('ses')
# gets VerificationToken for the domain, that will be used to add a TXT record to the DNS
result = client.verify_domain_identity(Domain=DOMINIO)
txt = result.get('VerificationToken')
# gets DKIM tokens that will be used to add 3 CNAME records
result = client.verify_domain_dkim(Domain=DOMINIO)
dkim_tokens = result.get('DkimTokens') # this is a list
At the end of the code, you will have "txt" and "dkim_tokens" variables, a string and a list respectively.
You will need to add a TXT record to your dns, where the host name is "_amazonses" and the value is the value of "txt" variable.
Also you will need to add 3 CNAME records to your dns, one for each token present in "dkim_tokens" list, where the host name of each record is of the form of [dkimtoken]._domainkey and the target is [dkimtoken].dkim.amazonses.com
After adding the dns records, after some minutes (maybe a couple of hours), Amazon will detect and verify the domain, and will send you an email notification. After that, you can enable Dkim signature by doing this call:
client.set_identity_dkim_enabled(Identity=DOMINIO, DkimEnabled=True)
The methods used here are verify_domain_identity, verify_domain_dkim and set_identity_dkim_enabled.
You may also want to take a look a get_identity_verification_attributes and get_identity_dkim_attributes.
I think the get_identity_dkim_attributes method will return the information you are looking for. You pass in the domain name(s) you are interested in and it returns the status for that identity as well as the DKIM tokens.

Can I create sperate queries for different views?

I'm learning sqlalchemy and not sure if I grasp it fully yet(I'm more used to writing queries by hand but I like the idea of abstracting the queries and getting objects). I'm going through the tutorial and trying to apply it to my code and ran into this part when defining a model:
def __repr__(self):
return "<User('%s','%s', '%s')>" % (self.name, self.fullname, self.password)
Its useful because I can just search for a username and get only the info about the user that I want but is there a way to either have multiple of these type of views that I can call? or am I using it wrong and should be writing a specific query for getting different data for different views?
Some context to why I'm asking my site has different templates, and most pages will just need the usersname, first/last name but some pages will require things like twitter or Facebook urls(also fields in the model).
First of all, __repr__ is not a view, so if you have a simple model User with defined columns, and you query for a User, all the columns will get loaded from the database, and not only those used in __repr__.
Lets take model Book (from the example refered to later) as a basis:
class Book(Base):
book_id = Column(Integer, primary_key=True)
title = Column(String(200), nullable=False)
summary = Column(String(2000))
excerpt = Column(Text)
photo = Column(Binary)
The first option to skip loading some columns is to use Deferred Column Loading:
class Book(Base):
# ...
excerpt = deferred(Column(Text))
photo = deferred(Column(Binary))
In this case when you execute query session.query(Book).get(1), the photo and excerpt columns will not be loaded until accessed from the code, at which point another query against the database will be executed to load the missing data.
But if you know before you query for the Book that you need the column photo immediately, you can still override the deferred behavior with undefer option: query = session.query(Book).options(undefer('photo')).get(1).
Basically, the suggestion here is to defer all the columns (in your case: except username, password etc) and in each use case (view) override with undefer those you know you need for that particular view. Please also see the group parameter of deferred, so that you can group the attributes by use case (view).
Another way would be to query only some columns, but in this case you are getting the tuple instance instead of the model instance (in your case User), so it is potentially OK for form filling, but not so good for model validation: session.query(Book.id, Book.title).all()

sqlalchemy query issue

I have a user update function and I allow users to change their email address but the same address must be unique in the database so, before I update, I must check if their new email already exists in the database but the query I use to check that returns the same row. Example:
user = User.query.get(1)
user.email = 'some#email.com'
if user.validate(): # The validate function performs a query to see if 'some#email.com' is already taken
user.save()
Now going into the validate function I have:
check = User.query.filter_by(User.email='some#email.com').first()
if check:
# email already exists
Problem is that check holds the same user I'm editing. Sqlalchemy submits the update to the database but under some sort of transaction so my query returns the same user I'm editing. I've solved this by creating a second session object but seems like an overkill. Any better ideas? Am I making sense?
Why not check, whether a user with a given e-mail address exists, before manipulating an existing user? You could e.g. write a standalone function for that:
def user_email_exists(email):
return (not User.query.filter(User.email=email) == None)
Then call user_email_exists before the attempt to change the user object.
...
# User object to alter
user = ...
# the new email address, which needs to be checked
new_email_addr = 'new#shiny.com'
if user_email_exists(new_email_addr):
raise SomeMeaningfulException() # or some `flash` message + a redirect
else:
user.email = new_email_addr
db.session.add(user)
db.commit()
...
The simplest way is probably just to add a second filter to the query inside your validate function. Instead of having it select any user with that email address, have it select any user with that email address and NOT the same username or user id. That way you're ensuring to only get a return if there is a different user with that email address.