SQL Server: Users & Logins — Where does the user name come in? - sql-server

I am trying to get a better understanding of the distinction between users & logins. I know how to create them, and I understand that they are required, but I don’t yet understand how SQL Server uses this distinction.
As far as I understand, a login has access to the Server, while a user has access to a database and its objects.
If I execute the following:
CREATE LOGIN fred WITH PASSWORD='…';
USE stuff;
CREATE USER wilma FOR LOGIN fred;
USE nonsense;
CREATE USER pebbles FOR LOGIN fred;
Then I have a Login of fred with two user names associated with two databases. My guess is that these user names can be regarded as aliases for fred.
I undersand that it is common to use the same username as the login name, but clearly not necessary.
The next step is to login as fred. I cannot login as one of the users.
At this point, I don’t see what happens next. How do I become one of the users, and what would do for me?

You never authenticate as a user.You authenticate as a login, which then maps to a single user in zero or more databases.
Create the login
CREATE LOGIN fred WITH PASSWORD='fredsecret', CHECK_POLICY = OFF;
GO
Create the users
USE stuff;
CREATE USER wilma FOR LOGIN fred;
GO
USE nonsense;
CREATE USER pebbles FOR LOGIN fred;
GO
Change context so it looks like I'm fred
SELECT SUSER_SNAME() --gbn
EXECUTE AS LOGIN = 'fred'
SELECT SUSER_SNAME() --fred
Note the differences
USE stuff
SELECT SUSER_SNAME(), USER_NAME()
USE nonsense;
SELECT SUSER_SNAME(), USER_NAME()
GO
and go back to me
REVERT
The name of the login (fred in this case) is only used for authentication to SQL Server. After authentication you mostly use the sid value: which links login (server principal) and user (database principal)
SELECT name, sid FROM sys.server_principals
USE stuff
SELECT name, sid FROM sys.database_principals
USE nonsense
SELECT name, sid FROM sys.database_principals
In my case, it is 0xC7C14DE4BFDF2445A7DABE158CC399F0
Note, sid is unique in a database. This will fail
USE nonsense;
CREATE USER barney FOR LOGIN fred;
GO
Msg 15063, Level 16, State 1, Line 10
The login already has an account under a different user name.

You connect to the server under the login, on base stuff will be CURRENT_USER wilma and on nonsense CURRENT_USER = pebbles

Related

Require old password when setting a new password to a specific user in Oracle

Does Oracle 12 support having the old password required when changing a password to a specific user?
What I would like:
ALTER USER user_a IDENTIFIED BY secret123;
-- ERROR, missing old password
ALTER USER user_a IDENTIFIED BY secret456 REPLACE secret123;
-- OK
ALTER USER user_b IDENTIFIED BY secret789;
-- OK, since user_b does not require old password when changing it
Thanks!
Yes, this is supported since Oracle 9i, when a function was introduced that checks a new password for complexity and optionally for difference to the old password. As Oracle stores only hashes, not the passwords, it cannot compare old and new passwords unless the user supplies it during the change.
So, all users with a PROFILE where the PASSWORD_VERIFY_FUNCTION is set are required to have the old password, even if this function doesn't check any passwords:
CREATE OR REPLACE FUNCTION always_true (
username VARCHAR2,
password VARCHAR2,
old_password VARCHAR2) RETURN boolean IS
BEGIN
RETURN TRUE;
END always_true;
/
CREATE PROFILE always_true
LIMIT PASSWORD_VERIFY_FUNCTION always_true;
CREATE USER user_a IDENTIFIED BY secret123 PROFILE always_true;
GRANT CREATE SESSION to user_a;
Now user_a has to specify the old password:
ALTER USER user_a IDENTIFIED BY secret123;
ORA-28221: REPLACE not specified
ALTER USER user_a IDENTIFIED BY secret456 REPLACE secret123;
User altered.
A user with a profile without PASSWORD_VERIFY_FUNCTION or this parameter set to NULL doesn't have to specify the old password:
CREATE PROFILE without_function
LIMIT PASSWORD_VERIFY_FUNCTION NULL;
CREATE USER user_b IDENTIFIED BY secret123 PROFILE without_function;
GRANT CREATE SESSION to user_b;
Now user_b can change his/her password without having the old password:
ALTER USER user_b IDENTIFIED BY secret789;
User altered.
The second option is to have the privilege ALTER USER, but that is only for administrators, as they can change all the passwords of all account.
Oracle docu says
You can omit the REPLACE clause if you are setting your own password for the first time or you have the ALTER USER system privilege and you are changing another user's password. However, unless you have the ALTER USER system privilege, you must always specify the REPLACE clause if a password complexity verification function has been enabled ...
so the answer would be - add a password verification function to those users that should provide the old password (and revoke the password change system privilege from them).

