I'm Trying to authenticate user using spring security with Cassandra database. I'm getting following exception:
Authentication Failed: PreparedStatementCallback; bad SQL grammar [select userName, password from user where userName=?];
nested exception is java.sql.SQLSyntaxErrorException:
No indexed columns present in by-columns clause with "equals" operator 'select userName, password from user where userName=?'
Here is configuration details:
security-config.xml
<form-login
login-processing-url="/j_spring_security_check"
login-page="/index.xhtml" always-use-default-target="true"
authentication-failure-url="/index.xhtml?error=true"
authentication-success-handler-ref="authenticationSuccessHandler"
authentication-failure-handler-ref="authenticationFailureHandler"/>
<authentication-manager alias="authenticationManager">
<authentication-provider>
<jdbc-user-service data-source-ref="cassandraDataSource" users-by-username-query="select userName, password from user where userName=?"
authorities-by-username-query="select roleId, userName, roleName from role where userName=?"/>
</authentication-provider>
</authentication-manager>
and I created the index with cql command:
CREATE INDEX userName_index ON role (userName);
CREATE INDEX userNameUser_index ON user (userName);
result of desc mykeyspaces
CREATE TABLE role (
roleid int PRIMARY KEY,
rolename text,
username text
) WITH bloom_filter_fp_chance = 0.01
AND caching = '{"keys":"ALL", "rows_per_partition":"NONE"}'
AND comment = ''
AND compaction = {'min_threshold': '4', 'class': 'org.apache.cassandra.db.compaction.SizeTieredCompactionStrategy', 'max_threshold': '32'}
AND compression = {'sstable_compression': 'org.apache.cassandra.io.compress.LZ4Compressor'}
AND dclocal_read_repair_chance = 0.1
AND default_time_to_live = 0
AND gc_grace_seconds = 864000
AND max_index_interval = 2048
AND memtable_flush_period_in_ms = 0
AND min_index_interval = 128
AND read_repair_chance = 0.0
AND speculative_retry = '99.0PERCENTILE';
CREATE INDEX userName_index ON role (username);
CREATE TABLE user (
userid int PRIMARY KEY,
email text,
firstname text,
lastname text,
password text,
phone int,
username text
) WITH bloom_filter_fp_chance = 0.01
AND caching = '{"keys":"ALL", "rows_per_partition":"NONE"}'
AND comment = ''
AND compaction = {'min_threshold': '4', 'class': 'org.apache.cassandra.db.compaction.SizeTieredCompactionStrategy', 'max_threshold': '32'}
AND compression = {'sstable_compression': 'org.apache.cassandra.io.compress.LZ4Compressor'}
AND dclocal_read_repair_chance = 0.1
AND default_time_to_live = 0
AND gc_grace_seconds = 864000
AND max_index_interval = 2048
AND memtable_flush_period_in_ms = 0
AND min_index_interval = 128
AND read_repair_chance = 0.0
AND speculative_retry = '99.0PERCENTILE';
CREATE INDEX userNameUser_index ON user (username);
cqlsh:system>
If you are using user and role tables for security, username should be a partition key on both of those tables. Just indexing them is going to cause querying multiple nodes and not going to be effective. The data model below will be more effective for your queries.
CREATE TABLE user (
userid int,
email text,
firstname text,
lastname text,
password text,
phone int,
username text,
PRIMARY KEY ((username), userid)
)
CREATE TABLE role (
roleid int,
rolename text,
username text,
PRIMARY KEY ((username), roleid)
)
As you can see, primary key consists of 2 columns, to insure uniqueness, but only the first one is a partition key. This insures that all records for that user will be on one node and queries by user name will be effective.
There is a lot of info available about data modeling for Cassandra which differs a lot from data modeling for relational database. The basic rules are:
Understand partition and cluster keys
Model to your queries, not data
Indexes are not efficient
Related
this is the code for my domains.
class Btr {
Date dateBreak
int timeBreak
String typeBreak
User usuario
static constraints = {
}
static mapping = {
}
}
class User {
String name
String user
String password
String confirmPassword
String state
String extent
String movileNumber
String email
String address
Rol rol
static constraints = {
}
static mapping = {
}
}
This is the code for my controller.
def df = new SimpleDateFormat("yyyy-MM-dd HH:mm")
def startDate = params.startDate
def stopDate = params.stopDate
resultado = Btr .executeQuery("select dateBreak, timeBreak, typeBreak,
user, usuario.rol from Btr inner join User on user = usuario.rol where
dateBreak between :startDate" and :stopDate", [startDate:
df.parse(startDate), stopDate: df.parse(stopDate)])
render (view: "data", model: [result: resultado])
This is my view.
<g:each in="${result}" var="results" status="i">
<tr><td>{results.dateBreak}</td><td>{results.timeBreak}</td><td>
{results.typeBreak} </td><td>${results.usuario.rol}</td></tr>
</g:each>
Then i get this error when i submit the form.
in the GSP, when i am printing data,
Exception evaluating property 'dateBreak' for java.util.Arrays$ArrayList, Reason: groovy.lang.MissingPropertyException: No such property: dateBreak for class: java.sql.Timestamp
could someone please tell me how to join tables in grails with executeQuery and also would be nice to learn to do it with, withCriteria
resultado = Btr .executeQuery("select dateBreak, timeBreak, typeBreak,
user, usuario.rol from Btr inner join User on user = usuario.rol where
dateBreak between :startDate" and :stopDate", [startDate:
df.parse(startDate), stopDate: df.parse(stopDate)])
Should be
resultado = Btr .executeQuery("""select new map (btr.dateBreak as dateBreak, btr.timeBreak as timeBreak, btr.typeBreak as typeBreak,
u as user, user.usuario.rol as rol) from Btr btr join btr.user u where
btr.dateBreak between :startDate and :stopDate""", [startDate:
df.parse(startDate), stopDate: df.parse(stopDate)])
what you have is raw sql and not HQL which is a slight variation and uses actual domain objects to join
Use left join for hasMany where it may be null join for typical one to one relationship
Also use left join if one to one relationship can be null
Beyond that you could have put your actual query as a raw sql query like so
def sql=new Sql(dataSource)
return sql.rows(query,whereParams)
Given the following tables:
SCHOOL
schoolid (int PK)
name
TEACHER
teacherId (int PK)
name, homeRoomId (fk varchar10)
subjectId (fk varchar10)
schoolid (FK int)
HOMEROOM
homeRoomId (PK varchar10)
roomNumber
active
SUBJECT
subjectId (PK varchar10)
name
active
I am using EF6 in an MVC app. I have lazy loading enabled. I am trying to return a list of all teachers for a given SchoolId and I need to include homeroom and subject data for each teacher.
A school contains many teachers, a teacher works for only one school, a teacher has only one homeroom and teachs only one subject. The homeroom and subject ids are varchars because they are pre-existing ids and look like: SUBJECT: A03, Math.
My code to load all teachers with homeroom and subject for a single schoolid:
public List<TeacherModel> GetTeachersBySchool(int schoolId)
{
List<TeacherModel> teachers = new List<TeacherModel>();
using (var db = new myDBEntities())
{
var list = db.Teacher.Where(a => a.SchoolId == schoolId).ToList();
foreach ( var s in list)
{
TeacherModel teacher = new TeacherModel()
{
TeacherId = s.TeacherId,
Name = s.Name,
HomeRoomId = s.HomeRoomId,
HomeRoomNumber = s.HomeRoom.RoomNumber,
SubjectId = s.SubjectId,
SubjectName = s.Subject.Name
};
teachers.Add(teacher);
}
return teachers;
}
}
The homeroom entity is loading but the Subject entity is null even through a sql query in the database returns one row for this teacher. Due to the null Subject entity, the query errors out as object reference not set to blah blah.
I have found that the problem seems to be when the SubjectId contains alpha characters. A couple examples of a subjectid are: "A03" or "1001023". The second entity will load, the first will not. I assume that even though the datatype is string/varchar EF6 is pulling out the numeric values and passing those as the id, so if the ID has alphas, it fails.
Does this jibe? How do I fix it? As a last resort I can add a surrogate key (INT Identity 1,1) for use with these entities but I'm hoping there is another way.
I'm using Entity Framework 4.1
I have a "DomainEntities" table that holds the common info for all my domain entities.
I have a users table the the UserID is a Foreign Key from "DomainEntities".
see EDMX:
When I run the following code i get an error:
Unable to determine a valid ordering for dependent operations.
Dependencies may exist due to foreign key constraints, model
requirements, or store-generated values.
The code:
static void addUserTest()
{
DomainEntity userToAdd = new DomainEntity()
{
EntityName = "Test User",
EntityTypeID = DomainEntity.eEntityType.User,
EntityCreationDate = new DateTime(),
EntityLastUpdateDate = new DateTime(),
EntityCreatorUserID = 0,
EntityUpdaterUserID = 0,
EntityParentID = null,
UserDetails = new User()
{
Username = "TestUser",
Password = "123",
FirstName = "Test",
LastName = "User"
}
};
using (var context = new CamelotDB())
{
context.DomainEntities.Add(userToAdd);
context.SaveChanges();
}
}
I cant understand what is the reason that EF can understand what is the INSERT order required,
It should be One record into "DomainEntities" and then one record into "Users".
What am I doing wrong ?
After searching for one more day I found it the problem was with the Creator and Updater self referenced foreign keys.
CreatorID is not Nullable so does UpdaterID and this is why EF requires the navigation properties to point to actual entities from the database so i added the following lines in the initializer of Test User.
EntityCreatorUserID = 0,
Creator = context.DomainEntities.Find(0),
EntityUpdaterUserID = 0,
Updater = context.DomainEntities.Find(0),
It seems that instead of having your User be related to your DomainEntity, you should make your User a subclass of DomainEntity. In the Entity Model designer, this is done by using the Inheritance tool (Double-click the Inheritance tool in the toolbox, then click once on the parent entity and once on the child entity.)
This more accurately describes the nature of a User; a User is a DomainEntity. Your current model, suggests that a User is related to a DomainEntity, which doesn't seem right.
I woud like to inquire if my Linq solution below is a good solution or if there is a better way. I am new to using Linq, and am most familiar with MySQL. So I've been converting one of my past projects from PHP to .NET MVC and am trying to learn Linq. I would like to find out if there is a better solution than the one I came up with.
I have the following table structures:
CREATE TABLE maplocations (
ID int NOT NULL AUTO_INCREMENT,
name varchar(35) NOT NULL,
Lat double NOT NULL,
Lng double NOT NULL,
PRIMARY KEY (ID),
UNIQUE KEY name (name)
);
CREATE TABLE reservations (
ID INT NOT NULL AUTO_INCREMENT,
loc_ID INT NOT NULL,
resDate DATE NOT NULL,
user_ID INT NOT NULL,
PRIMARY KEY (ID),
UNIQUE KEY one_per (loc_ID, resDate),
FOREIGN KEY (user_ID) REFERENCES Users (ID),
FOREIGN KEY (loc_ID) REFERENCES MapLocations (ID)
);
CREATE TABLE Users (
ID INT NOT NULL AUTO_INCREMENT,
name VARCHAR(20) NOT NULL,
email VARCHAR(50) NOT NULL,
pass VARCHAR(128) NOT NULL,
salt VARCHAR(5) NOT NULL,
PRIMARY KEY (ID),
UNIQUE KEY unique_names (name),
UNIQUE KEY unique_email (email)
);
In MySQL, I use the following query to get the ealiest reservation at each maplocation with a non null date for any locations that don't have a reservation.
SELECT locs.*, if(res.resDate,res.resDate,'0001-01-01') as resDate, res.Name as User
FROM MapLocations locs
LEFT JOIN (
SELECT loc_ID, resDate, Name
FROM Reservations, Users
WHERE resDate >= Date(Now())
AND user_ID = Users.ID
ORDER BY resDate
) res on locs.ID = res.loc_ID
group by locs.ID
ORDER BY locs.Name;
In Linq, with Visual studio automatically creating much of the structure after connecting to the database, I have come up with the following equivalent to that SQL Query
var resList = (from res in Reservations
where res.ResDate >= DateTime.Today
select res);
var locAndRes =
(from loc in Maplocations
join res in resList on loc.ID equals res.Loc_ID into join1
from res2 in join1.DefaultIfEmpty()
join usr in Users on res2.User_ID equals usr.ID into join2
from usr2 in join2.DefaultIfEmpty()
orderby loc.ID,res2.ResDate
select new {
ID = (int)loc.ID,
Name = (string)loc.Name,
Lat = (double)loc.Lat,
Lng = (double)loc.Lng,
resDate = res2 != null ?(DateTime)res2.ResDate : DateTime.MinValue,
user = usr2 != null ? usr2.Name : null
}).GroupBy(a => a.ID).Select(b => b.FirstOrDefault());
So, I'm wondering is there a better way to perform this query?
Are these equivalent?
Are there any good practices I should be following?
Also, one more question, I'm having trouble getting this from the var to a List. doing something like this doesn't work
List<locAndResModel> locList = locAndRes.AsQueryable().ToList<locAndResModel>();
In the above snippet locAndResModel is just a class which has variables to match the int, string, double double, DateTime, string results of the query. Is there an easy way to get a list without having to do a foreach and passing the results to a constructor override? Or should I just add it to ViewData and return the View?
You'll want to take advantage of the automatic joins performed by the Entity Framework. Give this a try and let me know if it does what you want:
var locAndRes = from maplocation in MapLocations
let earliestReservationDate = maplocation.Reservations.Min(res => res.resDate)
let earliestReservation = (from reservation in mapLocation.Reservations
where reservation.resDate == earliestReservationDate && reservation.resDate >= DateTime.Today
select reservation).FirstOrDefault()
select new locAndResModel( maplocation.ID, maplocation.name, maplocation.Lat, maplocation.Lng, earliestReservation != null ? earliestReservation.resDate : DateTime.MinValue, earliestReservation != null ?earliestReservation.User.name : null)
I'm wondering if there is a possibility when you upload your highscore you can compare your score with the one of your friends (if simpler, only selected contacts)?
And if so, could someone point me in the right direction, how to do it? I did not find anything useful about this on google.
As far as I pressume it should be possible, because apps like WhatsApp also let you choose specific contacts you want to send a message.
Related to that: Can I just use a/the cloud for uploading highscore or should I use my webspace?
I am not answering this specific to iOS/etc.
What you would typically do is expose a REST (or POX/POJSON - plain old XML or plain old JSON) service on your website that your application communicates with - it would be responsible for negotiating friendships, uploading high scores and retrieving high scores. This would either hit a database under your control or it would connect to a cloud server; there is no problem with either approach (Azure is a good option if you want to apply my SQL concepts).
Inside your database you would maintain a list of friends - this is a very simple structure to set up. Essentially you want two tables that look like the following:
CREATE TABLE [UserAccount]
(
[ID] BIGINT IDENTITY(1,1) PRIMARY KEY,
[Name] NVARCHAR(255),
)
CREATE TABLE [Friendship]
(
[User1] BIGINT, -- Primary key, FK to [UserAccount].[ID]
[User2] BIGINT, -- Primary key, FK to [UserAccount].[ID]
)
This would allow you to indicate friendships along the lines of:
User: ID = 1, Name = Joe
User: ID = 2, Name = Fred
Friendship: User1 = 1, User2 = 2
Friendship: User1 = 2, User1 = 1
You can then query friends using the following query:
SELECT [F1].[User2] AS [ID] FROM [Friendship] AS [F1]
WHERE [F1].[User1] = #CurrentUser
-- Check for symmetric relationship.
AND EXISTS
( SELECT 1 FROM [Friendship] AS [F2]
WHERE [F2].[User2] = [F1].[User1] AND [F2].[User1] = [F1].[User2] );
You could make that a TVF (Table Value Function) if your SQL variant supports them. Next you would create a high score table and a table to map it to users.
CREATE TABLE [Highscore]
{
[ID] BIGINT IDENTITY(1,1) PRIMARY KEY,
[Score] INT,
}
CREATE TABLE [UserHighscore]
{
[UserID] BIGINT, -- Primary key, FK to User.ID
[HighscoreID] BIGINT, -- Primary key, FK to Highscore.ID
}
Some sample data for this would be:
-- In this game you can only score over 9000!
Highscore: ID = 1, Score = 9001
Highscore: ID = 2, Score = 9005
Highscore: ID = 3, Score = 9008
UserHighscore: UserID = 1, HighscoreID = 1
UserHighscore: UserID = 1, HighscoreID = 2
UserHighscore: UserID = 2, HighscoreID = 3
You can then query for your friends' highscores:
SELECT TOP(10) [U].[Name], [H].[Score] FROM [Friendship] AS [F1]
LEFT INNER JOIN [User] AS [U] ON [U].[ID] = [F1].[User2]
LEFT INNER JOIN [HighscoreUser] AS [HU] ON [HU].[UserID] = [F1].[User2]
LEFT INNER JOIN [Highscore] AS [H] ON [H].[ID] = [HU].[UserID]
WHERE [F1].[User1] = #CurrentUser
-- Check for symmetric relationship.
AND EXISTS
( SELECT 1 FROM [Friendship] AS [F2]
WHERE [F2].[User2] = [F1].[User1] AND [F2].[User1] = [F1].[User2] )
ORDER BY [H].[Score] DESC;
That query would give the top 10 score your friends; giving you their name and score.