I want to authenticate an user by its nickname (networkId) and before-hand hashed password, with an user public table (recoyx.user) and an user private table (recoyx_private.user). The function is based on browsing this PostGraphile tutorial (PostGraphile combines GraphQL and PostgreSQL).
create function recoyx.authenticate(
network_id text,
password_hash text
) returns recoyx.jwt_token
begin;
set local id to (select (numeric_id) from recoyx.user where user.network_id = $1).numeric_id;
select (numeric_id, password_hash)::recoyx.jwt_token
from recoyx_private.user
where user.numeric_id = id and user.password_hash = $2;
end;
The query runner is giving invalid syntax within this function overall, both at the part like select * from recoyx.table where table.field = value, transaction frames and the id binding. I took the query runner from this example which gives a short
facility for initializing, querying and releasing the query runner for the PostgreSQL database (I got here through this postgraphile module API documentation).
When I eliminate this function from my query, it runs fine. As far as I've just seen the dot is valid, and the local assignment too. So is my syntax really wrong?
Update
Now this is my function:
create function recoyx.authenticate(
network_id text,
password_hash text
) returns recoyx.jwt_token
as
$body$
select (numeric_id, password_hash)::recoyx.jwt_token
from recoyx_private.user
where numeric_id = (select numeric_id from recoyx.user where network_id = $1)
and password_hash = $2;
$body$
language sql
stable;
I'm getting undefined relations, but I'm connecting to the default root role that comes within my PostgreSQL installation (postgres role) as I run the create function query
I've put the project on GitHub. I'm running the query through npm run init-database. See environment-example.json (it specifies the conventional "postgres" role).
As documented in the manual the function body is passed as a string in Postgres (and the tutorial you linked to actually included the necessary as $$ ...$$ - you just didn't copy it). You also forgot to specify the function's language.
set local id is neither a valid variable assignment in PL/pgSQL nor in SQL (which doesn't have variables to begin with).
But you don't really need a variable to do what you want, your function can be implemented as a SQL function:
create function recoyx.authenticate(
network_id text,
password_hash text
) returns recoyx.jwt_token
as
$body$
select (numeric_id, password_hash)::recoyx.jwt_token
from recoyx_private.user
where user.numeric_id = (select numeric_id
from recoyx.user
where network_id = $1)
and user.password_hash = $2;
$body$
language sql
stable;
Related
I have a query deinged in hyperledger composer queries.qry file.
query checkOwnerUniqueness{
description: "Select owner with given identifier"
statement:
SELECT org.acme.participant.Owner
WHERE (idNum == _$idNum)
}
Now My query works if id number is abc123 and user search for abc123 but it fails if user pass on value as ABC123. However I would like query to respond with owner whether characters are entered in lower case or upper case.
I have already tried general SQL API's i.e. LOWER() and UPPER() but seems they doesn't work in the Hyperleger composer Query language. SO some can please help me understand on how to do this in Hyperledger Composer query file.
I would first suggest that IDs are inserted in the first place (ie programatically) with the same case. Composer simply passes on the query to CouchDB's query language FYI and honours the case sensitivity at which the data was entered.
Are you aware that you can use regex to validate the ID field (see Modeling language docs here ? - seems quite an important field to me (as opposed to say a surname field).
You can use otherwise buildQuery function (eg. with one parameter still) in your function - or if you insist in keeping the QUERY in queries.qry then supply two (lower and upper):
query checkOwnerUniqueness{
description: "Select owner with given identifier"
statement:
SELECT org.acme.participant.Owner
WHERE (idNum == _$upper ID idNum == _$lower )
}
var str = "joe123";
var lower = str.toLowerCase();
var upper = str.toUpperCase();
return query('checkOwnerUniqueness', {idNum: str} ) // ID passed in lower case
// OR
// return query('checkOwnerUniqueness', {idNum: lower, idNum: upper}) // upper or lower two parms
.then(function (results) {
for (var n = 0; n < results.length; n++) { // blah
// process all objects returned in the query
}
});
Giving you alternatives anyway - I think I would make sure the ID field is always entered in the same case, just saying.
In SQL injection, why should you use 0 or 1=1, isn't this automatically evaluated as 1 in boolean operation? I don't understand why we should write it that way. Can someone explain?
Thanks in advance
Because it makes the condition always true.
For example, if someone's SQL code is:
string query = "SELECT * FROM Users WHERE Password = '" + somePassword + "'";
(Username clause omitted for brevity.)
Then you can enter something like this as the password:
' OR 1 = 1;--
Which would make the resulting query:
SELECT * FROM Users WHERE Password = '' OR 1 = 1;--'
The semicolon ends the statement, and -- denotes a comment so everything thereafter is ignored. So it simplifies to:
SELECT * FROM Users WHERE Password = '' OR 1 = 1
This will match all records in the table. Always. Because 1 = 1 is always true. Depending on how the application handles this response, you may be logged in. Perhaps even as the first user in the table, which is likely to be the admin user.
For SQL-injectable code, it's basically a universal password. (Provided you guess a correct username, which isn't difficult.)
Edit: I just noticed the 0 part of your question as well. This would be used when you expect the injected value to be looking for a number rather than a string. For example, consider a similar SQL statement:
string query = "SELECT * FROM Users WHERE Id = " + someID;
The leading 0 in the injected value prevents a syntax error. So the resulting query would be:
SELECT * FROM Users WHERE Id = 0 OR 1 = 1
Same concept as above. This will match all records every time.
Here is a brief explanation for this:-
select title, text from news where id=$id
In the example above the variable $id contains user-supplied data, while the remainder is the SQL static part supplied by the programmer; making the SQL statement dynamic.
Because the way it was constructed, the user can supply crafted input trying to make the original SQL statement execute further actions of the user's choice. The example below illustrates the user-supplied data “10 or 1=1”, changing the logic of the SQL statement, modifying the WHERE clause adding a condition “or 1=1”.
select title, text from news where id=10 or 1=1
so the query will still get executed
Here I create an empty table and a function:
CREATE TABLE "ebook_info_t" (
"ebook_id" uuid NOT NULL,
...
PRIMARY KEY ("ebook_id")
);
CREATE OR REPLACE FUNCTION add_ebook_f(
IN author text,
OUT existid uuid, OUT isexist bool)
RETURNS SETOF RECORD AS $$
DECLARE
rc uuid := NULL;
bkname text :=NULL;
BEGIN
SELECT ebook_id,title INTO rc,bkname FROM ebook_info_t WHERE author_name=author;
RAISE EXCEPTION 'bookid=%, title=%',rc,bkname;
END
$$
LANGUAGE plpgsql;
I call this function in nodejs by pg module which version is 6.1.2.
Firstly, table ebook_info_t is empty. I call function add_ebook_f then recieve bookid=3d6171d3-3d45-4869-8e89-b8975a31246b, title=thebookname.
Secondly, I remove WHERE-condition. Then I call it again. I recieve bookid=bac22a64-9cb2-440a-be39-b40d74c2ddfb, title=<NULL>.
Finally, I test this function in pgAdmin4. I shows ERROR: bookid=<NULL>, title=<NULL>.
If the book is not exist, I want to insert it to table. But if I call add_ebook_f in nodejs, I could not judge this book exist or not.
So my question is, how can I insert book info to table if it is not exist? In other words, could I know this book exist?
Using Microsoft Azure, I've created a database using easy tables (node.js backend). I have several tables and one view.
There's a trick to making the view work like an easy table which I've done.
Problem:
When using this code to read data from the view, I receive ALL data -- including data for other users.
table.read(function (context) {
return context.execute();
});
That makes sense, as I'm not specifying that I only want the authenticated user's data.
When using this code, I get NO data:
// READ operation
table.read(function (context) {
context.query.where({ userId: context.user.id });
return context.execute();
});
Using the above code for an actual table and not a view works perfectly.
From the log files:
2017-01-02T18:52:00.945Z - silly: Executing SQL statement SELECT TOP 3 * FROM [dbo].[ClassData] WHERE (([userId] = #p1) AND ([deleted] = #p2)); with parameters [{"name":"p1","pos":1,"value":"sid:REMOVED"},{"name":"p2","pos":2,"value":false}]
2017-01-02T18:52:00.945Z - silly: Read query returned 2 results
sprintf() will be removed in the next major release, use the sprintf-js package instead.
2017-01-02T18:52:01.867Z - silly: Executing SQL statement SELECT TOP 10 * FROM [dbo].[StacksNamesView] WHERE (([userId] = #p1) AND ([deleted] = #p2)) ORDER BY [createdAt]; with parameters [{"name":"p1","pos":1,"value":"sid:REMOVED"},{"name":"p2","pos":2,"value":false}]
2017-01-02T18:52:01.883Z - debug: SQL statement failed - Invalid column name 'userId'.: SELECT TOP 10 * FROM [dbo].[StacksNamesView] WHERE (([userId] = #p1) AND ([deleted] = #p2)) ORDER BY [createdAt]; with parameters [{"name":"p1","pos":1,"value":"sid:REMOVED"},{"name":"p2","pos":2,"value":false}]
UPDATE:
When creating Azure App Service EasyTables, the column "userId" is automatically a part of each table. However, it's not visible when looking at the schema via the Azure Portal. This is where my confusion was.
SOLUTION:
When using a view as an EasyTable and you need the "hidden" userId column, just make sure you select it as part of the view! This will work as long as your other EasyTables are executing queries such as the second block of code in this post.
We have created a bunch of users in CRM 2011 using the SDK. However, we added their Security Role records through the database.
Everything seems to work fine, until these users started to save their own User Dashboards and Advanced Finds.
The users could create their own User Dashboards. However, once they created them, they could not see them. They were not in their list of Dashboards - only the System Dashboards where there.
There were no errors in the event viewer or even the trace logs.
I used SQL Profiler to see what it was doing and I discovered it was checking the PrincipalEntityMap table for principals that had an objecttypecode of 1031 - which is the User Dashboard (called UserForm).
How do these records get created?
I can write a SQL script to populate the database with these missing records.
What I would like to know is why they are missing? Any ideas?
Where do the records for PrincipalEntityMap come from?
Because we created the UserRole (i.e. User Security Role) records through the database and not through the SDK - we missed some POA (Principal Object Access) related records.
There are a number of stored procedures that can be called to re-initialise these records.
We have written a script to reset these records for all users:
-- This will create PrincipalEntityMap for users - if they are not there:
INSERT INTO PrincipalEntityMap (ObjectTypeCode, PrincipalId, PrincipalEntityMapId)
SELECT 1031, sup.PrincipalId, NEWID()
FROM SystemUserPrincipals sup
INNER JOIN SystemUser su ON su.SystemUserId = sup.SystemUserId
WHERE
(sup.PrincipalId = su.SystemUserId) AND
(sup.PrincipalId NOT IN
(
SELECT pem.PrincipalId
FROM PrincipalEntityMap pem
WHERE pem.ObjectTypeCode = 1031
)
)
DECLARE #PrincipalTable TABLE (PrincipalID uniqueidentifier)
DECLARE #CurrentPrincipalID uniqueidentifier
DECLARE #UserIds VARCHAR(60)
DECLARE #Type INT
BEGIN TRANSACTION ResetPrincipalEntitiyMap
BEGIN
SET #Type = 8
INSERT INTO #PrincipalTable (PrincipalID)
SELECT sup.PrincipalId
FROM SystemUserPrincipals sup WITH (NOLOCK)
INNER JOIN SystemUser su WITH (NOLOCK) ON sup.SystemUserId = su.SystemUserId AND sup.PrincipalId = su.SystemUserId
WHILE EXISTS (SELECT PrincipalID FROM #PrincipalTable)
BEGIN
SELECT TOP 1 #CurrentPrincipalID = PrincipalID
FROM #PrincipalTable
ORDER BY PrincipalID ASC
EXEC p_PrincipalEntityMapReinit #CurrentPrincipalID, #Type
EXEC p_PrincipalAttributeAccessMapReinit #CurrentPrincipalID, #Type
SET #UserIds = cast(#CurrentPrincipalID AS VARCHAR(50))
EXEC p_SystemUserBuEntityMapReinit #UserIds
DELETE FROM #PrincipalTable WHERE PrincipalID = #CurrentPrincipalID
END
END
COMMIT TRANSACTION ResetPrincipalEntitiyMap
Please Note: Always perform inserts/updates/deletes of Security
Related entities (User, UserRole, Team, TeamRole, etc.) through the
SDK - rather than the database. The SDK does some weird stuff in the
background that will be missed if you use SQL.
While trying to resolve the common/constant problem with exchange server side sync on CRM 2013 (error code E-Mail-Server: Crm.80044151 when sync of contacts, tasks and appoitments is enabled), we've also tried to reinit the principal-tables using your script.
For CRM2013/15, it had to be modified slightly, because the signature of SP p_PrincipalEntityMapReinit has changed.
Here's the updated TSQL - maybe it helps someone else (in our case, it didn't :( ):
DECLARE #PrincipalTable dbo.EntityIdCollection
DECLARE #CurrentPrincipalID uniqueidentifier
DECLARE #UserIds VARCHAR(60)
DECLARE #Type INT
BEGIN TRANSACTION ResetPrincipalEntitiyMap
BEGIN
SET #Type = 8
INSERT INTO #PrincipalTable (id)
SELECT sup.PrincipalId
FROM SystemUserPrincipals sup WITH (NOLOCK)
INNER JOIN SystemUser su WITH (NOLOCK) ON sup.SystemUserId = su.SystemUserId AND sup.PrincipalId = su.SystemUserId
EXEC p_PrincipalEntityMapReinit #PrincipalTable, #Type
WHILE EXISTS (SELECT id FROM #PrincipalTable)
BEGIN
SELECT TOP 1 #CurrentPrincipalID = id
FROM #PrincipalTable
ORDER BY id ASC
EXEC p_PrincipalAttributeAccessMapReinit #CurrentPrincipalID, #Type, 1
SET #UserIds = cast(#CurrentPrincipalID AS VARCHAR(50))
EXEC p_SystemUserBuEntityMapReinit #UserIds
DELETE FROM #PrincipalTable WHERE id = #CurrentPrincipalID
END
END
COMMIT TRANSACTION ResetPrincipalEntitiyMap