SQL Server domain name and user name formatting

I am executing a SQL script in SQL Server Management Studio 2018. In my script I need to specify a user (including the domain - unsure if I need the server name).
So I have created a user sam, set the user type to SQL user without login and set the users role to db_datareader and db_datawriter.
I then execute my script but it gives me the error: User or role 'MHT.sam' does not exist in this database.
But I am almost certain I have added this user to the database (see my images below to double check). Is my user and domain name format correct? What do you think I am doing wrong?
Here's my domain and server:
The error is pretty obvious.
In your screen shot in the object explorer you have a user called SAM, but for sp_AddRoleMember you are using MHT.SAM user.
Your sp_addrolemember should also have only Sam something like...
Exec sp_addrolemember N'RunStoredProc' , N'Sam'
GO
Also to double check what your user type is what login it is mapped to and what really is going on, use the following query.
SELECT
d.name AS User_Name
, d.type_desc AS User_Type
, d.default_schema_name AS User_default_schema_name
, d.create_date AS User_Created_Date
, s.name AS Login_name
, s.type_desc AS Login_LoginType
, s.is_disabled AS Login_is_disabled
, s.create_date AS Login_create_date
, s.default_database_name AS Login_default_database_name
, s.default_language_name AS Login_default_language_name
FROM sys.server_principals s
INNER JOIN sys.database_principals d on s.sid = d.sid
WHERE d.name = 'Sam'

database usernames and passwords in delphi

Using Delphi XE2.
I have a database with a table in it called users. It has a user_id field, username field, a password field and an Active field in it. (the user_id is a unique number that identifies each user... 1,2,3 etc).
I am writing a database software package which requires username and passwords to login to it. (I already have a login form created).
How do I get it to match/check the usernames and passwords in the database then allow user to continue into the software? Also I would like the Active Field to store in the database 'Y' if user is logged in or 'N' if user is not logged in. Is this doable?
I am connected to the users table via TADQuery and TDataSource.
Example below of function which I thought would get me started(calling it at point of clicking the login button on the login form).
function TfrmLogin.CheckUser: Boolean;
begin
while not dmData.qryUser.Eof do
begin
if(editName.Text <> qryUser.FieldByName('uname').AsString) or (editPassword.Text <> qryUser.FieldByName('pword').AsString)
then ShowMessage('Username and/or Password not recognised');
Exit;
End;
Looping through all rows in the database will not suffice, especially when you get more than a handful of users. You need to SELECT from the database for the specific user, and see if you get results back. You can do the SELECT based just on the username:
qryUser.SQL.Text := 'SELECT uname, pword FROM users WHERE uName = :uname';
qryUser.ParamByName('uname').AsString := editName.Text;
try
qryUser.Open;
if qryUser.IsEmpty then // No record found for user
// Handle error
else
begin
if qryUser.FieldByName('pword').AsString <> editPassword.Text then
// Handle password mismatch;
end;
finally
qryUser.Close;
end;
It's not clear from your question which database components you're using (TADQuery might be a typo for TADOQuery, or it might be something else). If in fact it is TADOQuery, you'll need to make a couple of small changes to the code. (Actually, only three minor changes; two in the assignment of the parameter and one that reads the password value.)
qryUser.SQL.Text := 'SELECT uname, pword FROM users WHERE uName = :uname';
qryUser.Params.ParamByName('uname').Value := editName.Text;
try
qryUser.Open;
if qryUser.IsEmpty then // No record found for user
// Handle error
else
begin
if qryUser.FieldByName('pword').Value <> editPassword.Text then
// Handle password mismatch;
end;
finally
qryUser.Close;
end;
I'm with Sam, if possible do not store passwords in the database. If the database supports Active Directory Authentication (MS/SQL, Oracle, DB2, MySQL, SyBase) use the user name and password to validate against Active Directory before trying a connection to the database. Then only store the user name and active flag in the Users table.
This function ask Active Directory if the user and password are valid, before you ever try to make a connection to the database. Then you can build your connection parameters to the database (I assume you are using FireDAC and a TADConnection see here for instructions). Try to open the connection, if it fails the user does not have access to the database at all. If it passes, then query the Users table like Ken suggest but test the active field instead of the password. This way no one can see a users password and you do not have to manage passwords in your application. By using this method someone still needs to know a users Active Directory password to access your application even though your using Active Directory Authentication on the database.
function TfrmPassword.ActiveDirectoryValidate: Boolean;
var
LHandle: THandle;
lDomainName: String;
begin
Screen.Cursor := crHourglass;
try
// Get the Domain Name
lDomainName := GetEnvironmentVariable('USERDOMAIN');
// Test the user Logon
Result := LogonUser(PWideChar(edtUserID.Text),
PWideChar(lDomainName),
PWideChar(edtPassword.Text),
LOGON32_LOGON_NETWORK,
LOGON32_PROVIDER_DEFAULT,
LHandle);
// If True, we got a Handle, so close it
if Result then
CloseHandle(LHandle);
finally
Screen.Cursor := crDefault;
end;
end;

Different Permissions in Apache Shiro for every User?

I built a database with the entity user and permission
user (id, email, password, permission)
permission (id, create_user, delete_user, user_fk)
create_user and delete_user is BOOLEAN.
Relationship: One-One
Now every user can have it's own permissions.
My question is: How can I use shiro to read the permissions from the database?
If you really only wish to assign permissions on user level, you can "fake" the roles table to make Shiro happy.
As Wouter mentioned, use the JdbcRealm and specify the 3 queries for your table setup.
You should modify your permission table to have this structure:
permission (id, permissionname, user_fk)
Then you insert rows for the create_user/delete_user rights as needed.
This way it's very simple to add another permission (reset_password for example) to your setup, without the need to modify the db schema.
In the shiro.ini (or how you call the your shiro config file):
jdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
For the queries use then this:
jdbcRealm.authenticationQuery = select password from user where email=?
jdbcRealm.userRolesQuery = select id from user where email=?
jdbcRealm.authenticationQuery = select permissionname from permission where user_fk=?
The small trick in your setup is: you don't have roles at all, so we just return the id of the user as the role name.
When the lookup in the permission table is done, it then uses the role name (=user pk) and returns the associated permissions.
You should configure a JdbcReam in your .ini file:
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
If you change your schema to adhere to the shiro queries, no extra config is needed. You need tables users, user_roles and roles_permissions.
See the source code how the exact column names should be:
https://svn.apache.org/repos/asf/shiro/trunk/core/src/main/java/org/apache/shiro/realm/jdbc/JdbcRealm.java
Alternatively you can configure your own queries to match your schema in the .ini file like so:
jdbcRealm.authenticationQuery=<your password select statement>
jdbcRealm.userRolesQuery=<your role names for username select statement>
jdbcRealm.authenticationQuery=<your permissions for role name select statement>

Schema Changes to Integrate Facebook and Google Login

I have Users table to store user details with password and the authentication for the Application is working good with this.
But we want to integrate Facebook and Google Login in our system so please advise the related schema modifications.
CREATE TABLE dbo.Users(
UserId int IDENTITY(1, 1) PRIMARY KEY,
UserTypeId int, -- Admin = 1, End User = 2. (We have a master table for this, but eliminating here for simplicity)
UserName nvarchar(16) NOT NULL UNIQUE,
UserPassword nvarchar(16),
FirstName nvarchar(64),
LastName nvarchar(64),
DateOfBirth date,
Gender char(1),
PhoneNumber nvarchar(16),
Email nvarchar(128) UNIQUE,
IsActive bit,
UpdateTime datetime default CURRENT_TIMESTAMP )
Here is what I am thinking:
1) Once the user authenticated from Facebook or Google then the application will have claims (emailId)
2) The application should validate the emailId existence in Users Table and if exists it will allow login.
Q1> So will this require any update for the existing Row in Users Table?
Q2> If the user record does not exists (based on emailId claim record) then I think we should add the new record in users table?
Q3> In case of Add: What will be the Username and Password values?
Q4> Can the user (the added record) do a normal login without Facebook login?
Thanks.
In order to accept OpenID logins, you will have to accept and store the users' OpenID-URLs. This URL identifies the user just like an email address does.
Q1: Depends: If you want to allow both OpenID-logins and normal login for the same user, you will have to add another column to the table. If you don't allow mixed logins, you could use your Email column to store the OpenID URL.
Q2: Yes, if you see a new OpenID-URL, handle it just like an unknown email address
Q3: You will have to ask the user to pick a username - I assume you do the same for your current users. If you want to allow both logins for the same user, you will have to ask the user to set a password - otherwise they can only login through their OpenID provider.
Q4: Only if you did ask for a username and a password (see Q3)
Please note that allowing the same user to login through OpenID and using conventional username/password introduces potential security problem: A user might not unserstand that you're asking them to set a password and enter their Facebook (or Google) password. Or they might just not care and use the same password everywhere. If they do so and your database does not encrypt the password properly, your database will store the Facebook names and unencrypted passwords... even if just 10% used the same password on your site - just imagine what they could do with that.

Resources